AJAX, JSON and XML by ultteky




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 요청 보내기

  1. XMLHttpRequest 인스턴스를 생성합니다.
  2. XMLHttpRequest의 상태 변화를 처리하는 콜백함수를 설정합니다.
  3. Open() 메서드를 사용해서 요청 프로퍼티를 설정합니다.
    첫 번째 매개변수는 요청 메서드("GET" | "POST" | "PUT")를 설정합니다.
    두 번째 매개변수는 요청 URL입니다.
    세 번째 매개변수는 비동기호출 여부입니다. False일 경우 동기호출하게 됩니다.
  4. 실질적으로 서버에 요청을 하는 함수입니다. 매개변수는 open() 메서드의 첫 번째 매개변수를 "POST"로 설정한 후 사용합니다.
  5. 좀 더 자세한 사용법은 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 콜백처리

  1. readyState 프로퍼티 값

코드

상태

설명

0

UNINITIALIZED

XMLHttpRequest 객체를 생성하였지만, 초기화되지 않았다. (open() 메서드 실행 전)

1

LOADING

XMLHttpRequest 객체를 생성하였고, open() 메서드를 수행하였지만 send() 메서드를 수행하지 않은 상태

2

LOADED

Send()메서드를 수행하였지만, 서버가 처리를 준비하고 있는 상태

3

INTERACTIVE

처리를 완료하지 않았지만, 진행 중인 상태

4

COMPLETED

처리를 완료한 상태

표 4 XMLHttpRequest readyState 값

  1. 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

     

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&amp;t=633313287393593750"type="text/javascript"></script>

     

     

<scriptsrc="/ScriptResource.axd?d=kqan03Rc1PFiEPlsw6EV-D0y4jZezoBSpY_chLkpAdEG2QE63pzdmIR38MxUcUz9eeso0gLrTCA2MrP7fA1MLGzTdtqzN8co_Asjxvt7Ml41&amp;t=633323740192768809"type="text/javascript"></script>

<scriptsrc="/ScriptResource.axd?d=kqan03Rc1PFiEPlsw6EV-D0y4jZezoBSpY_chLkpAdEG2QE63pzdmIR38MxUcUz9eeso0gLrTCA2MrP7fA1MLDkidbdjIemqOivqCjZ5M8E1&amp;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 방식으로 사용하면 됩니다.

     

끝.


덧글

댓글 입력 영역