메뉴 바로가기 검색 및 카테고리 바로가기

한빛미디어

편집자/저자토크

ASP.NET 2.0 새 기능: 콜백 관리자

저자: Wei-Meng Lee, 역 한동훈
원문: http://www.ondotnet.com/pub/a/dotnet/2004/08/09/callbackmanager.html

웹 응용 프로그램이 갖고 있는 본질적인 문제는 라운드 트립(round-trip)이다. 웹 페이지가 어떤 것을 서버에 전달하고, 페이지를 다시 로딩하는 라운드 트립은 비용이 많이 드는 일이며, 시간이 필요하다. 페이지 일부를 변경할 때, 해당 부분만 변경하지 못하고 페이지 전체를 다시 로딩해야 한다. 예를 들어, 사용자가 등록을 위해 국가명을 "US"로 선택할 때 드랍 다운 목록에서 미국에 있는 모든 주를 표시해야 한다. 마찬가지로 사용자가 미국이 아닌 다른 나라를 선택하면 선택한 국가에 해당하는 주를 표시해야 한다. 지금까지 이런 것들은 전체 페이지를 다시 로딩하여 해결한다. 이런 방법은 느리며, 사용자를 불편하게 만든다.(다른 방법은 클라이언트에 전체 주 목록을 보내고 자바 스크립트를 사용하여 사용자가 국가를 선택할 때 해당 주를 보여주는 방법인데, 이것은 매우 큰 페이지를 만들게 되며, 로딩 시간이 길어진다.)

ASP.NET 1.0/1.1 개발자가 이런 전송문제를 해결하는 방법은 Microsoft XMLHTTP ActiveX 객체를 사용해서 클라이언트 자바스크립트에서 서버측 메서드로 요청을 전송하는 것이다. ASP.NET 2.0에서는 콜백 관리자(Callback Manager)로 알려진 함수로 캡슐화하여 이러한 절차를 간단하게 만들 수 있다.

ASP.NET 2.0 콜백 관리자는 서버와 클라이언트가 데이터를 주고 받는 복잡함을 캡슐화하기 위해 XMLHTTP를 사용한다. 콜백 관리자를 사용하기 위해서는 웹 브라우저가 XMLHTTP를 지원해야하며, 현재까지는 인터넷 익스플로러 밖에 없다.

간단한 예

ASP.NET 2.0 콜백 관리자가 동작하는 것을 설명하기 위해 그림 1과 같은 간단한 웹 응용 프로그램을 만들었다. 예제에서 사용자가 텍스트 박스에 우편 번호를 입력하면 서버와 통신하지 않고 해당 주소를 가져온다. 우편번호를 입력받기 위해 TextBox 컨트롤을 사용하고, 입력된 우편번호로부터 주소를 가져오기 위해 Button 컨트롤을 사용한다. 이 HTML 컨트롤은 서버에 있는 코드를 호출한다. 결과는 City와 State 텍스트박스에 표시된다.

폼에 DropDownList 컨트롤이 2개 있다. 사용자가 특정 국가를 선택하면, 거기에 해당하는 주(또는 도시)를 서버에서 가져와서 두번째 DropDownList 컨트롤에 표시한다.

그림1
그림1. 컨트롤 디자인

먼저, 웹 폼에서 필요한 데이터를 받아오기 위해서는 ICallbackEventHandler 인터페이스를 구현하는 것이다. 나중에 사용하기 위해 문자열을 하나 선언했다.

Partial Class Default_aspx
   Implements ICallbackEventHandler
   Public callbackStr As String

ICallbackEventHandler 인터페이스는 RaiseCallbackEvent 함수만 정의하고 있다. 이 함수는 클라이언트가 서버로 전송할 때 호출된다. 여기서는, 우편번호에 해당하는 도시와 주 정보를 검사하고, 해당 국가의 주와 도시를 가져오기 위해 RaiseCallbackEvent 함수를 사용한다.

이상적으로는 모든 데이터는 웹 서비스에서 가져와야하지만, 예제를 간단하게 하기 위해 결과를 직접 코드로 작성했다.

Public Function RaiseCallbackEvent(ByVal eventArgument As String) As _
   String Implements _
   System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent

   If eventArgument.StartsWith("1:") Then
      "---strips away the command
      eventArgument = eventArgument.Substring(2)
      "---get city and state based on Zipcode
      Select Case eventArgument
         Case "95472" : Return "Sebastopol,CA"
         Case "02140" : Return "Cambridge,MA"
         Case Else
            Return "ZipCode not valid."
      End Select
   ElseIf eventArgument.StartsWith("2:") Then
      "---strips away the command
      eventArgument = eventArgument.Substring(2)
      "---get states and cities related to country
      Select Case eventArgument
         Case "Sing" : Return "Singapore,"
         Case "US" : Return _
   "Alabama,California,Maryland,Massachusetts,New York,Oklahoma,Wisconsin,"
         Case "UK" : Return _
   "Birmingham,Cambridge,Christchurch,Leeds,Sheffield,"
         Case Else
            Return ""
      End Select
   Else
         Return "Command not recognized"
   End If
End Function

eventArgument 매개변수가 클라이언트에 절단된다. 우편번호에 해당하는 주와 도시를 가져오기 위해 eventArgument 매개변수는 다음과 같은 형태로 구성된다.

1:02140

1:은 명령을 의미하며, 02140은 우편번호를 의미한다.

해당 국가의 모든 주와 도시를 가져오는 eventArgument 매개변수는 다음과 같이 구성된다.

2:US

2:은 명령을 의미하며, US는 국가 코드를 의미한다.

첫번째 명령에서 주의할 점은 도시와 주는 Sebastopol,CA와 같이 콤마(,)로 구분되어 있다는 점이다.

두번째 명령에서도 주 또는 도시명은 콤마로 구분한다.

Alabama,California,Maryland,Massachusetts,New York,Oklahoma,Wisconsin,

다음으로 Page_Load 이벤트에서 Page 클래스의 GetCallbackEventReference를 사용하여 RaiseCallbackEvent에 대한 호출을 수행하는 코드를 작성한다.

Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
   Handles Me.Load

   ddlCountry.Attributes.Add("onChange", "GetStatesFromServer()")
   callbackStr = Page.GetCallbackEventReference(Me, "Command", _
                        "CallBackHandler", "context", "onError")
End Sub

위 코드는 callbackStr 변수에 다음과 같은 문자열을 저장한다.

WebForm_DoCallback("__Page",Command,CallBackHandler,context,onError)

여기서 중요한 부분은 Command 변수가 서버에 전달되는 문자열에 대한 참조를 갖고 있다는 점이며, CallBackHandler는 서버에서 클라이언트로 결과를 반환할때 클라이언트에서 호출되는 함수를 의미한다.

이제, 클라이언트측에서 사용할 함수를 정의해보자. 웹 폼에서 소스 뷰로 이동해서 다음과 같은 스크립트를 작성한다.

...
mmlt;/headmmgt;
mmlt;bodymmgt;
mmlt;scriptmmgt;
function GetStateFromZip(){
   var Command = "1:" + document.forms[0].elements["txtZipCode"].value;
   var context = new Object();
   context.CommandName = "GetStateFromZip";
   mmlt;%=callbackStr%mmgt;
}
  
function GetStatesFromServer() {
   var Command = "2:" + document.forms[0].elements["ddlCountry"].value;
   var context = new Object();
   context.CommandName = "GetStatesFromCountry";
   mmlt;%=callbackStr%mmgt;
}
      
function CallBackHandler(result, context) {
   if (context.CommandName == "GetStateFromZip" ) {
      var indexofComma = result.indexOf(",");
      var City = result.substring(0,indexofComma);
      var State = result.substring(indexofComma+1,result.length);
      document.forms[0].elements["txtState"].value = State;
      document.forms[0].elements["txtCity"].value = City;
   } else
   if (context.CommandName == "GetStatesFromCountry")
   {
      document.forms[0].elements["ddlState"].options.length=0;
      while (result.length>0) {
         var indexofComma = result.indexOf(",");
         var State = result.substring(0,indexofComma);
         result = result.substring(indexofComma+1)

         opt = new Option(State,State);
         document.forms[0].elements["ddlState"].add(opt);
      }
   }
}
    
function onError(message, context) {
   alert("Exception :\n" + message);
}
      
mmlt;/scriptmmgt;
mmlt;form id="form1" runat="server"mmgt;
...

GetStateFromZip과 GetStatesFromServer 함수는 서버로 전달되는 요청을 문자열화한다. TextBox 컨트롤과 DropDownList 컨트롤의 값을 가져오고, 이것을 callbackStr에 저장한다. <%=callbackStr%>는 생성된 문자열을 함수안에 삽입하는 역할을 하며, 웹 페이지가 실행되는 동안 다음과 같은 코드가 생성될 것이다.

function GetStateFromZip(){
   var Command = "1:" + document.forms[0].elements["txtZipCode"].value;
   var context = new Object();
   context.CommandName = "GetStateFromZip";
   WebForm_DoCallback("__Page",Command,CallBackHandler,context,onError)
}

function GetStatesFromServer(){
   var Command = "2:" + document.forms[0].elements["ddlCountry"].value;
   var context = new Object();
   context.CommandName = "GetStatesFromCountry";
   WebForm_DoCallback("__Page",Command,CallBackHandler,context,onError)
}

두 함수 모두 CallBackHandler 함수에 대한 호출을 반환하는 것을 알 수 있다. - CallBackHandler 함수는 서버에서 클라이언트로 결과를 반환할 때 호출된다. 따라서, 어떤 것이 반환 호출자(return caller)인지를 구분할 필요가 있다. 따라서, GetStateFromZip 또는 GetStatesFromCountry에서 호출된 것을 구별하기 위해 CommandName으로 컨텍스트 변수(Context Variable)을 사용하였다.

전달 되는 값에 따라 다양한 결과가 반환될 것이다. 결과는 분석된 다음에 페이지에 적절하게 표시된다.

예제를 마무리 하기 위해 GetStateFromZip 함수를 Button 컨트롤에 할당해야한다.

mmlt;input id="Button1" type="button" value="Get City and State"
         OnClick="GetStateFromZip()"
         style="width: 144px; height: 24px"/mmgt;

mmlt;asp:DropDownList ID="ddlCountry" Runat="Server" mmgt;
   mmlt;asp:ListItemmmgt;Select Countrymmlt;/asp:ListItemmmgt;
   mmlt;asp:ListItem Value="US"mmgt;United Statesmmlt;/asp:ListItemmmgt;
   mmlt;asp:ListItem Value="Sing"mmgt;Singaporemmlt;/asp:ListItemmmgt;
   mmlt;asp:ListItem Value="UK"mmgt;United Kingdommmlt;/asp:ListItemmmgt;
mmlt;/asp:DropDownListmmgt;

mmlt;asp:DropDownList ID="ddlState" Runat="server"mmgt;
mmlt;/asp:DropDownListmmgt;

Country를 표시하기 위한 DropDownList 컨트롤의 경우 Page_Load 이벤트에서 다음과 같은 문장을 사용한 것을 기억해야 한다.

ddlCountry.Attributes.Add("onChange", "GetStatesFromServer()")

위 코드는 DropDownList 컨트롤의 항목에 변경사항이 있을 때 GetStateFromServer 함수를 호출하라는 것을 의미한다.
역주: ASP.NET 컨트롤에 Attributes 컬렉션을 사용하여 특정 이벤트와 자바 스크립트를 연동할 수 있다. 이에 대해서는 [ASP.NET or .NET 초보자 FAQ]의 "Q. 마우스를 버튼에 위치시키거나 링크를 위치시킬 때 색상을 변경하려면 어떻게 합니까?"나 "Q. 삭제 버튼을 클릭하면 대화창을 띄워서 확인하고 싶습니다." 등을 참고하기 바란다.
응용 프로그램을 테스트하기 위해 F5를 누른다. 이제, 포스트백 없이 서버와 액세스하는 것을 볼 수 있을 것이다.(그림2)

그림2
그림2. 포스트백을 사용하지 않기 위해 콜백 관리자를 사용한 화면

주의: 자바스크립트는 대소문자를 구분하기 때문에 컨트롤 이름에 대소문자를 정확하게 사용해야 한다.

요약

ASP.NET 2.0에서 콜백 관리자는 잦은 응답을 요구하는 웹 응용 프로그램을 구축하게 해주는 매우 유용한 기능이다. 그러나 RaiseCallbackEvent 함수를 작성해야하고, 문자열 형식의 결과값을 반환해야 한다는 것에 주의해야 한다. 따라서, 클라이언트에서 서버로 복잡한 데이터 형식을 전달해야 한다면 이를 문자열로 순차화(serialize)하여 전달하고, 다시 객체로 복원하는 과정을 거쳐야한다.

Wie-Meng Lee는 Active Developer의 공동 설립자이자 기술 전문가이다. 이 회사는 최신 기술에 대한 실무 위주의 교육을 제공하는 회사이다. 그는 O’Reilly의 Windows XP Unwired의 저자이기도 하다.
TAG :
댓글 입력
자료실