Programming/.NET

AJAX를 따로 구현하지 않고 만들어진 일반적인 경우는 서버로 부터 특정 페이지의 모든 컨텐츠를 내려받아 화면에 표시하는 방법이 될것입니다.

 

public class Member
{
    public string Name { getset}

    public string Email { getset}

    public DateTime BirthDay { getset}
    public int Point { getset}

    public MemberType MT { getset}
}

public enum MemberType
{
    Admin,
    Normal,
    Guest
}

▶ Model

 

public class HomeController : Controller
{
    Member[] mb = {
        new Member() { Name = "홍길동"Email = "hong1@cliel.com"Point = 100, BirthDay = new DateTime(1981, 11, 14)MT = MemberType.Guest },
        new Member() { Name = "홍길남"Email = "hong2@cliel.com"Point = 110, BirthDay = new DateTime(1980, 4, 23)MT = MemberType.Admin },
        new Member() { Name = "홍길석"Email = "hong3@cliel.com"Point = 55, BirthDay = new DateTime(1973, 8, 19)MT = MemberType.Normal },
        new Member() { Name = "홍길순"Email = "hong4@cliel.com"Point = 80, BirthDay = new DateTime(1992, 6, 5)MT = MemberType.Normal },
        new Member() { Name = "홍길영"Email = "hong5@cliel.com"Point = 98, BirthDay = new DateTime(1986, 3, 8)MT = MemberType.Normal }
    };

    // GET: Home
    public ActionResult Index()
    {
        return View(mb);
    }

    [HttpPost]
    public ActionResult Index(string mt)
    {
        if (string.IsNullOrEmpty(mt))
            return View(mb);
        else {
            MemberType MT = (MemberType)Enum.Parse(typeof(MemberType)mt);
            return View(mb.Where(x => x.MT == MT));
        }
    }
}

▶ Controller

 

@using WebApplication1.Classes
@model IEnumerable<WebApplication1.Classes.Member>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <table border="1">
            <thead><tr><th>이름</th><th>포인트</th><th>생년월일</th></tr></thead>
            @foreach (Member m in Model) {
                <tr>
                    <td>@m.Name</td>
                    <td>@m.Point</td>
                    <td>@m.BirthDay.ToString("yyyy년MM월dd일")</td>
                </tr>
            }
        </table>
    </div>
    <br />
    @using (Html.BeginForm()) {
        @Html.DropDownList("mt"EnumHelper.GetSelectList(typeof(MemberType))"전체"new { @class = "form-control" })
        <input type="submit" value="확인" />
    }
</body>
</html>

▶ Index View

 

이 예제는 AJAX없는 일반적인 방법으로 View의 DropDownList로 표시된 항목중 원하는 항목을 선택하고 '확인'버튼을 누르면 HttpPost속성이 설정된 액션메서드로 선택한 타입이 전달되어 Where확장메서드를 통해 필터링된 Member의 리스트를 표시합니다. 여기서 주목해야할 부분은 Member의 리스트를 다시 받아오는 방식인데 현재는 View에 존재하는 모든 컨텐츠를 일괄적으로 받아오고 있습니다.

 

컨텐츠의 양이 작아 사실 이정도 트래픽이 발생하는 것은 부담스러운게 아니지만 View에 이미지나 CSS, JAVA Script파일등이 같이 존재하는 페이지면 단순히 명단을 불러오기위해 페이지 전체를 새로고침하게 되고, 이는 모든 컨텐츠를 처음부터 새로 받아와야 하는 비효휼적인 동작방식으로 연결됩니다.

 

이런 문제를 해결하기 위한 방법중 하나로 우리는 AJAX를 사용할 수 있습니다. AJAX는 Asynchronous JavaScript and XML로서 비동기로 서버와 통신해 필요한 내용만을 XML포멧으로 수신하여 웹페이지에 반영하는 기술입니다. 이것은 페이지의 새로고침을 유도하지 않고 페이지상에 필요한 부분만을 바꿀 수 있는 방법이 됩니다.

 

웹프로그램에 AJAX를 구현하려면 우선 NuGet 패키지 관리자를 통해 JQuery와 Microsoft.Jquery.Unobtrusive.Ajax 패키지를 설치해야 합니다.

 

 

패키지를 설치하고 나면 프로젝트의 Script 폴더에 몇가지 스크립트 파일이 추가되는데 이중에서 2가지 스크립트를 AJAX구현이 필요한 페이지에 다음과 같이 추가하도록 합니다.

 

<head>

    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Scripts/jquery-3.1.1.js"></script>
    <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
</head>

 

그런다음 본래 데이터를 받아오는 액션메서드를 자식액션메서드로 수정하여 데이터처리를 자식액션메서드에서 처리할 수 있도록 합니다.

 

public PartialViewResult GetMember(string mt)
{
    if (string.IsNullOrEmpty(mt))
        return PartialView(mb);
    else {
        MemberType MT = (MemberType)Enum.Parse(typeof(MemberType)mt);
        return PartialView(mb.Where(x => x.MT == MT));
    }
}

 

그리고 기존 Index 액션메서드를 수정해야 하는데 이는 Index View에서 더이상 Member의 열거타입에 대한 처리를 수행하고 있지 않기에 부분뷰로 필터링할 문자열넘 넘겨주는 구조가 되어야 하기 때문입니다.

 

public ActionResult Index()
{
    return View((object)string.Empty);
}

 

그런다음 자식액션에 맞는 부분뷰를 만들어 기존에 Index뷰에 있던 foreach부분을 그대로 옮겨놓습니다.

 

@using WebApplication1.Classes
@model IEnumerable<WebApplication1.Classes.Member>

@foreach (Member m in Model) {
    <tr>
        <td>@m.Name</td>
        <td>@m.Point</td>
        <td>@m.BirthDay.ToString("yyyy년MM월dd일")</td>
    </tr>
}

▶ GetMember.cshtml 부분뷰

 

Member를 표시하는 부분을 부분뷰로 이동시키고 나면 기존의 Index뷰에서는 기존 표시부분을 부분뷰로 대체할 수 있도록 다음과 같이 변경해야 합니다.

 

@using WebApplication1.Classes
@model string

@{
    Layout = null;

    AjaxOptions ao = new AjaxOptions { UpdateTargetId = "memberBody" };
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Scripts/jquery-3.1.1.js"></script>
    <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
</head>
<body>
    <div>
        <table border="1">
            <thead><tr><th>이름</th><th>포인트</th><th>생년월일</th></tr></thead>
            <tbody id="memberBody">
                @Html.Action("GetMember"new { mt = Model })
            </tbody>
        </table>
    </div>
    <br />
    @using (Ajax.BeginForm("Getmember", ao)) {
        @Html.DropDownList("mt"EnumHelper.GetSelectList(typeof(MemberType))"전체"new { @class = "form-control" })
        <input type="submit" value="확인" />
    }
</body>
</html>

 

본문에 foreach로 Member를 표시하는 부분을 자식뷰를 통해 가져올 수 있도록 하였습니다. 또한 기존 Html.BeginFrom을 Ajax로 대체하여 DropDonList의 선택항목을 자식액션메서드인 GetMember로 전송하게 하고 AjaxOptions 의 인스턴스를 함께 전달하고 있습니다.

 

따라서 DropDownList 에서 원하는 항목을 선택하고 폼을 제출하는 버튼인 '확인'을 누르면 서버측에 데이터를 요청하고 그 결과를 화면의 새로고침이 없이 즉각적으로 갱신할 것입니다. 만약 버튼대신 특정 링크를 통해 AJAX동작을 수행하고자 한다면 BeginForm대신 ActionLink 메서드를 통해 다음과 같이 링크를 생성할 수 있습니다.

 

@Ajax.ActionLink("Guest""GetMember"new { mt = "Guest" }, ao)

 

AjaxOptions는 또한 여러 속성을 지정하여 AJAX동작을 수행할때 필요한 다양한 설정을 동반할 수 있습니다. 예를 들어 Confirm은 AJAX동작을 수행하기전 사용자에게 확인메세지를 표시하는 용도로 사용될 수 있으며 UpdateTargetId 는 서버로 부터 수신된 내용을 표시할 HTML영역에 대한 ID를 지정할 수 있습니다.

 

@{
    Layout = null;

    AjaxOptions ao = new AjaxOptions { UpdateTargetId = "memberBody"Confirm = "Memer를 불러 오시겠습니까?" };
}

 

이렇게 AJAX를 구현하고 나면 페이지를 새로고침하지 않고도 표현된 데이터를 갱신할 수 있게 되었습니다. 그런데 만약 요청하려는 데이터를 가져오기까지 시간이 많이 걸리는 경우 사용자 입장에서는 그냥 화면이 정지해 있는 상태로 보여질 수 있기 때문에 서버측에 데이터를 요청하면 화면이 갱신되기 까지 적절한 메세지를 표시할 필요가 있을 것입니다.

 

이를 위해 우선 사용자에게 필요한 메세지 영역을 만들고

 

<body>

    <div id="loading" style="display:none">
        기다려 주십시오.
    </div>
    <div>
        <table border="1">
            <thead><tr><th>이름</th><th>포인트</th><th>생년월일</th></tr></thead>
            <tbody id="memberBody">
                @Html.Action("GetMember"new { mt = Model })
            </tbody>
        </table>
    </div>
    <br />
    @using (Ajax.BeginForm("GetMember", ao)) {
        @Html.DropDownList("mt"EnumHelper.GetSelectList(typeof(MemberType))"전체"new { @class = "form-control" })
        <input type="submit" value="확인" />
    }
</body>

 

AjaxOptions 개체의 속성을 다음과 같이 설정합니다.

 

AjaxOptions ao = new AjaxOptions { UpdateTargetId = "memberBody"Confirm = "Memer를 불러 오시겠습니까?"LoadingElementId="loading"LoadingElementDuration=1000 };

 

LoadingElementId 는 메세지 영역을 만들어둔 html 요소의 id값을 지정하며 LoadingElementDuration 에는 해당 영역이 애니메시션형식으로 표시될 시간을 지정합니다. 이렇게 하면 서버에 데이터를 요청할때마다 메세지가 존재하는 div영역이 1초간 서서히 화면에 표시될 수 있습니다.

 

AJAX를 통해 서버에 데이터를 요청하고 응답된 데이터를 가져와 화면에 갱신시키는 과정에서 필요하다면 콜백메서드를 활용해 특정 처리를 수행할 수도 있습니다. 예를 들어 작업이 완료된 경우 사용자에게 '완료'라는 메세지를 보여주고 싶다면 아마도 다음과 같이 처리할 수 있을 것입니다.

 

<script type="text/javascript">
    var OnComplete = function (req, status) {

            alert('완료됨');
        };
</script>

 

우선 위와 같이 콜백으로 호출될 메서드를 생성하고

 

AjaxOptions ao = new AjaxOptions
{
    UpdateTargetId = "memberBody",
    LoadingElementId = "loading",
    LoadingElementDuration = 1000,
    OnComplete = "OnComplete"
};

 

해당 메서드를 AjaxOptions 개체에 설정하면 모든 작업이 완료되었을때 OnComplete 메서드를 자동으로 호출할 것입니다. 이 외에도 처리시작전 호출되는 메서드는 OnBegin, 작업실패시 호출되는 메서드는 OnFailure, 작업성공시 호출되는 메서드는 OnSuccess 속성을 통해 설정하면 됩니다.

 

특히 OnSuccess 메서드는 매개변수로 서버로부터 응답되어 오는 데이터를 받을 수 있는데 이를 이용하면 AJAX의 응답을 JSON으로 처리하려고 할때 해당 데이터를 적절히 처리할 수 있는 방안을 제공합니다.

 

public JsonResult GetMemberJson(string mt)
{
    if (string.IsNullOrEmpty(mt))
        return Json(mb);
    else
    {
        MemberType MT = (MemberType)Enum.Parse(typeof(MemberType)mt);
        return Json(mb.Where(x => x.MT == MT));
    }
}

 

서버로 부터의 결과를 JSON으로 받고자 한다면 우선 JsonResult 형식을 반환하는 액션메서드를 생성해야 합니다. 또한 액션 메서드는 내부에서의 반환을 Json메서드로 처리하여 데이터를 JSON 포멧으로 반환할 수 있도록 처리합니다.

 

@using (Ajax.BeginForm("GetMemberJson", ao)) {
    @Html.DropDownList("mt"EnumHelper.GetSelectList(typeof(MemberType))"전체"new { @class = "form-control" })
    <input type="submit" value="확인" />
}

 

그런 다음 Form에서의 데이터 요청이 위에서 작성한 GetMemberJson으로 갈 수 있도록 하고

 

<script type="text/javascript">
    var OnSuccess = function (data) {
        $('#memberBody').empty();

        for (var i = 0; i < data.length; i++) {
            var date = new Date(parseInt(data[i].BirthDay.substr(6)));
            $('#memberBody').append('<tr><td>' + data[i].Name + '</td><td>' + data[i].Point + '</td><td>' + date.getFullYear() + '년' + date.getMonth() + '월' + date.getDay() + '일' + '</td></tr>');
        }
    };
</script>

 

OnSuccess 메서드에서 data를 인자로 받아 이전과 동일하게 보여질 수 있도록 스크립트를 통해 처리합니다. 이때 data는 JSON포멧형태로 반환되어 오는 데이터가 될것입니다.

 

AjaxOptions ao = new AjaxOptions
{
    UpdateTargetId = "memberBody",
    LoadingElementId = "loading",
    LoadingElementDuration = 1000,
    OnSuccess = "OnSuccess"
};

 

그리고 AjaxOptions 개체에 OnSuccess 메서드를 지정하면 JSON으로 데이터 포멧을 다룰 수 있는 모든 처리가 완료됩니다.

 

하지만 스크립트를 통해서 데이터를 처리하다보니 다소 코드가 장황해지는 상황이 발생할 수 있는데 예제에서는 Member의 BirthDay를 처리하기 위해 getFullYear나 getMonth처럼 각 날짜에 해당하는 메서드를 일일이 호출하여 날짜형식의 포멧을 맞추려고 하고 있습니다. 물론 굳이 이렇게 하지 않아도 되지만 이런 형태의 장황함은 얼마든지 존재할 수 있으며 이러한 문제는 액션메서드에서 다음과 같이 필요한 필드의 포멧을 맞추어 줌으로서 해결할 수 있습니다.

 

public JsonResult GetMemberJson(string mt)
{
    if (string.IsNullOrEmpty(mt))
        return Json(mb);
    else
    {
        MemberType MT = (MemberType)Enum.Parse(typeof(MemberType)mt);
        return Json(mb.Where(x => x.MT == MT).Select(x => new { Name = x.NamePoint = x.PointBirthDay = x.BirthDay.ToString("yyyy년MM월dd일") }));
    }
}

 

참고로 예제에서는 UpdateTargetId 속성이 여전히 존재하는데 이 속성은 수정된 현재의 상황에는 필요없는 속성이므로 제거되어도 프로그램에는 아무런 영향을 끼치지 않습니다.

'Programming > .NET' 카테고리의 다른 글

[ASP.NET MVC] 번들(Bundle)  (0) 2018.01.02
[C#] nameof  (0) 2017.12.27
[ASP.NET MVC] AJAX  (0) 2017.12.22
[C#] Assembly  (0) 2017.12.13
[C#] LINQ  (0) 2017.12.05
[C#] 문자열 보간 ($)  (0) 2017.11.28
0 0