ASP.NET AJAX 웹 서비스 이해

작성 자: Scott Cate

Web Services는 분산 시스템 간에 데이터를 교환하기 위한 플랫폼 간 솔루션을 제공하는 .NET 프레임워크의 필수적인 부분입니다. 웹 서비스는 일반적으로 다양한 운영 체제, 개체 모델 및 프로그래밍 언어가 데이터를 보내고 받을 수 있도록 하는 데 사용되지만, 데이터를 ASP.NET AJAX 페이지에 동적으로 삽입하거나 페이지에서 백 엔드 시스템으로 데이터를 보내는 데도 사용할 수 있습니다. 이 모든 작업은 포스트백 작업에 의존하지 않고 수행할 수 있습니다.

ASP.NET AJAX를 사용하여 웹 서비스 호출

Dan Wahlin

Web Services는 분산 시스템 간에 데이터를 교환하기 위한 플랫폼 간 솔루션을 제공하는 .NET 프레임워크의 필수적인 부분입니다. 웹 서비스는 일반적으로 다양한 운영 체제, 개체 모델 및 프로그래밍 언어가 데이터를 보내고 받을 수 있도록 하는 데 사용되지만, 데이터를 ASP.NET AJAX 페이지에 동적으로 삽입하거나 페이지에서 백 엔드 시스템으로 데이터를 보내는 데도 사용할 수 있습니다. 이 모든 작업은 포스트백 작업에 의존하지 않고 수행할 수 있습니다.

ASP.NET AJAX UpdatePanel 컨트롤은 AJAX가 모든 ASP.NET 페이지를 사용하도록 설정하는 간단한 방법을 제공하지만 UpdatePanel을 사용하지 않고 서버의 데이터에 동적으로 액세스해야 하는 경우가 있을 수 있습니다. 이 문서에서는 ASP.NET AJAX 페이지 내에서 Web Services를 만들고 사용하여 이 작업을 수행하는 방법을 알아보세요.

이 문서에서는 핵심 ASP.NET AJAX 확장에서 사용할 수 있는 기능과 AutoCompleteExtender라는 ASP.NET AJAX 도구 키트의 웹 서비스 사용 컨트롤에 중점을 줍니다. 다루는 항목에는 AJAX 지원 웹 서비스 정의, 클라이언트 프록시 만들기 및 JavaScript를 사용하여 Web Services 호출이 포함됩니다. ASP.NET 페이지 메서드에 대한 웹 서비스 호출을 직접 수행할 수 있는 방법도 확인할 수 있습니다.

Web Services 구성

Visual Studio 2008을 사용하여 새 웹 사이트 프로젝트를 만들 때 web.config 파일에는 이전 버전의 Visual Studio 사용자에게 익숙하지 않을 수 있는 여러 가지 새로운 추가 사항이 있습니다. 이러한 수정 사항 중 일부는 "asp" 접두사를 ASP.NET AJAX 컨트롤에 매핑하므로 페이지에서 사용할 수 있고 다른 수정 내용은 필수 HttpHandlers 및 HttpModules를 정의합니다. 목록 1은 웹 서비스 호출에 <httpHandlers> 영향을 주는 web.config 요소에 대한 수정 사항을 보여 줍니다. .asmx 호출을 처리하는 데 사용되는 기본 HttpHandler가 제거되고 System.Web.Extensions.dll 어셈블리에 있는 ScriptHandlerFactory 클래스로 대체됩니다. System.Web.Extensions.dll ASP.NET AJAX에서 사용하는 모든 핵심 기능을 포함합니다.

목록 1. ASP.NET AJAX 웹 서비스 처리기 구성

<httpHandlers>
     <remove verb="*" path="*.asmx"/>
     <add verb="*" path="*.asmx" validate="false"
          type="System.Web.Script.Services.ScriptHandlerFactory,
          System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
          PublicKeyToken=31bf3856ad364e35"/>
</httpHandlers>

이 HttpHandler 교체는 JavaScript 웹 서비스 프록시를 사용하여 JSON(JavaScript Object Notation) 호출을 ASP.NET AJAX 페이지에서 .NET Web Services로 수행할 수 있도록 하기 위해 수행됩니다. ASP.NET AJAX는 일반적으로 Web Services와 연결된 표준 SOAP(Simple Object Access Protocol) 호출이 아닌 웹 서비스에 JSON 메시지를 보냅니다. 이로 인해 전체 요청 및 응답 메시지가 더 작아지게 될 수 있습니다. 또한 ASP.NET AJAX JavaScript 라이브러리가 JSON 개체로 작동하도록 최적화되어 있으므로 데이터를 보다 효율적으로 클라이언트 쪽에서 처리할 수 있습니다. 목록 2 및 목록 3은 JSON 형식으로 직렬화된 웹 서비스 요청 및 응답 메시지의 예를 보여 줍니다. 목록 2에 표시된 요청 메시지는 값이 "Belgium"인 국가 매개 변수를 전달하고 목록 3의 응답 메시지는 Customer 개체 및 관련 속성의 배열을 전달합니다.

목록 2. JSON으로 직렬화된 웹 서비스 요청 메시지

{"country":"Belgium"}

> [! 참고] 작업 이름은 웹 서비스에 대한 URL의 일부로 정의됩니다. 또한 요청 메시지가 항상 JSON을 통해 제출되는 것은 아닙니다. Web Services는 UseHttpGet 매개 변수가 true로 설정된 ScriptMethod 특성을 활용하여 매개 변수가 쿼리 문자열 매개 변수를 통해 전달되도록 할 수 있습니다.

목록 3. JSON으로 직렬화된 웹 서비스 응답 메시지

[{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Maison
     Dewey","CustomerID":"MAISD","ContactName":"Catherine
     Dewey"},{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Suprêmes
     délices","CustomerID":"SUPRD","ContactName":"Pascale
     Cartrain"}]

다음 섹션에서는 JSON 요청 메시지를 처리하고 단순 및 복합 형식으로 응답할 수 있는 Web Services를 만드는 방법을 알아봅니다.

AJAX-Enabled 웹 서비스 만들기

ASP.NET AJAX 프레임워크는 Web Services를 호출하는 여러 가지 방법을 제공합니다. AutoCompleteExtender 컨트롤(ASP.NET AJAX 도구 키트에서 사용 가능) 또는 JavaScript를 사용할 수 있습니다. 그러나 서비스를 호출하기 전에 클라이언트 스크립트 코드에서 호출할 수 있도록 AJAX를 사용하도록 설정해야 합니다.

ASP.NET Web Services를 새로 사용하든 그렇지 않든 간에 서비스를 만들고 AJAX를 사용하도록 설정하는 것이 간단합니다. .NET Framework는 2002년 처음 릴리스된 이래로 ASP.NET Web Services 만들기를 지원해 왔으며, ASP.NET AJAX 확장은 .NET Framework의 기본 기능 집합을 기반으로 하는 추가 AJAX 기능을 제공합니다. Visual Studio .NET 2008 베타 2는 .asmx 웹 서비스 파일 만들기를 기본적으로 지원하며 System.Web.Services.WebService 클래스의 클래스 옆에 연결된 코드를 자동으로 파생합니다. 클래스에 메서드를 추가할 때 Web Service 소비자가 메서드를 호출하려면 WebMethod 특성을 적용해야 합니다.

목록 4에서는 GetCustomersByCountry()라는 메서드에 WebMethod 특성을 적용하는 예제를 보여 줍니다.

목록 4. 웹 서비스에서 WebMethod 특성 사용

[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}

GetCustomersByCountry() 메서드는 국가 매개 변수를 허용하고 Customer 개체 배열을 반환합니다. 메서드에 전달된 국가 값은 비즈니스 계층 클래스로 전달되며, 이 클래스는 데이터 계층 클래스를 호출하여 데이터베이스에서 데이터를 검색하고 Customer 개체 속성을 데이터로 채우고 배열을 반환합니다.

ScriptService 특성 사용

WebMethod 특성을 추가하면 표준 SOAP 메시지를 웹 서비스에 보내는 클라이언트에서 GetCustomersByCountry() 메서드를 호출할 수 있지만, ASP.NET AJAX 애플리케이션에서 JSON 호출을 기본적으로 수행할 수 없습니다. JSON 호출을 허용하려면 ASP.NET AJAX 확장의 ScriptService 특성을 웹 서비스 클래스에 적용해야 합니다. 이렇게 하면 웹 서비스가 JSON을 사용하여 형식이 지정된 응답 메시지를 보낼 수 있으며 클라이언트 쪽 스크립트가 JSON 메시지를 전송하여 서비스를 호출할 수 있습니다.

목록 5는 CustomersService라는 웹 서비스 클래스에 ScriptService 특성을 적용하는 예제를 보여 줍니다.

목록 5. ScriptService 특성을 사용하여 웹 서비스 AJAX 사용

[System.Web.Script.Services.ScriptService]
[WebService(Namespace = "http://xmlforasp.net")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CustomersService : System.Web.Services.WebService
{
     [WebMethod]
     public Customer[] GetCustomersByCountry(string country)
     {
          return Biz.BAL.GetCustomersByCountry(country);
     }
}

ScriptService 특성은 AJAX 스크립트 코드에서 호출할 수 있음을 나타내는 표식 역할을 합니다. 실제로 백그라운드에서 발생하는 JSON 직렬화 또는 역직렬화 작업을 처리하지 않습니다. ScriptHandlerFactory(web.config 구성됨) 및 기타 관련 클래스는 대량의 JSON 처리를 수행합니다.

ScriptMethod 특성 사용

ScriptService 특성은 ASP.NET AJAX 페이지에서 사용할 수 있도록 .NET 웹 서비스에서 정의해야 하는 유일한 ASP.NET AJAX 특성입니다. 그러나 ScriptMethod라는 다른 특성은 서비스의 웹 메서드에 직접 적용할 수도 있습니다. ScriptMethod는 및 를 ResponseFormat 포함한 UseHttpGet세 가지 속성을 정의합니다XmlSerializeString. 이러한 속성의 값을 변경하면 웹 메서드에서 수락한 요청 형식을 GET으로 변경해야 하는 경우, 웹 메서드가 원시 XML 데이터를 또는 XmlElement 개체 형식 XmlDocument 으로 반환해야 하거나 서비스에서 반환된 데이터가 항상 JSON 대신 XML로 직렬화되어야 하는 경우에 유용할 수 있습니다.

웹 메서드가 POST 요청이 아닌 GET 요청을 수락해야 하는 경우 UseHttpGet 속성을 사용할 수 있습니다. 요청은 Web Method 입력 매개 변수가 QueryString 매개 변수로 변환된 URL을 사용하여 전송됩니다. UseHttpGet 속성은 기본적으로 false로 설정되며 작업이 안전한 것으로 알려져 있고 중요한 데이터가 웹 서비스에 전달되지 않는 경우에만 로 설정 true 해야 합니다. 목록 6은 UseHttpGet 속성과 함께 ScriptMethod 특성을 사용하는 예제를 보여 줍니다.

목록 6. UseHttpGet 속성과 함께 ScriptMethod 특성을 사용합니다.

[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
     return input;
}

목록 6에 표시된 HttpGetEcho 웹 메서드가 호출될 때 전송되는 헤더의 예는 다음과 같습니다.

GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1

Web Methods가 HTTP GET 요청을 수락하도록 허용하는 것 외에도 JSON이 아닌 서비스에서 XML 응답을 반환해야 하는 경우에도 ScriptMethod 특성을 사용할 수 있습니다. 예를 들어 웹 서비스는 원격 사이트에서 RSS 피드를 검색하여 XmlDocument 또는 XmlElement 개체로 반환할 수 있습니다. 그런 다음 클라이언트에서 XML 데이터를 처리할 수 있습니다.

목록 7에서는 ResponseFormat 속성을 사용하여 웹 메서드에서 XML 데이터를 반환하도록 지정하는 예제를 보여 줍니다.

목록 7. ResponseFormat 속성과 함께 ScriptMethod 특성을 사용합니다.

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
     XmlDocument doc = new XmlDocument();
     doc.Load(url);
     return doc.DocumentElement;
}

ResponseFormat 속성은 XmlSerializeString 속성과 함께 사용할 수도 있습니다. XmlSerializeString 속성의 기본값은 false입니다. 즉, 속성이 로 설정된 ResponseFormat.Xml경우 ResponseFormat 웹 메서드에서 반환된 문자열을 제외한 모든 반환 형식이 XML로 직렬화됩니다. 가 로 true설정되면 XmlSerializeString 웹 메서드에서 반환된 모든 형식이 문자열 형식을 포함하여 XML로 직렬화됩니다. ResponseFormat 속성에 XmlSerializeString 속성 값 ResponseFormat.Json 이 있으면 무시됩니다.

목록 8에서는 XmlSerializeString 속성을 사용하여 문자열을 XML로 직렬화하도록 강제하는 예제를 보여 줍니다.

목록 8. XmlSerializeString 속성과 함께 ScriptMethod 특성 사용

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
     return input;
}

목록 8에 표시된 GetXmlString 웹 메서드 호출에서 반환되는 값은 다음에 표시됩니다.

<?xml version="1.0"?>
     <string>Test</string>

기본 JSON 형식은 요청 및 응답 메시지의 전체 크기를 최소화하고 브라우저 간 방식으로 ASP.NET AJAX 클라이언트에서 더 쉽게 사용되지만 Internet Explorer 5 이상과 같은 클라이언트 애플리케이션에서 XML 데이터가 웹 메서드에서 반환될 것으로 예상할 때 ResponseFormat 및 XmlSerializeString 속성을 사용할 수 있습니다.

복합 형식 작업

목록 5는 웹 서비스에서 Customer라는 복합 형식을 반환하는 예제를 보여 줍니다. Customer 클래스는 FirstName 및 LastName과 같은 속성과 같은 여러 가지 간단한 형식을 내부적으로 정의합니다. AJAX 사용 웹 메서드에서 입력 매개 변수 또는 반환 형식으로 사용되는 복합 형식은 클라이언트 쪽으로 전송되기 전에 JSON으로 자동으로 직렬화됩니다. 그러나 중첩된 복합 형식(다른 형식 내에서 내부적으로 정의된 형식)은 기본적으로 독립 실행형 개체로 클라이언트에서 사용할 수 없습니다.

웹 서비스에서 사용하는 중첩된 복합 형식도 클라이언트 페이지에서 사용해야 하는 경우 ASP.NET AJAX GenerateScriptType 특성을 웹 서비스에 추가할 수 있습니다. 예를 들어 목록 9에 표시된 CustomerDetails 클래스에는 중첩된 복합 형식을 나타내는 Address 및 Gender 속성이 포함됩니다.

목록 9. 여기에 표시된 CustomerDetails 클래스에는 두 가지 중첩된 복합 형식이 포함되어 있습니다.

public class CustomerDetails : Customer
{
     public CustomerDetails()
     {
     }
     Address _Address;
     Gender _Gender = Gender.Unknown;
     public Address Address
     {
          get { return _Address; }
          set { _Address = value; }
     }
     public Gender Gender
     {
          get { return _Gender; }
          set { _Gender = value; }
     }
}

목록 9에 표시된 CustomerDetails 클래스 내에 정의된 Address 및 Gender 개체는 중첩 형식이므로 JavaScript를 통해 클라이언트 쪽에서 자동으로 사용할 수 없습니다(Address는 클래스이고 Gender는 열거형임). 웹 서비스 내에서 사용되는 중첩 형식을 클라이언트 쪽에서 사용할 수 있어야 하는 경우 앞에서 언급한 GenerateScriptType 특성을 사용할 수 있습니다(목록 10 참조). 이 특성은 서비스에서 다른 중첩된 복합 형식이 반환되는 경우 여러 번 추가할 수 있습니다. 웹 서비스 클래스 또는 특정 웹 메서드 위에 직접 적용할 수 있습니다.

목록 10. GenerateScriptService 특성을 사용하여 클라이언트에서 사용할 수 있어야 하는 중첩 형식을 정의합니다.

[System.Web.Script.Services.ScriptService]
[System.Web.Script.Services.GenerateScriptType(typeof(Address))]
[System.Web.Script.Services.GenerateScriptType(typeof(Gender))]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class NestedComplexTypeService : System.Web.Services.WebService
{
     //Web Methods
}

웹 서비스에 특성을 적용하면 GenerateScriptType 주소 및 성별 형식이 클라이언트 쪽 ASP.NET AJAX JavaScript 코드에서 자동으로 사용할 수 있게 됩니다. 웹 서비스에 GenerateScriptType 특성을 추가하여 자동으로 생성되고 클라이언트로 전송되는 JavaScript의 예는 목록 11에 표시됩니다. 문서의 뒷부분에서 중첩된 복합 형식을 사용하는 방법을 확인할 수 있습니다.

목록 11. ASP.NET AJAX 페이지에서 사용할 수 있는 중첩된 복합 형식입니다.

if (typeof(Model.Address) === 'undefined')
{
     Model.Address=gtc("Model.Address");
     Model.Address.registerClass('Model.Address');
}
Model.Gender = function() { throw Error.invalidOperation(); }
Model.Gender.prototype = {Unknown: 0,Male: 1,Female: 2}
Model.Gender.registerEnum('Model.Gender', true);

이제 Web Services를 만들고 ASP.NET AJAX 페이지에서 액세스할 수 있도록 하는 방법을 살펴보았으므로 데이터를 검색하거나 웹 서비스로 보낼 수 있도록 JavaScript 프록시를 만들고 사용하는 방법을 살펴보겠습니다.

JavaScript 프록시 만들기

표준 웹 서비스(.NET 또는 다른 플랫폼)를 호출하려면 일반적으로 SOAP 요청 및 응답 메시지를 보내는 복잡성으로부터 보호하는 프록시 개체를 만들어야 합니다. ASP.NET AJAX 웹 서비스 호출을 사용하면 JSON 메시지 직렬화 및 역직렬화에 대한 걱정 없이 JavaScript 프록시를 만들고 사용하여 서비스를 쉽게 호출할 수 있습니다. javaScript 프록시는 ASP.NET AJAX ScriptManager 컨트롤을 사용하여 자동으로 생성될 수 있습니다.

Web Services를 호출할 수 있는 JavaScript 프록시 만들기는 ScriptManager의 Services 속성을 사용하여 수행됩니다. 이 속성을 사용하면 ASP.NET AJAX 페이지에서 포스트백 작업 없이 데이터를 보내거나 받기 위해 비동기적으로 호출할 수 있는 하나 이상의 서비스를 정의할 수 있습니다. ASP.NET AJAX ServiceReference 컨트롤을 사용하고 웹 서비스 URL을 컨트롤의 Path 속성에 할당하여 서비스를 정의합니다. 목록 12는 CustomersService.asmx라는 서비스를 참조하는 예제를 보여 줍니다.

<asp:ScriptManager ID="ScriptManager1" runat="server">
     <Services>
          <asp:ServiceReference Path="~/CustomersService.asmx" />
     </Services>
</asp:ScriptManager>

목록 12. ASP.NET AJAX 페이지에서 사용되는 웹 서비스 정의

ScriptManager 컨트롤을 통해 CustomersService.asmx에 대한 참조를 추가하면 JavaScript 프록시가 동적으로 생성되고 페이지에서 참조됩니다. 프록시는 스크립트> 태그를 <사용하여 포함되며 CustomersService.asmx 파일을 호출하고 /js를 끝에 추가하여 동적으로 로드됩니다. 다음 예제에서는 web.config 디버깅을 사용하지 않도록 설정할 때 JavaScript 프록시가 페이지에 포함되는 방법을 보여줍니다.

<script src="CustomersService.asmx/js" type="text/javascript"></script>

>[! 참고] 생성된 실제 JavaScript 프록시 코드를 보려면 원하는 .NET 웹 서비스에 대한 URL을 인터넷 Explorer 주소 상자에 입력하고 끝에 /js를 추가할 수 있습니다.

디버깅이 web.config 사용하도록 설정된 경우 JavaScript 프록시의 디버그 버전이 다음과 같이 페이지에 포함됩니다.

<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>

ScriptManager에서 만든 JavaScript 프록시는 스크립트> 태그의 src 특성을 사용하여 <참조하는 대신 페이지에 직접 포함할 수도 있습니다. ServiceReference 컨트롤의 InlineScript 속성을 true로 설정하여 이 작업을 수행할 수 있습니다(기본값은 false임). 이는 프록시가 여러 페이지에서 공유되지 않고 서버에 대한 네트워크 호출 수를 줄이려는 경우에 유용할 수 있습니다. InlineScript가 true로 설정된 경우 프록시 스크립트는 브라우저에서 캐시되지 않으므로 ASP.NET AJAX 애플리케이션의 여러 페이지에서 프록시를 사용하는 경우 기본값 false를 사용하는 것이 좋습니다. InlineScript 속성을 사용하는 예제는 다음과 같습니다.

<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>

JavaScript 프록시 사용

ScriptManager 컨트롤을 사용하여 ASP.NET AJAX 페이지에서 웹 서비스를 참조하면 웹 서비스에 대한 호출을 수행할 수 있으며 반환된 데이터는 콜백 함수를 사용하여 처리할 수 있습니다. 웹 서비스는 네임스페이스(있는 경우), 클래스 이름 및 웹 메서드 이름을 참조하여 호출됩니다. 웹 서비스에 전달된 모든 매개 변수는 반환된 데이터를 처리하는 콜백 함수와 함께 정의할 수 있습니다.

JavaScript 프록시를 사용하여 GetCustomersByCountry()라는 웹 메서드를 호출하는 예제가 목록 13에 표시됩니다. GetCustomersByCountry() 함수는 최종 사용자가 페이지에서 단추를 클릭할 때 호출됩니다.

목록 13. JavaScript 프록시를 사용하여 웹 서비스 호출

function GetCustomerByCountry()
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
     if (results != null)
     {
          CreateCustomersTable(results);
          GetMap(results);
     }
}

이 호출은 서비스에 정의된 InterfaceTraining 네임스페이스, CustomersService 클래스 및 GetCustomersByCountry 웹 메서드를 참조합니다. 텍스트 상자에서 가져온 국가 값과 비동기 웹 서비스 호출이 반환될 때 호출해야 하는 OnWSRequestComplete라는 콜백 함수를 전달합니다. OnWSRequestComplete는 서비스에서 반환된 Customer 개체의 배열을 처리하고 이를 페이지에 표시되는 테이블로 변환합니다. 호출에서 생성된 출력은 그림 1에 표시됩니다.

웹 서비스에 대한 비동기 AJAX 호출을 통해 얻은 데이터 바인딩

그림 1: 웹 서비스에 대한 비동기 AJAX 호출을 수행하여 가져온 데이터 바인딩 (전체 크기 이미지를 보려면 클릭)

JavaScript 프록시는 웹 메서드를 호출해야 하지만 프록시가 응답을 기다리지 않아야 하는 경우 Web Services에 단방향 호출을 수행할 수도 있습니다. 예를 들어 웹 서비스를 호출하여 작업 흐름과 같은 프로세스를 시작하지만 서비스에서 반환 값을 기다리지 않을 수 있습니다. 서비스에 대한 단방향 호출이 필요한 경우 목록 13에 표시된 콜백 함수를 생략할 수 있습니다. 콜백 함수가 정의되지 않았으므로 프록시 개체는 웹 서비스가 데이터를 반환할 때까지 기다리지 않습니다.

오류 처리

웹 서비스에 대한 비동기 콜백은 네트워크 가동 중단, 웹 서비스를 사용할 수 없음 또는 반환되는 예외와 같은 다양한 유형의 오류가 발생할 수 있습니다. 다행히 ScriptManager에서 생성된 JavaScript 프록시 개체를 사용하면 이전에 표시된 성공 콜백 외에도 오류 및 실패를 처리하기 위해 여러 콜백을 정의할 수 있습니다. 목록 14와 같이 웹 메서드 호출에서 표준 콜백 함수 바로 다음에 오류 콜백 함수를 정의할 수 있습니다.

목록 14. 오류 콜백 함수 정의 및 오류 표시

function GetCustomersByCountry() 
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, 
          OnWSRequestComplete, OnWSRequestFailed);
}
function OnWSRequestFailed(error)
{
     alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
          "Error: " + error.get_message() + "/r/n" +
          "Status Code: " + error.get_statusCode() + "/r/n" +
          "Exception Type: " + error.get_exceptionType() + "/r/n" +
          "Timed Out: " + error.get_timedOut());
}

웹 서비스가 호출될 때 발생하는 오류는 오류를 매개 변수로 나타내는 개체를 허용하는 OnWSRequestFailed() 콜백 함수를 호출하도록 트리거합니다. 오류 개체는 오류의 원인과 호출 시간이 초과되었는지 여부를 확인하기 위해 여러 가지 함수를 노출합니다. 목록 14는 다른 오류 함수를 사용하는 예제를 보여 줍니다. 그림 2는 함수에서 생성된 출력의 예를 보여 줍니다.

ASP.NET AJAX 오류 함수를 호출하여 생성된 출력입니다.

그림 2: ASP.NET AJAX 오류 함수를 호출하여 생성된 출력입니다. (전체 크기 이미지를 보려면 클릭)

웹 서비스에서 반환된 XML 데이터 처리

이전에 Web Method가 ResponseFormat 속성과 함께 ScriptMethod 특성을 사용하여 원시 XML 데이터를 반환하는 방법을 알아보았습니다. ResponseFormat이 ResponseFormat.Xml 설정되면 웹 서비스에서 반환된 데이터는 JSON이 아닌 XML로 직렬화됩니다. JavaScript 또는 XSLT를 사용하여 처리하기 위해 XML 데이터를 클라이언트에 직접 전달해야 하는 경우에 유용할 수 있습니다. 현재 인터넷 Explorer 5 이상은 MSXML에 대한 기본 제공 지원으로 인해 XML 데이터를 구문 분석하고 필터링하는 데 가장 적합한 클라이언트 쪽 개체 모델을 제공합니다.

웹 서비스에서 XML 데이터를 검색하는 것은 다른 데이터 형식을 검색하는 것과 다르지 않습니다. 먼저 JavaScript 프록시를 호출하여 적절한 함수를 호출하고 콜백 함수를 정의합니다. 호출이 반환되면 콜백 함수에서 데이터를 처리할 수 있습니다.

목록 15는 XmlElement 개체를 반환하는 GetRssFeed()라는 웹 메서드를 호출하는 예제를 보여 줍니다. GetRssFeed()는 검색할 RSS 피드의 URL을 나타내는 단일 매개 변수를 허용합니다.

목록 15. 웹 서비스에서 반환된 XML 데이터 작업

function GetRss()
{
     InterfaceTraining.DemoService.GetRssFeed(
          "https://blogs.interfacett.com/dan-wahlins-blog/rss.xml",
          OnWSRequestComplete);
}
function OnWSRequestComplete(result)
{
     if (document.all) //Filter for IE DOM since other browsers are limited
     {
          var items = result.selectNodes("//item");
          for (var i=0;i<items.length;i++)
          {
               var title = items[i].selectSingleNode("title").text;
               var href = items[i].selectSingleNode("link").text;
               $get("divOutput").innerHTML +=
               "<a href='" + href + "'>" + title + "</a><br/>";
          }
     }
     else
     {
          $get("divOutput").innerHTML = "RSS only available in IE5+";
     }
}

이 예제에서는 RSS 피드에 URL을 전달하고 OnWSRequestComplete() 함수에서 반환된 XML 데이터를 처리합니다. OnWSRequestComplete()는 먼저 브라우저가 인터넷인지 Explorer 확인하여 MSXML 파서가 사용 가능한지 여부를 확인합니다. 이 경우 XPath 문을 사용하여 RSS 피드 내의 모든 <항목> 태그를 찾습니다. 그런 다음 각 항목이 반복되고 연결된 <제목> 및 <링크> 태그가 배치되고 처리되어 각 항목의 데이터를 표시합니다. 그림 3에서는 JavaScript 프록시를 통해 GetRssFeed() 웹 메서드에 대한 ASP.NET AJAX 호출을 통해 생성된 출력의 예를 보여 줍니다.

복합 형식 처리

웹 서비스에서 수락하거나 반환하는 복합 형식은 JavaScript 프록시를 통해 자동으로 노출됩니다. 그러나 앞에서 설명한 대로 GenerateScriptType 특성이 서비스에 적용되지 않는 한 중첩된 복합 형식은 클라이언트 쪽에서 직접 액세스할 수 없습니다. 클라이언트 쪽에서 중첩된 복합 형식을 사용하려는 이유는 무엇인가요?

이 질문에 대답하려면 ASP.NET AJAX 페이지에 고객 데이터가 표시되고 최종 사용자가 고객의 주소를 업데이트할 수 있다고 가정합니다. 웹 서비스에서 주소 형식(CustomerDetails 클래스 내에 정의된 복합 형식)을 클라이언트로 보낼 수 있도록 지정하는 경우 코드 재사용을 위해 업데이트 프로세스를 별도의 함수로 나눌 수 있습니다.

RSS 데이터를 반환하는 웹 서비스를 호출하여 만드는 출력입니다.

그림 3: RSS 데이터를 반환하는 웹 서비스를 호출하여 만드는 출력입니다. (전체 크기 이미지를 보려면 클릭)

목록 16은 모델 네임스페이스에 정의된 Address 개체를 호출하고 업데이트된 데이터로 채우고 CustomerDetails 개체의 Address 속성에 할당하는 클라이언트 쪽 코드의 예를 보여 줍니다. 그런 다음 CustomerDetails 개체가 처리를 위해 웹 서비스에 전달됩니다.

목록 16. 중첩된 복합 형식 사용

function UpdateAddress()
{
     var cust = new Model.CustomerDetails();
     cust.CustomerID = $get("hidCustomerID").value;
     cust.Address = CreateAddress();
     InterfaceTraining.DemoService.UpdateAddress(cust,OnWSUpdateComplete);
}
function CreateAddress()
{
     var addr = new Model.Address();
     addr.Street = $get("txtStreet").value;
     addr.City = $get("txtCity").value;
     addr.State = $get("txtState").value;
     return addr;
}
function OnWSUpdateComplete(result)
{
     alert("Update " + ((result)?"succeeded":"failed")+ "!");
}

페이지 메서드 만들기 및 사용

Web Services는 다시 사용할 수 있는 서비스를 ASP.NET AJAX 페이지를 비롯한 다양한 클라이언트에 노출하는 훌륭한 방법을 제공합니다. 그러나 페이지가 다른 페이지에서 사용하거나 공유하지 않는 데이터를 검색해야 하는 경우가 있을 수 있습니다. 이 경우 서비스가 단일 페이지에서만 사용되므로 페이지에 데이터에 액세스할 수 있도록 .asmx 파일을 만드는 것은 과잉처럼 보일 수 있습니다.

ASP.NET AJAX는 독립 실행형 .asmx 파일을 만들지 않고 웹 서비스와 유사한 호출을 만들기 위한 또 다른 메커니즘을 제공합니다. 이 작업은 "페이지 메서드"라고 하는 기술을 사용하여 수행됩니다. 페이지 메서드는 WebMethod 특성이 적용된 페이지 또는 코드 옆에 있는 파일에 직접 포함된 정적(VB.NET 공유) 메서드입니다. WebMethod 특성을 적용하면 런타임에 동적으로 생성되는 PageMethods라는 특수 JavaScript 개체를 사용하여 호출할 수 있습니다. PageMethods 개체는 JSON serialization/deserialization 프로세스로부터 보호하는 프록시 역할을 합니다. PageMethods 개체를 사용하려면 ScriptManager의 EnablePageMethods 속성을 true로 설정해야 합니다.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>

목록 17은 ASP.NET 코드 옆에 있는 클래스에서 두 페이지 메서드를 정의하는 예제를 보여 줍니다. 이러한 메서드는 웹 사이트의 App_Code 폴더에 있는 비즈니스 계층 클래스에서 데이터를 검색합니다.

목록 17. 페이지 메서드 정의

[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
     return Biz.BAL.GetCustomersByID(id);
}

ScriptManager가 페이지에서 웹 메서드의 존재를 감지하면 앞에서 언급한 PageMethods 개체에 대한 동적 참조를 생성합니다. 웹 메서드 호출은 PageMethods 클래스와 메서드 이름 및 전달해야 하는 필요한 매개 변수 데이터를 참조하여 수행됩니다. 목록 18은 이전에 표시된 두 페이지 메서드를 호출하는 예제를 보여 줍니다.

목록 18. PageMethods JavaScript 개체를 사용하여 페이지 메서드를 호출합니다.

function GetCustomerByCountry() 
{
     var country = $get("txtCountry").value;
     PageMethods.GetCustomersByCountry(country, OnWSRequestComplete);
}
function GetCustomerByID() 
{
     var custID = $get("txtCustomerID").value;
     PageMethods.GetCustomersByID(custID, OnWSRequestComplete);
}
function OnWSRequestComplete(results) 
{
     var searchResults = $get("searchResults");
     searchResults.control.set_data(results);
     if (results != null) GetMap(results[0].Country,results);
}

PageMethods 개체를 사용하는 것은 JavaScript 프록시 개체를 사용하는 것과 매우 유사합니다. 먼저 페이지 메서드에 전달되어야 하는 모든 매개 변수 데이터를 지정한 다음 비동기 호출이 반환될 때 호출해야 하는 콜백 함수를 정의합니다. 오류 콜백을 지정할 수도 있습니다(오류 처리 예제는 목록 14 참조).

AutoCompleteExtender 및 ASP.NET AJAX 도구 키트

ASP.NET AJAX 도구 키트(에서 https://www.devexpress.com/Products/AJAX-Control-Toolkit사용 가능)는 웹 서비스에 액세스하는 데 사용할 수 있는 몇 가지 컨트롤을 제공합니다. 특히 도구 키트에는 JavaScript 코드를 전혀 작성하지 않고 웹 서비스를 호출하고 페이지에 데이터를 표시하는 데 사용할 수 있는 이라는 AutoCompleteExtender 유용한 컨트롤이 포함되어 있습니다.

AutoCompleteExtender 컨트롤을 사용하여 텍스트 상자의 기존 기능을 확장하고 사용자가 찾고 있는 데이터를 보다 쉽게 찾을 수 있습니다. 텍스트 상자에 입력할 때 컨트롤을 사용하여 웹 서비스를 쿼리하고 텍스트 상자 아래에 결과를 동적으로 표시할 수 있습니다. 그림 4에서는 AutoCompleteExtender 컨트롤을 사용하여 지원 애플리케이션에 대한 고객 ID를 표시하는 예제를 보여 줍니다. 사용자가 텍스트 상자에 다른 문자를 입력하면 입력에 따라 아래에 다른 항목이 표시됩니다. 그런 다음 사용자는 원하는 고객 ID를 선택할 수 있습니다.

ASP.NET AJAX 페이지 내에서 AutoCompleteExtender를 사용하려면 AjaxControlToolkit.dll 어셈블리를 웹 사이트의 bin 폴더에 추가해야 합니다. 도구 키트 어셈블리가 추가되면 web.config 참조하여 포함된 컨트롤을 애플리케이션의 모든 페이지에서 사용할 수 있도록 합니다. 이 작업은 web.config <컨트롤 태그 내에 다음 태그를 추가하여 수행할 수 있습니다.>

<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>

특정 페이지에서만 컨트롤을 사용해야 하는 경우 web.config 업데이트하는 대신 다음에 표시된 대로 참조 지시문을 페이지 맨 위에 추가하여 참조할 수 있습니다.

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" 
     TagPrefix="ajaxToolkit" %>

AutoCompleteExtender 컨트롤 사용.

그림 4: AutoCompleteExtender 컨트롤 사용. (전체 크기 이미지를 보려면 클릭)

웹 사이트가 ASP.NET AJAX 도구 키트를 사용하도록 구성되면 일반 ASP.NET 서버 컨트롤을 추가하는 것처럼 AutoCompleteExtender 컨트롤을 페이지에 추가할 수 있습니다. 목록 19는 컨트롤을 사용하여 웹 서비스를 호출하는 예제를 보여 줍니다.

목록 19. ASP.NET AJAX 도구 키트 AutoCompleteExtender 컨트롤을 사용합니다.

<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
     MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
     ServicePath="~/CustomersService.asmx"
     TargetControlID="txtCustomerID" />

AutoCompleteExtender에는 서버 컨트롤에 있는 표준 ID 및 runat 속성을 비롯한 여러 가지 속성이 있습니다. 이 외에도 웹 서비스가 데이터에 대해 쿼리되기 전에 최종 사용자가 입력하는 문자 수를 정의할 수 있습니다. 목록 19에 표시된 MinimumPrefixLength 속성을 사용하면 문자가 텍스트 상자에 입력될 때마다 서비스가 호출됩니다. 사용자가 문자를 입력할 때마다 웹 서비스가 호출되어 텍스트 상자의 문자와 일치하는 값을 검색할 때마다 이 값을 설정해야 합니다. 호출할 웹 서비스와 대상 웹 메서드는 각각 ServicePath 및 ServiceMethod 속성을 사용하여 정의됩니다. 마지막으로 TargetControlID 속성은 AutoCompleteExtender 컨트롤을 후크할 텍스트 상자를 식별합니다.

호출되는 웹 서비스는 앞에서 설명한 대로 ScriptService 특성을 적용해야 하며 대상 웹 메서드는 prefixText 및 count라는 두 개의 매개 변수를 수락해야 합니다. prefixText 매개 변수는 최종 사용자가 입력한 문자를 나타내고 count 매개 변수는 반환할 항목 수를 나타냅니다(기본값은 10). 목록 20은 목록 19의 앞부분에 표시된 AutoCompleteExtender 컨트롤에서 호출한 GetCustomerIDs 웹 메서드의 예를 보여 줍니다. 웹 메서드는 비즈니스 계층 메서드를 호출하여 데이터 필터링 및 일치하는 결과 반환을 처리하는 데이터 계층 메서드를 호출합니다. 데이터 계층 메서드에 대한 코드는 목록 21에 표시됩니다.

목록 20. AutoCompleteExtender 컨트롤에서 보낸 데이터 필터링

[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count) 
{
     return Biz.BAL.GetCustomerIDs(prefixText, count);
}

목록 21. 최종 사용자 입력에 따라 결과를 필터링합니다.

public static string[] GetCustomerIDs(string prefixText, int count)
{
     //Customer IDs cached in _CustomerIDs field to improve performance
     if (_CustomerIDs == null)
     {
          List<string> ids = new List<string>();
          //SQL text used for simplicity...recommend using sprocs
          string sql = "SELECT CustomerID FROM Customers";
          DbConnection conn = GetDBConnection();
          conn.Open();
          DbCommand cmd = conn.CreateCommand();
          cmd.CommandText = sql;
          DbDataReader reader = cmd.ExecuteReader();
          while (reader.Read())
          {
               ids.Add(reader["CustomerID"].ToString());
          }
          reader.Close();
          conn.Close();
          _CustomerIDs = ids.ToArray();
     }
     int index = Array.BinarySearch(_CustomerIDs, prefixText, new CaseInsensitiveComparer());
     //~ is bitwise complement (reverse each bit)
     if (index < 0) index = ~index;
     int matchingCount;
     for (matchingCount = 0; matchingCount < count && index + matchingCount < _CustomerIDs.Length; matchingCount++)
     {
          if (!_CustomerIDs[index + matchingCount].StartsWith(prefixText, StringComparison.CurrentCultureIgnoreCase))
          {
               break;
          }
     }
     String[] returnValue = new string[matchingCount];
     if (matchingCount > 0)
     {
          Array.Copy(_CustomerIDs, index, returnValue, 0, matchingCount);
     }
     return returnValue;
}

결론

ASP.NET AJAX는 요청 및 응답 메시지를 처리하기 위해 많은 사용자 지정 JavaScript 코드를 작성하지 않고 웹 서비스를 호출하는 데 탁월한 지원을 제공합니다. 이 문서에서는 AJAX에서 .NET Web Services를 사용하여 JSON 메시지를 처리할 수 있도록 하는 방법과 ScriptManager 컨트롤을 사용하여 JavaScript 프록시를 정의하는 방법을 살펴보았습니다. 또한 JavaScript 프록시를 사용하여 Web Services를 호출하고, 단순하고 복잡한 형식을 처리하고, 오류를 처리하는 방법도 살펴보았습니다. 마지막으로 페이지 메서드를 사용하여 웹 서비스 호출을 만들고 만드는 프로세스를 간소화하는 방법과 AutoCompleteExtender 컨트롤이 입력할 때 최종 사용자에게 도움을 제공하는 방법을 살펴보았습니다. ASP.NET AJAX에서 사용할 수 있는 UpdatePanel은 단순성으로 인해 많은 AJAX 프로그래머가 선택할 수 있지만 JavaScript 프록시를 통해 웹 서비스를 호출하는 방법을 아는 것은 많은 애플리케이션에서 유용할 수 있습니다.

사용자 정보

Dan Wahlin(microsoft Most Valuable Professional for ASP.NET 및 XML Web Services)은 인터페이스 기술 교육(http://www.interfacett.com)의 .NET 개발 강사 및 아키텍처 컨설턴트입니다. Dan은 ASP.NET Developers 웹 사이트(www.XMLforASP.NET)를 위한 XML을 설립했으며, INETA 스피커의 국에 있으며 여러 컨퍼런스에서 연설하고 있습니다. Dan이 공동 저술한 Professional Windows DNA(Wrox), ASP.NET: 팁, 자습서 및 코드(Sams), ASP.NET 1.1 참가자 솔루션, Professional ASP.NET 2.0 AJAX(Wrox), ASP.NET 2.0 MVP Hacks 및 ASP.NET Developers(Sams)를 위한 XML 작성 코드, 기사 또는 책을 쓰지 않을 때 댄은 음악을 쓰고 녹음하고 아내와 아이들과 골프와 농구를 하는 것을 즐깁니다.

Scott Cate은 1997년부터 Microsoft 웹 기술과 함께 일해 왔으며 myKB.com(www.myKB.com)의 사장으로 기술 자료 소프트웨어 솔루션에 중점을 둔 ASP.NET 기반 애플리케이션을 작성하는 일을 전문으로 합니다. Scott은 이메일 scott.cate@myKB.com 또는 ScottCate.com 블로그를 통해 연락할 수 있습니다.