일곱 번째 반복에서는 Ajax에 대한 지원을 추가하여 애플리케이션의 응답성과 성능을 향상시킵니다.
연락처 관리 ASP.NET MVC 애플리케이션 빌드(VB)
이 자습서 시리즈에서는 처음부터 끝까지 전체 연락처 관리 애플리케이션을 빌드합니다. 연락처 관리자 애플리케이션을 사용하면 사용자 목록에 대한 연락처 정보(이름, 전화 번호 및 전자 메일 주소)를 저장할 수 있습니다.
여러 반복을 통해 애플리케이션을 빌드합니다. 반복할 때마다 애플리케이션을 점진적으로 개선합니다. 이 다중 반복 방법의 목표는 각 변경 이유를 이해할 수 있도록 하는 것입니다.
반복 #1 - 애플리케이션을 만듭니다. 첫 번째 반복에서는 가능한 가장 간단한 방법으로 연락처 관리자를 만듭니다. 기본 데이터베이스 작업인 CRUD(만들기, 읽기, 업데이트 및 삭제)에 대한 지원을 추가합니다.
반복 #2 - 애플리케이션을 멋지게 만듭니다. 이 반복에서는 기본 ASP.NET MVC 보기 master 페이지 및 계단식 스타일시트를 수정하여 애플리케이션의 모양을 개선합니다.
반복 #3 - 양식 유효성 검사를 추가합니다. 세 번째 반복에서는 기본 양식 유효성 검사를 추가합니다. 사용자가 필요한 양식 필드를 완료하지 않고 양식을 제출하지 못하도록 합니다. 또한 이메일 주소 및 전화 번호의 유효성을 검사합니다.
반복 #4 - 애플리케이션을 느슨하게 결합합니다. 이 네 번째 반복에서는 여러 소프트웨어 디자인 패턴을 활용하여 Contact Manager 애플리케이션을 더 쉽게 유지 관리하고 수정할 수 있습니다. 예를 들어 리포지토리 패턴 및 종속성 주입 패턴을 사용하도록 애플리케이션을 리팩터링합니다.
반복 #5 - 단위 테스트 만들기 다섯 번째 반복에서는 단위 테스트를 추가하여 애플리케이션을 더 쉽게 유지 관리하고 수정할 수 있습니다. 데이터 모델 클래스를 모의하고 컨트롤러 및 유효성 검사 논리에 대한 단위 테스트를 빌드합니다.
반복 #6 - 테스트 기반 개발을 사용합니다. 이 여섯 번째 반복에서는 단위 테스트를 먼저 작성하고 단위 테스트에 대한 코드를 작성하여 애플리케이션에 새로운 기능을 추가합니다. 이 반복에서는 연락처 그룹을 추가합니다.
반복 #7 - Ajax 기능 추가 일곱 번째 반복에서는 Ajax에 대한 지원을 추가하여 애플리케이션의 응답성과 성능을 향상시킵니다.
이 반복
이 Contact Manager 애플리케이션 반복에서는 Ajax를 사용하도록 애플리케이션을 리팩터링합니다. Ajax를 활용하여 애플리케이션의 응답성을 높입니다. 페이지의 특정 지역만 업데이트해야 하는 경우 전체 페이지를 렌더링하지 않도록 할 수 있습니다.
다른 사람이 새 연락처 그룹을 선택할 때마다 전체 페이지를 다시 표시할 필요가 없도록 인덱스 보기를 리팩터링합니다. 대신 누군가가 연락처 그룹을 클릭하면 연락처 목록을 업데이트하고 나머지 페이지는 그대로 둡니다.
삭제 링크의 작동 방식도 변경합니다. 별도의 확인 페이지를 표시하는 대신 JavaScript 확인 대화 상자를 표시합니다. 연락처를 삭제하려는 경우 서버에 대해 HTTP DELETE 작업이 수행되어 데이터베이스에서 연락처 레코드를 삭제합니다.
또한 jQuery를 활용하여 인덱스 보기에 애니메이션 효과를 추가합니다. 서버에서 새 연락처 목록을 가져올 때 애니메이션을 표시합니다.
마지막으로, 브라우저 기록을 관리하기 위한 ASP.NET AJAX 프레임워크 지원을 활용하겠습니다. Ajax 호출을 수행하여 연락처 목록을 업데이트할 때마다 기록 지점을 만듭니다. 이렇게 하면 브라우저 뒤로 및 앞으로 단추가 작동합니다.
Ajax를 사용하는 이유
Ajax를 사용하면 많은 이점이 있습니다. 먼저 애플리케이션에 Ajax 기능을 추가하면 사용자 환경이 향상됩니다. 일반 웹 애플리케이션에서는 사용자가 작업을 수행할 때마다 전체 페이지를 서버에 다시 게시해야 합니다. 작업을 수행할 때마다 브라우저가 잠기고 사용자는 전체 페이지가 페치되고 다시 표시될 때까지 기다려야 합니다.
이는 데스크톱 애플리케이션의 경우 허용되지 않는 환경입니다. 그러나 일반적으로 웹 애플리케이션의 경우 더 잘 할 수 있다는 것을 알지 못했기 때문에 이 나쁜 사용자 환경으로 살았습니다. 실제로는 상상력의 한계에 불과할 때 웹 애플리케이션의 한계라고 생각했습니다.
Ajax 애플리케이션에서는 페이지를 업데이트하기 위해 사용자 환경을 중지할 필요가 없습니다. 대신 백그라운드에서 비동기 요청을 수행하여 페이지를 업데이트할 수 있습니다. 페이지의 일부가 업데이트되는 동안 사용자가 기다리도록 강제하지 않습니다.
Ajax를 활용하여 애플리케이션의 성능을 향상시킬 수도 있습니다. Ajax 기능 없이 현재 Contact Manager 애플리케이션의 작동 방식을 고려합니다. 연락처 그룹을 클릭하면 전체 인덱스 보기가 다시 표시되어야 합니다. 데이터베이스 서버에서 연락처 목록 및 연락처 그룹 목록을 검색해야 합니다. 이 모든 데이터는 웹 서버에서 웹 브라우저로 유선으로 전달되어야 합니다.
그러나 애플리케이션에 Ajax 기능을 추가한 후에는 사용자가 연락처 그룹을 클릭할 때 전체 페이지를 다시 표시하지 않도록 할 수 있습니다. 더 이상 데이터베이스에서 연락처 그룹을 잡을 필요가 없습니다. 또한 전체 인덱스 보기를 유선으로 푸시할 필요가 없습니다. Ajax를 활용하여 데이터베이스 서버에서 수행해야 하는 작업의 양을 줄이고 애플리케이션에 필요한 네트워크 트래픽의 양을 줄입니다.
아약스를 두려워하지 마십시오.
일부 개발자는 하위 브라우저에 대해 걱정하기 때문에 Ajax를 사용하지 않습니다. JavaScript를 지원하지 않는 브라우저에서 액세스할 때 웹 애플리케이션이 계속 작동하는지 확인하려고 합니다. Ajax는 JavaScript에 의존하기 때문에 일부 개발자는 Ajax를 사용하지 않습니다.
그러나 Ajax를 구현하는 방법에 주의하는 경우 고급 브라우저와 하위 브라우저 모두에서 작동하는 애플리케이션을 빌드할 수 있습니다. Contact Manager 애플리케이션은 JavaScript를 지원하는 브라우저와 그렇지 않은 브라우저에서 작동합니다.
JavaScript를 지원하는 브라우저에서 Contact Manager 애플리케이션을 사용하는 경우 더 나은 사용자 환경을 갖게 됩니다. 예를 들어 연락처 그룹을 클릭하면 연락처를 표시하는 페이지의 영역만 업데이트됩니다.
반면에 JavaScript를 지원하지 않거나 JavaScript를 사용하지 않도록 설정된 브라우저에서 Contact Manager 애플리케이션을 사용하는 경우 약간 덜 바람직한 사용자 환경을 갖게 됩니다. 예를 들어 연락처 그룹을 클릭하면 일치하는 연락처 목록을 표시하려면 전체 인덱스 보기를 브라우저에 다시 게시해야 합니다.
필요한 JavaScript 파일 추가
애플리케이션에 Ajax 기능을 추가하려면 세 개의 JavaScript 파일을 사용해야 합니다. 이러한 세 파일은 모두 새 ASP.NET MVC 애플리케이션의 Scripts 폴더에 포함됩니다.
애플리케이션의 여러 페이지에서 Ajax를 사용하려는 경우 애플리케이션 보기 master 페이지에 필요한 JavaScript 파일을 포함하는 것이 좋습니다. 이렇게 하면 JavaScript 파일이 애플리케이션의 모든 페이지에 자동으로 포함됩니다.
보기 master 페이지의 헤드> 태그 내에 <다음 JavaScript 포함을 추가합니다.
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.2.6.min.js" type="text/javascript"></script>
Ajax를 사용하도록 인덱스 뷰 리팩터링
먼저 연락처 그룹을 클릭하면 연락처를 표시하는 보기의 영역만 업데이트되도록 인덱스 보기를 수정해 보겠습니다. 그림 1의 빨간색 상자에는 업데이트하려는 지역이 포함되어 있습니다.
그림 01: 연락처만 업데이트(전체 크기 이미지를 보려면 클릭)
첫 번째 단계는 비동기적으로 업데이트하려는 보기 부분을 별도의 부분(사용자 컨트롤 보기)으로 분리하는 것입니다. 연락처 테이블을 표시하는 인덱스 보기의 섹션이 목록 1의 일부로 이동되었습니다.
목록 1 - Views\Contact\ContactList.ascx
<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of ContactManager.Group)" %>
<table class="data-table" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th class="actions edit">
Edit
</th>
<th class="actions delete">
Delete
</th>
<th>
Name
</th>
<th>
Phone
</th>
<th>
Email
</th>
</tr>
</thead>
<tbody>
<% For Each item in Model.Contacts %>
<tr>
<td class="actions edit">
<a href='<%= Url.Action("Edit", New With {.id=item.Id}) %>'><img src="../../Content/Edit.png" alt="Edit" /></a>
</td>
<td class="actions delete">
<a href='<%= Url.Action("Delete", New With {.id=item.Id}) %>'><img src="../../Content/Delete.png" alt="Delete" /></a>
</td>
<th>
<%= Html.Encode(item.FirstName) %>
<%= Html.Encode(item.LastName) %>
</th>
<td>
<%= Html.Encode(item.Phone) %>
</td>
<td>
<%= Html.Encode(item.Email) %>
</td>
</tr>
<% Next %>
</tbody>
</table>
목록 1의 일부는 인덱스 보기와 다른 모델을 사용합니다. %@ Page %> 지시문의 <Inherits 특성은 Partial이 ViewUserControl<그룹> 클래스에서 상속되도록 지정합니다.
업데이트된 인덱스 보기는 목록 2에 포함되어 있습니다.
목록 2 - Views\Contact\Index.aspx
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<ul id="leftColumn">
<% For Each item in Model.Groups %>
<li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList"})%>
</li>
<% Next %>
</ul>
<div id="divContactList">
<% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>
<div class="divContactList-bottom"> </div>
</asp:Content>
목록 2에서 업데이트된 보기에 대해 알아야 할 두 가지 사항이 있습니다. 먼저 부분으로 이동된 모든 콘텐츠가 Html.RenderPartial()에 대한 호출로 바뀝니다. Html.RenderPartial() 메서드는 초기 연락처 집합을 표시하기 위해 인덱스 뷰를 처음 요청할 때 호출됩니다.
둘째, 연락처 그룹을 표시하는 데 사용되는 Html.ActionLink()가 Ajax.ActionLink()로 대체되었습니다. Ajax.ActionLink()는 다음 매개 변수를 사용하여 호출됩니다.
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList"})%>
첫 번째 매개 변수는 링크에 표시할 텍스트를 나타내고, 두 번째 매개 변수는 경로 값을 나타내고, 세 번째 매개 변수는 Ajax 옵션을 나타냅니다. 이 경우 UpdateTargetId Ajax 옵션을 사용하여 Ajax 요청이 완료된 후 업데이트하려는 HTML <div> 태그를 가리킵니다. div> 태그를 <새 연락처 목록으로 업데이트하려고 합니다.
연락처 컨트롤러의 업데이트된 Index() 메서드는 목록 3에 포함되어 있습니다.
목록 3 - Controllers\ContactController.vb(Index 메서드)
Public Function Index(ByVal id As Integer?) As ActionResult
' Get selected group
Dim selectedGroup = _service.GetGroup(id)
if IsNothing(selectedGroup) Then
Return RedirectToAction("Index", "Group")
End If
' Normal Request
if Not Request.IsAjaxRequest() Then
Dim model As new IndexModel With { _
.Groups = _service.ListGroups(), _
.SelectedGroup = selectedGroup _
}
Return View("Index", model)
End If
' Ajax Request
return PartialView("ContactList", selectedGroup)
End Function
업데이트된 Index() 작업은 두 가지 중 하나를 조건부로 반환합니다. Ajax 요청에 의해 Index() 작업이 호출되면 컨트롤러는 일부를 반환합니다. 그렇지 않으면 Index() 작업은 전체 뷰를 반환합니다.
Index() 작업은 Ajax 요청에 의해 호출될 때 많은 데이터를 반환할 필요가 없습니다. 일반 요청의 컨텍스트에서 인덱스 작업은 모든 연락처 그룹 및 선택한 연락처 그룹의 목록을 반환합니다. Ajax 요청의 컨텍스트에서 Index() 작업은 선택한 그룹만 반환합니다. Ajax는 데이터베이스 서버에서 더 적은 작업을 의미합니다.
수정된 인덱스 보기는 업레벨 브라우저와 하위 브라우저의 경우 모두 작동합니다. 연락처 그룹을 클릭하고 브라우저에서 JavaScript를 지원하는 경우 연락처 목록이 포함된 보기 영역만 업데이트됩니다. 반면 브라우저에서 JavaScript를 지원하지 않는 경우 전체 보기가 업데이트됩니다.
업데이트된 인덱스 보기에는 한 가지 문제가 있습니다. 연락처 그룹을 클릭하면 선택한 그룹이 강조 표시되지 않습니다. 그룹 목록은 Ajax 요청 중에 업데이트되는 지역 외부에 표시되므로 올바른 그룹이 강조 표시되지 않습니다. 이 문제는 다음 섹션에서 해결하겠습니다.
jQuery 애니메이션 효과 추가
일반적으로 웹 페이지에서 링크를 클릭하면 브라우저 진행률 표시줄을 사용하여 브라우저가 업데이트된 콘텐츠를 적극적으로 가져오는지 여부를 검색할 수 있습니다. 반면 Ajax 요청을 수행할 때 브라우저 진행률 표시줄에 진행률이 표시되지 않습니다. 이렇게 하면 사용자가 긴장할 수 있습니다. 브라우저가 고정되었는지 어떻게 알 수 있나요?
Ajax 요청을 수행하는 동안 작업이 수행되고 있음을 사용자에게 나타낼 수 있는 여러 가지 방법이 있습니다. 한 가지 방법은 간단한 애니메이션을 표시하는 것입니다. 예를 들어 Ajax 요청이 시작될 때 영역을 페이드 아웃하고 요청이 완료되면 지역에서 페이드 아웃할 수 있습니다.
Microsoft ASP.NET MVC 프레임워크에 포함된 jQuery 라이브러리를 사용하여 애니메이션 효과를 만듭니다. 업데이트된 인덱스 보기는 목록 4에 포함되어 있습니다.
목록 4 - Views\Contact\Index.aspx
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
function beginContactList(args)
{
// Highlight selected group
$('#leftColumn li').removeClass('selected');
$(this).parent().addClass('selected');
// Animate
$('#divContactList').fadeOut('normal');
}
function successContactList()
{
// Animate
$('#divContactList').fadeIn('normal');
}
function failureContactList()
{
alert("Could not retrieve contacts.");
}
</script>
<ul id="leftColumn">
<% For Each item in Model.Groups %>
<li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" })%>
</li>
<% Next %>
</ul>
<div id="divContactList">
<% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>
<div class="divContactList-bottom"> </div>
</asp:Content>
업데이트된 인덱스 보기에는 세 개의 새 JavaScript 함수가 포함되어 있습니다. 처음 두 함수는 jQuery를 사용하여 새 연락처 그룹을 클릭할 때 연락처 목록에서 페이드 아웃 및 페이드 아웃합니다. 세 번째 함수는 Ajax 요청으로 인해 오류(예: 네트워크 시간 제한)가 발생할 때 오류 메시지를 표시합니다.
또한 첫 번째 함수는 선택한 그룹을 강조 표시합니다. class= selected 특성은 클릭한 요소의 부모 요소(LI 요소)에 추가됩니다. 다시 말하지만 jQuery를 사용하면 올바른 요소를 쉽게 선택하고 CSS 클래스를 추가할 수 있습니다.
이러한 스크립트는 Ajax.ActionLink() AjaxOptions 매개 변수의 도움으로 그룹 링크에 연결됩니다. 업데이트된 Ajax.ActionLink() 메서드 호출은 다음과 같습니다.
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" })%>
브라우저 기록 지원 추가
일반적으로 페이지를 업데이트하는 링크를 클릭하면 브라우저 기록이 업데이트됩니다. 이렇게 하면 브라우저 뒤로 단추를 클릭하여 페이지의 이전 상태로 시간을 거슬러 이동할 수 있습니다. 예를 들어 친구 연락처 그룹을 클릭한 다음 비즈니스 연락처 그룹을 클릭하면 브라우저 뒤로 단추를 클릭하여 친구 연락처 그룹이 선택되었을 때 페이지 상태로 다시 이동할 수 있습니다.
아쉽게도 Ajax 요청을 수행해도 브라우저 기록이 자동으로 업데이트되지 않습니다. 연락처 그룹을 클릭하면 Ajax 요청으로 일치하는 연락처 목록이 검색되면 브라우저 기록이 업데이트되지 않습니다. 브라우저 뒤로 단추를 사용하여 새 연락처 그룹을 선택한 후 연락처 그룹으로 다시 이동할 수 없습니다.
사용자가 Ajax 요청을 수행한 후 브라우저 뒤로 단추를 사용할 수 있도록 하려면 좀 더 많은 작업을 수행해야 합니다. ASP.NET AJAX Framework에서 빌드된 브라우저 기록 관리 기능을 활용해야 합니다.
AJAX 브라우저 기록을 ASP.NET 다음 세 가지 작업을 수행해야 합니다.
- enableBrowserHistory 속성을 true로 설정하여 브라우저 기록을 사용하도록 설정합니다.
- addHistoryPoint() 메서드를 호출하여 보기 상태가 변경되면 기록 지점을 저장합니다.
- navigate 이벤트가 발생할 때 뷰의 상태를 다시 구성합니다.
업데이트된 인덱스 보기는 목록 5에 포함되어 있습니다.
목록 5 - Views\Contact\Index.aspx
<%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of ContactManager.IndexModel)" %>
<%@ Import Namespace="ContactManager" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<title>Index</title>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script type="text/javascript">
var _currentGroupId = -1;
Sys.Application.add_init(pageInit);
function pageInit() {
// Enable history
Sys.Application.set_enableHistory(true);
// Add Handler for history
Sys.Application.add_navigate(navigate);
}
function navigate(sender, e) {
// Get groupId from address bar
var groupId = e.get_state().groupId;
// If groupId != currentGroupId then navigate
if (groupId != _currentGroupId) {
_currentGroupId = groupId;
$("#divContactList").load("/Contact/Index/" + groupId);
selectGroup(groupId);
}
}
function selectGroup(groupId) {
$('#leftColumn li').removeClass('selected');
if (groupId)
$('a[groupid=' + groupId + ']').parent().addClass('selected');
else
$('#leftColumn li:first').addClass('selected');
}
function beginContactList(args) {
// Highlight selected group
_currentGroupId = this.getAttribute("groupid");
selectGroup(_currentGroupId);
// Add history point
Sys.Application.addHistoryPoint({ "groupId": _currentGroupId });
// Animate
$('#divContactList').fadeOut('normal');
}
function successContactList() {
// Animate
$('#divContactList').fadeIn('normal');
}
function failureContactList() {
alert("Could not retrieve contacts.");
}
</script>
<ul id="leftColumn">
<% For Each item in Model.Groups %>
<li <%= Html.Selected(item.Id, Model.SelectedGroup.Id) %>>
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" }, New With { .groupid = item.Id })%>
</li>
<% Next %>
</ul>
<div id="divContactList">
<% Html.RenderPartial("ContactList", Model.SelectedGroup) %>
</div>
<div class="divContactList-bottom"> </div>
</asp:Content>
목록 5에서 브라우저 기록은 pageInit() 함수에서 사용하도록 설정됩니다. pageInit() 함수는 navigate 이벤트에 대한 이벤트 처리기를 설정하는 데도 사용됩니다. 탐색 이벤트는 브라우저 앞으로 또는 뒤로 단추로 인해 페이지 상태가 변경될 때마다 발생합니다.
연락처 그룹을 클릭하면 beginContactList() 메서드가 호출됩니다. 이 메서드는 addHistoryPoint() 메서드를 호출하여 새 기록 지점을 만듭니다. 클릭한 연락처 그룹의 ID가 기록에 추가됩니다.
그룹 ID는 연락처 그룹 링크의 expando 특성에서 검색됩니다. 링크는 Ajax.ActionLink()에 대한 다음 호출로 렌더링됩니다.
<%= Ajax.ActionLink(item.Name, "Index", New With { .id = item.Id }, New AjaxOptions With { .UpdateTargetId = "divContactList", .OnBegin = "beginContactList", .OnSuccess = "successContactList", .OnFailure = "failureContactList" }, New With { .groupid = item.Id })%>
Ajax.ActionLink()에 전달된 마지막 매개 변수는 groupid라는 expando 특성을 링크에 추가합니다(XHTML 호환성의 경우 소문자).
사용자가 브라우저 뒤로 또는 앞으로 단추를 누르면 navigate 이벤트가 발생하고 navigate() 메서드가 호출됩니다. 이 메서드는 탐색 메서드에 전달된 브라우저 기록 지점에 해당하는 페이지의 상태와 일치하도록 페이지에 표시된 연락처를 업데이트합니다.
Ajax 삭제 수행
현재 연락처를 삭제하려면 삭제 링크를 클릭한 다음 삭제 확인 페이지에 표시된 삭제 단추를 클릭해야 합니다(그림 2 참조). 데이터베이스 레코드를 삭제하는 것과 같이 간단한 작업을 수행하라는 많은 페이지 요청처럼 보입니다.
그림 02: 삭제 확인 페이지(전체 크기 이미지를 보려면 클릭)
삭제 확인 페이지를 건너뛰고 인덱스 보기에서 직접 연락처를 삭제하려고 합니다. 이 접근 방식을 사용하면 애플리케이션이 보안 허점으로 열리기 때문에 이러한 유혹을 피해야 합니다. 일반적으로 웹 애플리케이션의 상태를 수정하는 작업을 호출할 때는 HTTP GET 작업을 수행하지 않습니다. 삭제를 수행할 때 HTTP POST 또는 더 나은 HTTP DELETE 작업을 수행하려고 합니다.
삭제 링크는 ContactList 부분의 에 포함되어 있습니다. ContactList 부분의 업데이트된 버전은 목록 6에 포함되어 있습니다.
목록 6 - Views\Contact\ContactList.ascx
<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of ContactManager.Group)" %>
<%@ Import Namespace="ContactManager" %>
<table class="data-table" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th class="actions edit">
Edit
</th>
<th class="actions delete">
Delete
</th>
<th>
Name
</th>
<th>
Phone
</th>
<th>
Email
</th>
</tr>
</thead>
<tbody>
<% For Each item in Model.Contacts %>
<tr>
<td class="actions edit">
<a href='<%= Url.Action("Edit", New With {.id=item.Id}) %>'><img src="../../Content/Edit.png" alt="Edit" /></a>
</td>
<td class="actions delete">
<%= Ajax.ImageActionLink("../../Content/Delete.png", "Delete", "Delete", New with { .id = item.Id }, New AjaxOptions With { .Confirm = "Delete contact?", .HttpMethod = "Delete", .UpdateTargetId = "divContactList" })%>
</td>
<th>
<%= Html.Encode(item.FirstName) %>
<%= Html.Encode(item.LastName) %>
</th>
<td>
<%= Html.Encode(item.Phone) %>
</td>
<td>
<%= Html.Encode(item.Email) %>
</td>
</tr>
<% Next %>
</tbody>
</table>
Delete 링크는 Ajax.ImageActionLink() 메서드에 대한 다음 호출로 렌더링됩니다.
<%= Ajax.ImageActionLink("../../Content/Delete.png", "Delete", "Delete", New with { .id = item.Id }, New AjaxOptions With { .Confirm = "Delete contact?", .HttpMethod = "Delete", .UpdateTargetId = "divContactList" })%<
참고
Ajax.ImageActionLink()는 ASP.NET MVC 프레임워크의 표준 부분이 아닙니다. Ajax.ImageActionLink()는 Contact Manager 프로젝트에 포함된 사용자 지정 도우미 메서드입니다.
AjaxOptions 매개 변수에는 두 가지 속성이 있습니다. 먼저 Confirm 속성을 사용하여 팝업 JavaScript 확인 대화 상자를 표시합니다. 둘째, HttpMethod 속성은 HTTP DELETE 작업을 수행하는 데 사용됩니다.
목록 7에는 연락처 컨트롤러에 추가된 새 AjaxDelete() 작업이 포함되어 있습니다.
목록 7 - Controllers\ContactController.vb(AjaxDelete)
<AcceptVerbs(HttpVerbs.Delete), ActionName("Delete")> _
Public Function AjaxDelete(ByVal id As Integer) As ActionResult
' Get contact and group
Dim contactToDelete = _service.GetContact(id)
Dim selectedGroup = _service.GetGroup(contactToDelete.Group.Id)
' Delete from database
_service.DeleteContact(contactToDelete)
' Return Contact List
Return PartialView("ContactList", selectedGroup)
End Function
AjaxDelete() 작업은 AcceptVerbs 특성으로 데코레이팅됩니다. 이 특성은 HTTP DELETE 작업 이외의 HTTP 작업을 제외하고 작업이 호출되지 않도록 합니다. 특히 HTTP GET을 사용하여 이 작업을 호출할 수 없습니다.
데이터베이스 레코드를 삭제한 후에는 삭제된 레코드를 포함하지 않는 업데이트된 연락처 목록을 표시해야 합니다. AjaxDelete() 메서드는 ContactList 부분 및 업데이트된 연락처 목록을 반환합니다.
요약
이 반복에서는 Contact Manager 애플리케이션에 Ajax 기능을 추가했습니다. Ajax를 사용하여 애플리케이션의 응답성과 성능을 향상했습니다.
먼저 연락처 그룹을 클릭하면 전체 보기가 업데이트되지 않도록 인덱스 보기를 리팩터링했습니다. 대신 연락처 그룹을 클릭하면 연락처 목록만 업데이트됩니다.
다음으로, jQuery 애니메이션 효과를 사용하여 연락처 목록에서 페이드 아웃하고 페이드합니다. Ajax 애플리케이션에 애니메이션을 추가하여 애플리케이션 사용자에게 브라우저 진행률 표시줄에 해당하는 애니메이션을 제공할 수 있습니다.
또한 Ajax 애플리케이션에 브라우저 기록 지원을 추가했습니다. 사용자가 브라우저 뒤로 및 앞으로 단추를 클릭하여 인덱스 보기의 상태를 변경할 수 있도록 했습니다.
마지막으로 HTTP DELETE 작업을 지원하는 삭제 링크를 만들었습니다. Ajax 삭제를 수행하면 사용자가 추가 삭제 확인 페이지를 요청하지 않고도 데이터베이스 레코드를 삭제할 수 있습니다.