XMLHttpRequest
개요
XMLHttpRequest는 본래 ActiveX의 구성요소 중 하나로 IE5에서 처음 구현되었습니다. 최근에Firefox, Safari, Opera등의 브라우저에서 XMLHttpRequest를 지원하면서 AJAX의 기반기술이되었습니다. 하지만 여전히 XMLHttpRequest는 W3C 표준이 아님을 기억해야 합니다. W3C의 표준안은 DOMLevel3 Load and Save라는 이름의 스펙으로 존재합니다. 하지만 DOM Level3는 현재 대부분의 브라우저에서지원하지 않습니다.
동작 방식
그림 1 XMLHttpRequest 동기/비동기 요청 흐름
그림 2 AJAX를 통한 클라이언트-서버간 상호작용
- 그림출처 : Foundation of AJAX(APRESS)
프로그래밍
XMLHttpRequest 생성
var xmlHttp; function createXMLHttpRequest(){ if(window.ActiveXObject){ xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if(window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } } |
표 1 XMLHttpRequest 생성
MS IE의 경우 ActiveX 객체로 구현하며 그 외 브라우저의 경우에는 순수 자바스크립트 객체로 구현하고 있습니다.이러한 이유로 브라우저를 구분하여 XMLHttpRequest 인스턴스를 생성하도록 코딩을 해 주어야 합니다. 다행스럽게도동적으로 타입이 지정된 이후에는 XMLHttpRequest메서드와 프로퍼티가 서로 호환되어 동일한 코드를 사용할 수 있습니다.
요청 보내기
function startRequestXml() { createXMLHttpRequest(); xmlHttp.onreadystatechange = handleStateChange; xmlHttp.open("GET", "simpleResponse.xml", true); xmlHttp.send(null); } |
표 2 XMLHttpRequest 요청 보내기
- XMLHttpRequest 인스턴스를 생성합니다.
- XMLHttpRequest의 상태 변화를 처리하는 콜백함수를 설정합니다.
- Open() 메서드를 사용해서 요청 프로퍼티를 설정합니다.
첫 번째 매개변수는 요청 메서드("GET" | "POST" | "PUT")를 설정합니다.
두 번째 매개변수는 요청 URL입니다.
세 번째 매개변수는 비동기호출 여부입니다. False일 경우 동기호출하게 됩니다. - 실질적으로 서버에 요청을 하는 함수입니다. 매개변수는 open() 메서드의 첫 번째 매개변수를 "POST"로 설정한 후 사용합니다.
- 좀 더 자세한 사용법은 XMLHttpRequest를 찾아보세요.
콜백함수(상태 변화 처리)
function handleStateChange() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var result = xmlHttp.responseText; alert("The server replied with: " + result);
var response = xmlHttp.responseXML.documentElement; var name = response.getElementsByTagName('name')[0].firstChild.data; var age = response.getElementsByTagName('age')[0].firstChild.data; var method = response.getElementsByTagName('sayHello')[0].firstChild.data; alert("The hero's name is " + name); alert("His age is " + age);
var sayHello = eval('(' + method + ')'); sayHello(); } } } |
표 3 XMLHttpRequest 콜백처리
- readyState 프로퍼티 값
코드 | 상태 | 설명 |
0 | UNINITIALIZED | XMLHttpRequest 객체를 생성하였지만, 초기화되지 않았다. (open() 메서드 실행 전) |
1 | LOADING | XMLHttpRequest 객체를 생성하였고, open() 메서드를 수행하였지만 send() 메서드를 수행하지 않은 상태 |
2 | LOADED | Send()메서드를 수행하였지만, 서버가 처리를 준비하고 있는 상태 |
3 | INTERACTIVE | 처리를 완료하지 않았지만, 진행 중인 상태 |
4 | COMPLETED | 처리를 완료한 상태 |
표 4 XMLHttpRequest readyState 값
- status 값
Number | Description |
100 | Continue |
101 | Switching protocols |
200 | OK |
201 | Created |
202 | Accepted |
203 | Non-Authoritative Information |
204 | No Content |
205 | Reset Content |
206 | Partial Content |
300 | Multiple Choices |
301 | Moved Permanently |
302 | Found |
303 | See Other |
304 | Not Modified |
305 | Use Proxy |
307 | Temporary Redirect |
400 | Bad Request |
401 | Unauthorized |
402 | Payment Required |
403 | Forbidden |
404 | Not Found |
405 | Method Not Allowed |
406 | Not Acceptable |
407 | Proxy Authentication Required |
408 | Request Timeout |
409 | Conflict |
410 | Gone |
411 | Length Required |
412 | Precondition Failed |
413 | Request Entity Too Large |
414 | Request-URI Too Long |
415 | Unsupported Media Type |
416 | Requested Range Not Suitable |
417 | Expectation Failed |
500 | Internal Server Error |
501 | Not Implemented |
502 | Bad Gateway |
503 | Service Unavailable |
504 | Gateway Timeout |
505 | HTTP Version Not Supported |
표 5 XMLHttpRequest Status 값
JSON vs. XML
XML 응답의 파싱
Content-Type 응답헤더가 text/plain(기본값)으로 설정되어 있다면 일반 텍스트로도 응답을 받을 수있습니다. 하지만 text/xml일 경우에는 XML로 응답해야 합니다. XML로 응답이 왔을 경우에는 처리하기 위해W3C DOM스펙을 적용합니다. W3C DOM은 XML 문서의 검색과 처리를 위한 풍부한 API를 제공합니다.
표 3의 소스에서 다음 부분이 XML 응답을 파싱하여 처리하는 간단한 예를 보여줍니다.
var response = xmlHttp.responseXML.documentElement; var name = response.getElementsByTagName('name')[0].firstChild.data; var age = response.getElementsByTagName('age')[0].firstChild.data; var method = response.getElementsByTagName('sayHello')[0].firstChild.data; |
표 6 XML 응답 파싱
복잡한 데이터 구조를 갖는 응답일 경우 XML 응답 방식은 좋은 선택이 될 수 있습니다. 다만 응답 데이터의 양이 많을경우 추가적인 태그의 양도 증가하게 되므로 네트워크 트래픽을 증가시키는 요인이 됩니다. 또한 DOM 을 사용한 자료처리의 경우표 6과 같이 자료 탐색을 위한 추가적인 오버헤드가 발생하게 됩니다.
이런 문제에 대한 대안으로 JSON을 사용할 수 있습니다.
JSON 응답 처리
Content-Type 응답헤더가 text/plain일 경우 일반 텍스트로도 응답을 받을 수 있다고 언급하였습니다. 간단한데이터를 표현한 JSON문자열을 서버에 저장하고 있는 텍스트 파일을 요청하여 처리하는 예제를 살펴보겠습니다.
simpleJsonObject.txt
{name:'Batman', age: 16,sayHello: function(){alert('hello');}}
요청
function startRequestJSON() { createXMLHttpRequest(); xmlHttp.onreadystatechange = handleStateChangeJSON; xmlHttp.open("GET", "simpleJsonObject.txt", true); xmlHttp.send(null); }
응답 처리
function handleStateChangeJSON() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var result = xmlHttp.responseText; alert("The server replied with: " + result);
// eval var hero = eval('(' + result + ')');
alert("The hero's name is " + hero.name); alert("His age is " + hero.age);
hero.sayHello(); } } } |
응답처리 코드의 굵게 강조된 문장을 살펴보면 XML 응답에 비해 얼마나 간단하게 사용하고 있는지 비교하여 볼 수 있습니다.
간단 비교 시연
첨부한 AjaxPractice.zip 파일을 압축 풀고 시연해 봅니다. 단, ASP.NET 2.0 AJAX Extension 1.0이 설치되어 있어야 합니다. http://asp.net/ajax 사이트에서 다운로드 받을 수 있습니다.
ASP.NET에서 JSON
ASP.NET 2.0 AJAX 1.0 Extension?
Atlas라는 개발코드명으로 불리다가 2007년 1월 정식버전이 릴리즈되었습니다. 이와 관련하여 자세한 내용은 http://asp.net/ajax에서 구하실 수 있습니다.
비동기 통신 레이어 개요
그림 3 Client-Server Communication
그림 4 Client Architecture
클라이언트 측 비동기 통신 레이어 요소
- 메소드 프록시 객체 : Web Service, Page Method, Profile, Authentication
- 데이터 관련 객체 : XMLHttpExecutor, XMLHttp, JSON Serialization
- HTTP 객체 : WebRequest, WebRequestManager
그림 5 Server Architecture
서버 측 비동기 통신 레이어 요소
- 메소드 객체 : Web Services, Page Methods, Authentication Service, Profile Service
- 데이터 관련 객체 : XML Serialization, JSON Serialization
- HTTP 객체 : HTTP Handler
- 자료 출처
http://www.asp.net/ajax/documentation/live/overview/AsynchronousLayerOverview.aspx
http://nopd.egloos.com/3015900
AJAX 1.0 Extension에서 JSON ? – JSON Serialization
앞에서 살펴보았듯이 통신에 사용하는 데이터를 XML 포맷으로 사용할 경우 데이터의 크기가 문제될 수 있습니다. 이런 이유로JSON이 발표되었고 ASP.NET AJAX에서도 JSON을 사용할 수 있습니다. 특별히 지정하지 않는 한 ASP.NETAJAX에서는 JSON을 기본 응답 데이터 포맷으로 설정해 놓았습니다. XML을 사용하고 싶을 경우[ScripMethod(ResponseFormat.Xml)] 어트리트 뷰트를 웹메서드에 설정해 주는 것으로 변경할 수 있습니다.
객체와 JSON 변환
클라이언트 사이드
http://www.json.org 사이트에서 각 종 언어의JSON 라이브러리를 구할 수 있습니다. JavaScript를 위한 JSON 라이브러리(JSON Parser와 JSONstringifier)를 구하면 JSON 객체를 JSON 문자열로 변환하거나 JSON 문자열을 JSON 객체로 변환할 수있습니다. ASP.NET AJAX를 사용할 경우에는 Sys.Serialization.JavaScriptSerializer클라이언트 JS 클래스를 사용하여 JSON을 다룰 수 있습니다.
Web.Config
<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"/> <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35"/> <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" validate="false"/> </httpHandlers>
XXXX.aspx
<script type="text/javascript"> function serializeObject() { var value = {name : '홍길동', age : 20}; var result = Sys.Serialization.JavaScriptSerializer.serialize(value); alert(result); } </script>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> |
표 7 ASP.NET AJAX Client Side JSON Serialization
ASP.NET AJAX Extension 1.0을 설치한 후 Web.Config에서 ASP.NET AJAX 스크립트라이브러리 참조를 위한 설정을 먼저 해주어야 합니다. ASPX 페이지에서는 ScriptManager서버 컨트롤을 추가하여 서버에있는 ASP.NET AJAX 클라이언트 스크립트 라이브러리를 참조하는 코드를 추가합니다. JSON 객체를 JSON 문자열로변환(직렬화)할 때에는 JavaScriptSerializer 클래스의 serialize() 메서드를 사용합니다. 그 반대의 경우deserialize() 메서드를 사용합니다.
<scriptsrc="/WebResource.axd?d=0-r6URviUNdIIVyo8v9Q7w2&t=633313287393593750"type="text/javascript"></script>
<scriptsrc="/ScriptResource.axd?d=kqan03Rc1PFiEPlsw6EV-D0y4jZezoBSpY_chLkpAdEG2QE63pzdmIR38MxUcUz9eeso0gLrTCA2MrP7fA1MLGzTdtqzN8co_Asjxvt7Ml41&t=633323740192768809"type="text/javascript"></script> <scriptsrc="/ScriptResource.axd?d=kqan03Rc1PFiEPlsw6EV-D0y4jZezoBSpY_chLkpAdEG2QE63pzdmIR38MxUcUz9eeso0gLrTCA2MrP7fA1MLDkidbdjIemqOivqCjZ5M8E1&t=633323740192768809"type="text/javascript"></script> <div> <script type="text/javascript"> //<![CDATA[ Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1')); Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90); //]]> </script>
<script type="text/javascript"> //<![CDATA[ Sys.Application.initialize(); //]]> </script> |
표 8 ScriptManager에 의해 생성된 스크립트 코드
서버 사이드
ASP.NET의 서버 사이드 스크립트에서 일반 객체를 JSON 문자열로 직렬화하는 방법을 살펴보겠습니다.
simpleJson.htm
// 비동기 요청 function startRequestServerSideJson() { createXMLHttpRequest(); xmlHttp.onreadystatechange = handleStateChangeServerSideJson; xmlHttp.open("GET", "ServerSideJson.aspx?job=0", true); xmlHttp.send(null); }
// 응답 데이터 콜백 처리 function handleStateChangeServerSideJson() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var result = xmlHttp.responseText; alert("The server replied with: " + result);
// eval var hero = eval('(' + result + ')');
alert("The hero's name is " + hero.Name); alert("His age is " + hero.Age);
//alert("GetName() : " + hero.GetName());
} } }
ServerSideJson.aspx.cs
using System.Web.Script.Serialization;
// 변환 대상 클래스 public class Hero { public string Name; public int Age;
public string GetName() { return Name; } }
// 일반 객체를 JSON으로 변환 private void ConvertObjectToJson() { Hero hero = new Hero(); hero.Name = "홍길동"; hero.Age = 18;
JavaScriptSerializer jss = new JavaScriptSerializer(); string jsonHero = jss.Serialize(hero);
Response.ContentType = "text/plain"; Response.Write(jsonHero); Response.End(); } |
표 9 ASP.NET 객체의 JSON 직렬화
클라이언트에서는 XMLHttpRequest를 사용해서 일반적인 비동기 호출을 합니다. 이 때 서버 리소스는 ASPX페이지가 되며 이 ASPX 페이지의 비하인드 코드의 Page_Load() 이벤트에서 Hero 객체를 JSON문자열로 변환 후응답데이터를 작성, 응답을 종료합니다. 이렇게 되면 클라이언트의 응답 데이터 콜백 처리 스크립트에서 Hero 객체의 JSON문자열이 일반 텍스트로 전달되어 eval() 메서드를 사용하여 JSON 객체로 변환, 사용할 수 있게 됩니다.
DataSet 변환
마지막으로 DataSet을 JSON 문자열로 변환하여 클라이언트의 응답 데이터로 전달하는 방법을 살펴보겠습니다.ATLAS의 최종 프리뷰 릴리즈 버전에서는 DataSetConverter, DataTableConverter,DataRowConverter를 제공했지만 AJAX Extension 1.0에서는 이 부분이 제외되었습니다. 하지만 사용자 정의컨버터를 손쉽게 제작할 수 있도록 JavaScriptConverter기반 클래스를 제공합니다.JavaScriptConverter를 사용해서 DataSet을 JSON 문자열로 직렬화하고 이를 Client에 전달하여 사용하는방법을 알아봅니다.
ServerSideJson.aspx.cs
public class DataSetConverter : JavaScriptConverter { … public override System.Collections.Generic.IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Dictionary<string, object> dtDic = new Dictionary<string, object>();
DataSet ds = obj as DataSet;
foreach (DataTable dt in ds.Tables) { Dictionary<string, object> rowDic = new Dictionary<string, object>(); int i = 0; foreach (DataRow row in dt.Rows) { Dictionary<string, object> colDic = new Dictionary<string, object>(); foreach (DataColumn col in row.Table.Columns) { colDic.Add(col.ColumnName, row[col]); } rowDic.Add("row" + (i++).ToString(), colDic); }
dtDic.Add(dt.TableName, rowDic); }
return dtDic;
}
public override System.Collections.Generic.IEnumerable<Type> SupportedTypes { get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(DataSet) })); }
} }
// 데이터셋을 JSON으로 변환 private void ConvertDataSetToJson() { DataSet ds = new DataSet();
DataTable dt = new DataTable("Table"); dt.Columns.Add(new DataColumn("Name", Type.GetType("System.String"))); dt.Columns.Add(new DataColumn("Age", Type.GetType("System.Int32")));
DataRow row = dt.NewRow(); row["Name"] = "홍길동"; row["Age"] = 20;
dt.Rows.Add(row); ds.Tables.Add(dt);
JavaScriptSerializer jss = new JavaScriptSerializer(); jss.RegisterConverters(new JavaScriptConverter[] { new DataSetConverter() });
string jsonDs = jss.Serialize(ds);
Response.ContentType = "text/plain"; Response.Write(jsonDs); Response.End(); }
simpleJson.htm
function handleStateChangeServerSideJsonDataSet() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var result = xmlHttp.responseText; alert("The server replied with: " + result);
// eval var hero = eval('(' + result + ')');
alert("The hero's name is " + hero.Table.row0.Name); alert("His age is " + hero.Table.row0.Age); } } } |
표 10 ASP.NET AJAX DataSet을 JSON문자열로 직렬화
JavaScriptConverter 클래스를 상속하여 구현해야 하는 메서드는 Deserialize(),Serialize()와 SupportedTypes 프로퍼티입니다. 표 10에서는 Deserialize() 메서드 구현을 생략하고Serialize() 메서드 구현만을 다루었습니다. SuportedTypes 프로퍼티는 어떤 타입의 객체를 지원하는지 명시적으로알려주는 역할을 합니다. DataSet을 JSON 문자열로 직렬화할때에는 JavaScriptSerializer.RegisterConverters()메서드에 DataSetConverter를 지정하고 Serialize(ds) 메서드를 호출하여사용합니다. 이 후 사용법은 일반 객체 직렬화 방법과 같습니다. 클라이언트의 응답 데이터 콜백 함수에서는 eval()메서드를사용하여 JSON 객체로 변환한 후 hero.Table.row0.Name 방식으로 사용하면 됩니다.
끝.
덧글