|
|
난이도 : 중급
Brett McLaughlin, Author and Editor, O'Reilly Media Inc.
2006 년 6 월 05 일 2006 년 12 월 12 일 수정
대부분의 웹 애플리케이션들은 서버에서 전체 HTML 페이지를 얻는 요청/응답 모델을 사용합니다. 다시 말해서, 이 모델은 버튼을 클릭하고, 서버를 기다리고, 또 다른 버튼을 클릭하고, 다시 기다리는 일이 다반사입니다. Ajax와 XMLHttpRequest 객체를 사용하면 서버 응답을 기다리지 않아도 되는 요청/응답 모델을 사용할 수 있습니다.
시리즈 소개
지난 글(참고자료)에서는 Ajax 애플리케이션에 관한 서론 및 이 애플리케이션에 필요한 몇 가지 기본개념에 대해 알아봤다. 본 글에서는 JavaScript, HTML 및 XHTML, 동적 HTML, 심지어는 몇 가지 DOM(동적 객체 모델) 등 이미 알고 있는 수많은 기술에 대해 중점적으로 다뤘다.
본 글에서는 모든 Ajax관련 객체 및 프로그래밍 방식의 기초인 XMLHttpRequest 객체에 대해 먼저 다룰 것이다. 이 객체는 모든 Ajax 애플리케이션 전반에 걸쳐 유일한 공통 줄기가 된다. 예상하다시피, XMLHttpRequest 객체를 완전히 이해해서 프로그래밍의 한계에 다다르고자 할 것이다. 사실, XMLHttpRequest 객체를 적절히 이용해도 분명 그 객체를 사용하지 못하는 경우가 있다. 도대체 XMLHttpRequest 객체는 무엇일까?
Web 2.0
먼저 코드에 관해 자세히 알아보기 전에 Web 2.0에 관한 개요를 살펴 보면서 확실한 개념을 얻도록 하자. Web 2.0이라는 용어를 들을 때 다음과 같이 " Web 1.0은 무엇입니까?" 라고 물어봐야 한다. Web 1.0에 대해선 거의 들어보지 못했지만 명료한 요청 및 응답 모델이 포함된 전통 웹을 가리켜 Web 1.0이라 한다. 예를 들어 Amazon.com으로 들어가서 버튼을 클릭하거나 탐색 용어를 입력하면 서버에 요청을 생성하고 이에 대한 응답이 브라우저로 다시 보낸다. 그 요청은 책, 타이틀 목록 이상으로 중요하며 실지로 또 다른 완전 HTML 페이지를 만들어낸다. 그 결과 새로운 HTML 페이지가 웹 브라우저 스크린에 다시 나타날 때 플래시/플리커링 현상이 나타나기도 한다. 사실, 각각의 새로운 페이지에서 나오는 요청, 응답을 분명히 알게 된다.
Web 2.0은 이와 같은 왕복이동 움직임이 상당부분 필요 없다. 예를 들어, Google Maps 또는 Flickr(참고자료)를 방문하면 Google Maps 상에서는 맵을 끌어다가 재 드로잉을 약간만 해도 맵이 축소, 확대된다. 물론, 요청 및 응답은 상상을 초월할 정도로 계속 이루어진다. 이로 인해 사용자로서의 경험은 훨씬 짜릿하며 데스크톱 애플리케이션 상에 있는 것과 같이 느껴진다. 이런 새로운 느낌, 패러다임은 누군가가 Web 2.0에 대해 언급할 때 나오는 현상들이다.
이와 같이 새로운 상호작용이 가능하도록 하는 방법에 대해 주의를 기울여야 한다. 분명 요청 및 필드 응답을 생성하지만 이로 인해 매 순간 요청/응답 상호작용에 관한 HTML 재 드로잉이 발생돼 느리고 볼품없는 웹 인터페이스를 생성하게 된다. 따라서 사용자가 요청을 생성하고 전반적인 HTML 페이지보다는 필요한 데이터만 포함하는 응답을 수신하는 방식이 필요하다. 사용자가 새로운 페이지를 보고자 할 때가 완전히 새로운 HTML을 얻는 유일한 경우다.
하지만 대부분의 상호작용으로 인해 상세사항 추가/본문 텍스트 변환/기존 페이지에 데이터 겹쳐쓰기 등이 발생한다. 모든 경우, Ajax 및 Web 2.0방식으로 전체 HTML 페이지를 업데이트하지 않고 데이터를 전송, 수신한다. 이런 기능으로 임의의 수많은 웹 서퍼들은 애플리케이션의 속도가 빨라지고 응답성이 증가하는 것으로 느끼게 되며 상호작용이 반복적으로 이루어지게 된다.
XMLHttpRequest
이렇게 새롭고 놀라운 현상이 실지로 발생하기 위해선 XMLHttpRequest 라 하는 JavaScript 객체에 관해 완전 익숙해져야 한다. 오랜 시간 동안 몇몇 브라우저에서 사용된 이 객체는 Web 2.0, Ajax 및 앞으로 이 글에서 배우게 될 기타 사항을 이해하는 데 있어 중요한 역할을 하게 된다. 실지로 빠른 이해를 위해 이 객체에서 사용되는 방법 및 속성에 대해 알아보자.
open() : 새로운 요청을 서버에 설정함.
send() : 요청을 서버에 전송함.
abort() : 현 요청에서 벗어남.
readyState : 현 HTML 준비상태를 제공함.
responseText : 요청에 응답하기 위해 서버에서 재전송하는 텍스트.
위의 모든 명령을 다 이해하지 못하더라도(중요한 것을 이해하지 못한다 하더라도) 걱정하지 마라. 다음 글에서 각 명령에 대한 방법 및 속성에 관해 배우게 된다. 여기서는 XMLHttpRequest 와 관련된 좋은 아이디어를 얻는 게 필요하다.여기서, 각 방법 및 속성은 요청 전송 및 응답 처리와 연관된다는 것을 명심하라. 사실, XMLHttpRequest 객체에 관한 방법, 속성을 다는 알지 못하기 때문에 이들이 매우 간단한 요청/응답 모델과 연관 있다는 것도 모르게 된다. 그래서 놀랍고도 새로운 GUI 객체, 또는 사용자 상호작용을 생성하는 흥미로운 몇 가지 방식 등에 관해서도 배우지 않는다. 별로 재미가 없는 것 같지만 XMLHttpRequest 객체 하나만 잘 사용해도 완전 애플리케이션을 변경할 수 있다.
단순함
우선, 새로운 변수를 생성한 다음 이를 XMLHttpRequest 객체 인스턴스에 할당한다. JavaScript 상에서는 상당히 간단한 작업이다. 여기서 Listing 1에 보다시피, 객체 이름과 같이 new 키워드를 사용하면 된다. Listing 1. 새로운 XMLHttpRequest 객체 형성
<script language="javascript" type="text/javascript">
var request = new XMLHttpRequest();
</script>
|
새로운 객체 형성과정이 그다지 어려운 일은 아니지 않는가? JavaScript에서는 변수 상에 입력하는 과정이 필요 없어 Listing 2(자바에서 XMLHttpRequest 객체를 생성하는 과정)와 같이 값을 전혀 입력할 필요가 없다. Listing 2. XMLHttpRequest 객체를 생성하기 위한 자바 유사-코드
XMLHttpRequest request = new XMLHttpRequest();
|
따라서 JavaScript에서 var 로 변수를 생성해 변수에 명칭("request" 등)을 부여한 다음 이를 XMLHttpRequest 객체의 새로운 인스턴스에 할당한다. 이 시점에서 XMLHttpRequest 객체를 사용할 준비가 된 것이다.
에러 처리과정
실제 세계에서, 에러가 발생할 수 있어 에러 발생코드는 에러 처리기능을 제공하지 않는다. 따라서 XMLHttpRequest 를 생성한 다음 에러가 발생한 경우 이 객체의 기능을 점차 저하시킨다. 일례로, XMLHttpRequest 객체를 지원하지 않는 구 브라우저들(믿건 말건, 사람들은 Netscape Navigator의 구 버전을 여전히 이용한다.)이 많아 사용자는 어디서 에러가 났는지 알 필요가 있다. Listing 3은 에러가 난 경우 XMLHttpRequest 객체를 생성하는 방식에 대해 나와 있다. 여기서 XMLHttpRequest 객체로 JavaScript 경고가 발생한다. Listing 3. 에러 처리기능으로 XMLHttpRequest 객체 생성
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (failed) {
request = false;
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>
|
여기서 다음의 각 단계를 반드시 이해한다.
request 라는 새 변수를 생성한 다음 이를 거짓으로 설정한다. XMLHttpRequest 객체가 아직 생성되지 않은 상태에서 거짓 값으로 설정한다.
- try/catch 블록에서 추가로 다음과 같은 작업을 한다.
XMLHttpRequest 객체를 시험한 다음 생성한다.
- 1번 과정이 실패한 경우(
catch (failed) ), request 가 여전히 거짓으로 설정되어 있는지 확인한다.
request 가 여전히 거짓으로 설정되어 있는지 확인한다. (에러가 없는 경우, 거짓으로 설정되지 않는다.)
- 에러가 발생한 경우(
request 가 거짓인 경우), JavaScript 경고를 사용해 문제가 발생했다는 사실을 사용자에게 알린다.
이런 작업은 상당히 간단하다. 실제로 대부분의 JavaScript 및 웹 개발자들은 객체를 읽고 작성하는 것보다는 이해하는 게 더 빠르다. 이제, XMLHttpRequest 객체를 생성하는 일부 에러-증명 코드가 생성되어 에러 여부를 알게 된다.
Microsoft로 처리하기
적어도 인터넷 상에서 이 코드를 적용하기 전까지는 이와 같은 작업이 무난하다. 이 코드를 적용하면 그림 1과 같이 에러가 나오게 된다. 그림 1. 에러 보고 인터넷
|
Microsoft가 잘 작동되는가? Ajax에 관해 쓴 글이 많고 Microsoft는 이 영역에 있어 점점 더 관심을 기울이고 있다. 사실 2006년 말에 출시될 것으로 예정된 Microsoft사의 Internet Explorer 최신버전인 버전 7.0은 XMLHttpRequest 객체를 직접 지원해 모든 Msxml2.XMLHTTP 생성코드 대신 new 키워드를 사용한다. 하지만 너무 빠져 들지 마라. 아직도 구 브라우저를 지원해야 하므로 크로스-브라우저 코드는 곧장 사라지지는 않을 전망이다. | |
분명, 에러가 발생하고 있다. Internet Explorer는 구식 브라우저가 아니며, 전 세계의 70% 정도가 사용하는 툴이다. 즉, Microsoft 및 Internet Explorer를 지원하지 않는 한 웹 상에서 잘 운영하지 못하게 된다. 따라서 Microsoft 브라우저를 다룰 다른 방식이 필요하다.
Microsoft는 Ajax를 지원하지만 이전과 다른 XMLHttpRequest 버전을 호출하며 사실은 여러 다른 버전을 호출한다. Internet Explorer의 새로운 버전을 사용하는 경우, Msxml2.XMLHTTP 라 하는 객체를 사용해야 한다. Internet Explorer의 구 버전에서는 Microsoft.XMLHTTP 객체를 사용한다. 그러므로 이와 같은 두 가지 객체 형태를 지원해야 한다. (비-Microsoft 브라우저에 대한 지원기능을 손실하지 않은 상태에서.) 이미 언급한 코드에 Microsoft 지원기능을 추가한 Listing 4를 참조하라. Listing 4. Microsoft 브라우저에 지원기능 추가
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
</script>
|
사실 브라우저에 대한 지원기능이 손실되기 쉽다. 따라서 다음과 같이 단계별로 하길 권한다.
request 명의 새 변수를 생성한 다음 이를 거짓으로 설정한다. XMLHttpRequest 객체가 아직 생성되지 않았다는 전제 하에 거짓으로 설정한다.
- try/catch 블록에서 추가로 다음과 같은 작업을 한다.
XMLHttpRequest 객체를 시험한 다음 생성한다.
- 1번 과정이 실패한 경우 (
catch (trymicrosoft) ):
- 새로운 Microsoft 버전 (
Msxml2.XMLHTTP )을 이용해 Microsoft 호환성 객체를 시험한 다음 생성한다.
- 전 과정이 실패한 경우(
catch (othermicrosoft) ), 이전 Microsoft 버전(Microsoft.XMLHTTP )을 이용해 Microsoft 호환성 객체를 시험한 다음 생성한다.
- 그래도 실패한 경우(
catch (failed) )에는, request 가 여전히 거짓으로 설정되어 있는지 확인한다.
request 가 여전히 거짓으로 설정되어 있는지 다시 확인한다. (에러가 나지 않는 경우 거짓으로 설정되지 않는다.)
- 그래도 문제가 발생한 경우(
request 가 거짓인 경우), JavaScript 경고를 사용해 사용자에게 문제가 발생했다는 것을 알린다.
코드를 변화시키고 Internet Explorer 상에서 다시 한 번 시도해 보면 에러 메시지 없이 생성한 형태를 보게 된다. 본인의 경우엔 그와 같은 시도가 그림 2와 같은 것으로 나타난다. 그림 2. 정상적으로 작동하는 Internet Explorer
동적/정적
Listing 1, 3, 4를 다시 보면 모든 코드는 script 태그 내에 직접 포함되어 있다는 것을 알게 된다. JavaScript가 그와 같이 코드화되고 메소드/기능 본체 내에 들어가지 않은 경우, 이를 정적 JavaScript라 한다. 이는 페이지가 스크린 상에 나타나기 전 코드를 실행했다는 것을 의미한다.(코드 및 브라우저가 따로 실행될 경우, 사양과는 100% 일치하지 않지만 사용자와 페이지가 상호작용하기 전, 코드를 실행했다는 건 확실하다.) 일반적으로 그렇게 해서 대부분의 Ajax 프로그래머가 XMLHttpRequest 객체를 생성한다.
즉, Listing 5처럼, 메소드에 이와 같은 코드를 첨가한다. Listing 5. XMLHttpRequest 생성코드를 메소드로 이동하기
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
</script>
|
이와 같이 코드를 설정하면, Ajax 작업을 하기 전 이와 같은 메소드를 호출해야 한다. 그럴 경우 Listing 6과 같은 것을 얻을 수도 있다. Listing 6. XMLHttpRequest 생성 메소드 사용
<script language="javascript" type="text/javascript">
var request;
function createRequest() {
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
}
function getCustomerInfo() {
createRequest();
// Do something with the request variable
}
</script>
|
Listing 6을 활용하면 에러 통지기능을 지연시키므로 대부분의 Ajax 프로그래머들은 위의 방법을 활용하지 않는다. 10/15 필드가 있는 복잡한 형태에 선택상자 등등이 있다고 상상해 보면 사용자가 필드 14에 있는 텍스트를 형식에 나온 대로 기입할 때 몇 가지 Ajax 코드를 전송한다. 이 시점에서 getCustomerInfo() 를 실행해 XMLHttpRequest 객체를 생성하려 했지만 실패한다. (이 예에서) 그러면 사용자에게 이 애플리케이션을 사용할 수 없다는 것을 경고로 알리게 된다 (많은 경우). 하지만 사용자는 이미 형식 상에서 데이터를 기입하느라 시간을 보냈다. 상당히 짜증을 내게 되면서 사용자는 결국엔 사이트로 관심을 기울이지 않게 된다.
정적 JavaScript를 사용하는 경우, 사용자는 페이지에 에러가 나자마자 에러를 포착하게 된다. 또 짜증나는가? 아마도 사용자로선 웹 애플리케이션이 브라우저 상에서 작동되지 않을 때 상당히 죽을 맛일 게다. 하지만, 10분 동안 정보를 기입한 뒤 동일한 에러가 나오는 것보다는 확실히 낫다. 따라서 정적으로 코드를 설정하고 난 다음 발생할 수 있는 문제에 대해 사용자가 조기에 알도록 하는 게 중요하다고 본다.
XMLHttpRequest로 요청 전송하기
요청 객체가 있으면 요청/응답 사이클을 시작한다. 여기서 요청을 생성한 다음 응답을 수신하는 게 XMLHttpRequest 객체에서 이루고자 하는 유일한 것임을 명심하라. 사용자 인터페이스 변환, 이미지 교환 및 서버에서 재전송하는 데이터 해석 등의 작업은 페이지에 있는 JavaScript, CSS 또는 기타 코드에서 일어나는 현상이다. XMLHttpRequest 객체가 사용 대기 중일 때 서버에 요청을 생성하게 된다.
샌드박스
Ajax는 샌드박스 보안 모델이 포함되어 있다. 그 결과 Ajax 코드(특히 XMLHttpRequest 객체)는 실행 중인 동일한 도메인에만 요청을 생성한다. 다음 글에서 보안 및 Ajax에 관해 더 많은 것을 배우게 되겠지만 지금으로선 로컬 머신 상에서 작동하는 코드만으로도 로컬 머신 상의 서버측 스크립트에 요청을 생성한다는 것을 알게 된다. www.breakneckpizza.com상에서 Ajax 코드를 실행하는 경우, www.breakneckpizza.com상에서 실행하는 스크립트에 관한 요청을 생성한다.
서버 URL 설정
여기서 우선 결정할 것은 연결할 서버의 URL이다. URL은 Ajax에서만 있는 것은 아니다. 분명URL을 구성하는 방법에 대해 알아야 한다. 하지만 URL은 연결 설정 시 여전히 필수적인 것이다. 대부분의 애플리케이션에서 사용자가 다루는 형식에서 나온 데이터와 정적 데이터 세트를 결합해 URL을 구성한다. 예를 들어 Listing 7에서는 전화번호 필드의 값을 알아내고 그 데이터를 이용해 URL을 구성하는 JavaScript에 관해 나와 있다. Listing 7. 요청 URL 구축
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
}
</script>
|
여기서 딴지를 걸만한 게 없다. 먼저 여기 나온 코드는 phone 이라는 이름의 새로운 변수를 생성, 이를 "phone"의 ID로 형식 필드 값을 지정한다. Listing 8에는 phone 필드 및 id 속성에서 알 수 있는 특수 형태에 관한 XHTML에 관해 나와 있다. Listing 8. Break Neck Pizza 형식
<body>
<p><img src="breakneck-logo_4c.gif" alt="Break Neck Pizza" /></p>
<form action="POST">
<p>Enter your phone number:
<input type="text" size="14" name="phone" id="phone"
onChange="getCustomerInfo();" />
</p>
<p>Your order will be delivered to:</p>
<div id="address"></div>
<p>Type your order in here:</p>
<p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>
<p><input type="submit" value="Order Pizza" id="submit" /></p>
</form>
</body>
|
사용자가 전화번호를 입력/변경하는 경우, Listing 8에서 보는 대로 getCustomerInfo() 메소드가 나온다는 사실을 알아둔다. 이 방법으로 전화번호를 알아낸 다음 url 변수에 저장된 URL 문자열을 구성하는 데 활용한다. Ajax 코드는 묶여져 있고 동일한 도메인에만 연결되기 때문에 URL에서 도메인 명칭이 필요 없다는 사실을 명심해야 한다. 이 예에서 스크립트 명칭은 /cgi-local/lookupCustomer.php 다. 결국 전화 번호는 상기 스트립트에 Get 매개변수로서 추가된다. ("phone=" + escape(phone) )
이전에 escape() 메소드를 알지 못한 경우, 이 메소드는 정확히 명백한 텍스트로 전송될 수 없는 문자로부터 벗어나는 데 사용된다. 예를 들어, 전화번호에서의 임의의 공간은 %20 문자로 바뀌며 이로 인해 URL과 같이 문자를 전송할 수 있게 된다.
그런 다음 필요한 많은 매개변수를 추가한다. 예를 들어 또 다른 매개변수를 추가하고자 한다면 URL 상에 추가해 여러 매개변수를 ampersand(& ) 문자로 분리시킨다. (첫 번째 매개변수는 의문부호(? )로 스크립트 명칭으로부터 분리되어 있다.)
요청 열기
|
open() 메소드가 열릴까? 인터넷 개발자들은 open() 메소드의 정확한 기능에 대해 서로 의견이 다르다. 실지로 이 메소드에 없는 기능은 요청 열기 기능이다. 네트워크 및 XHYML/Ajax 페이지와 연결 스크립트 간 데이터 이동을 감시했더라면 open() 메소드 호출 시 트래픽 현상이 발생하지도 않았을 것이다. Open() 명칭이 선택된 이유는 여전히 불분명하지만 분명 훌륭한 명칭선택이라 볼 수는 없다. | |
URL이 연결된 상태에서 XMLHttpRequest 객체 상의 open() 메소드를 사용해 요청을 구성한다. 이 메소드는 5가지 매개변수가 있다.
- request-type: 전송 요청 형태.
GET /POST 가 일반적인 값이고 HEAD 요청도 전송함.
- url: 연결된 URL.
- asynch: 비동기 요청을 설정할 경우 참값, 동기식 요청인 경우에는 거짓임. 이 매개변수는 옵션이고 기본값이 참값임.
- username: 사용인증을 요구할 경우 사용자이름을 지정한다. 옵션 매개변수고 기본값이 없다.
- password: 사용인증을 요구할 경우 암호를 지정한다, 옵션 매개변수고 기본값이 없다.
일반적으로 5개의 매개변수 중 3개의 첫 매개변수만 사용한다. 사실, 비동기식 요청을 원할 경우, 제3의 매개변수로 "true"을 설정한다. 그게 기본값 설정이다. 하지만 훌륭한 자체 문서화 작업으로 이 작업을 통해 항상 요청이 비동기식인지 아닌지 여부를 알 수 있다.
기본값 설정을 완료한 다음 일반적으로 Listing 9와 비슷한 라인으로 작업을 완료한다. Listing 9. 요청 열기
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
}
|
일단 URL을 이해했으면 그 다음에는 상당히 단순하다. 대부분의 요청의 경우, GET 을 사용하는 것만으로도 충분하다. (다음 글에서 POST 를 사용하고자 하는 경우를 보게 된다.) URL과 같이 open() 메소드를 사용하기만 하면 된다.
비동시성에 대한 문제
이 시리즈의 후반부에서는 비동기식 코드 작성 및 사용에 관한 설명에 할애할 것이다. 하지만 open() 메소드에서 마지막 매개변수가 중요한 이유에 대해 알아야 한다. 정상 요청/응답 모델에서 Web 1.0,을 생각해 보면 클라이언트(로컬 머신 상에서 실행하는 브라우저/코드)는 서버에 요청을 생성한다. 그 요청은 동기식이다. 다시 말하면 클라이언트는 서버로부터의 응답이 올 때까지 대기한다. 클라이언트가 대기 중일 때 일반적으로 적어도 대기 중인 여러 통지 형태 중 하나만 얻으면 된다.
- Hourglass (Windows 경우에만).
- 회전 비치볼(일반적으로 Mac 머신에서의 경우임).
- 애플리케이션은 기본적으로 정지되고 때로 커서가 변환되기도 한다.
이런 특성으로 웹 애플리케이션은 볼품없거나 느린 것으로 보여진다. 즉 실제 대화성이 부족한 것이다. 버튼을 누르면 트리거된 요청이 응답되기 전까지는 애플리케이션을 사용할 수 없다. 광범위한 서버 처리작업을 요구하는 요청을 생성할 경우 대기시간은 어마어마할 것이다. (적어도 오늘날 멀티 프로세서, DSL, 비대기 세계의 경우처럼.)
하지만 비동기식 요청은 서버가 응답할 때까지 대기하지 않는다. 요청을 전송한 다음에는 애플리케이션을 계속 실행한다. 사용자는 웹 형식에서 데이터를 기입한 다음 기타 버튼을 클릭하고 형식 기입을 종료한다. 회전하는 비치볼, 소용돌이치는 hourglass 및 대형 애플리케이션 정지 등의 현상이 생기지 않는다. 서버는 재빨리 요청에 응답하고 서버가 종료된 경우, 요청으로 인해 원 요청자는 서버가 종료되었음을 알게 된다. 결국 볼품없고 느린 대신 민감하고 대화성 있고 빠른 애플리케이션을 얻게 된다. 정확한 GUI 구성요소 및 웹 디자인 패러다임만 가지고는 느리고 동기적인 요청/응답 모델의 한계를 극복할 수 없다.
요청 전송
일단 open() 메소드로 요청을 구성하고 나면 요청 전송 준비를 한다. 다행히도, 요청을 전송하는 메소드를 open() 의 경우에 비해 더 적절하게 명명한다. 명칭은 단순히 send() 이다.
send() 메소드는 단 하나의 매개변수인 전송 컨텐트만 있으면 된다. 그 메소드에 대해 너무 깊게 생각하기 이전에 이미 URL 자체를 통해 데이터를 전송했음을 기억하라.
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
|
send() 메소드를 사용해 데이터를 전송하지만 URL자체를 통해서도 된다. 사실, GET 요청(일반 Ajax 이용률의 80%를 이루고 있음.)에서는 URL에서 데이터를 전송하는 게 더 용이하다. 안전한 정보/XML을 전송하기 시작한 경우, send() 메소드를 통한 전송 컨텐트에 대해 알아보려고 할 것이다. (이 시리즈 후반부에 안전한 데이터 및 XML 메시징에 대해 논의한다.) send() 를 통해 데이터를 전송하지 않아도 되는 경우, 이 메소드에 대한 인수로 null 을 전송하면 된다. 따라서 이 글을 통해 알게 된 예에서 보듯 요청 전송작업을 하는 게 필요하다.(Listing 10)
Listing 10. 요청 전송
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.send(null);
}
|
콜백 메소드 지정
이 시점에서, 새롭고 혁신적이거나 비동기적이라고 생각될 만한 작업을 거의 하지 못했다. open() 메소드의 키워드 "true"는 비동기식 요청을 설정한다고 하는 게 정확하다. 하지만 그거 말고도 open() 메소드 코드는 Java servlets및 JSP, PHP/Perl이 함께 어우러진 프로그래밍과 유사하다. 그렇다면 Ajax 및 Web 2.0에 담긴 커다란 비밀은 무엇일까? 그 비밀은 onreadystatechange 라는 명칭의 XMLHttpRequest 의 단순한 속성에서 나오게 된다.
먼저, open() 코드에서 생성된 과정에 대해 확실히 이해한다.(Listing 10) 요청을 설정하고 생성한다. 게다가 이 XMLHttpRequest는 동기식 요청이라 자바 메소드(예에 나온 getCustomerInfo() )는 서버 상에서 대기하지 않는다. open() 코드는 계속 진행되고 이 경우 자바 메소드는 정지되며 제어기능은 형태로 나오게 된다. 사용자들은 계속 정보를 입력하고 애플리케이션은 서버 상에서 대기하지 않는다.
이렇게 되면 재미있는 질문이 나오게 된다. 서버가 요청 처리 과정을 완료할 시 발생하는 현상은 어떤 것인가? 적어도 코드가 지금 당장 유지되는 한은 아무 현상도 없다 라는 말이 정답이다. 분명 좋은 현상은 아니다. 따라서 서버에 XXMLHttpRequest 객체에 의해 전송된 요청에 관한 처리과정을 완료할 경우 서버는 몇 가지 형태의 명령어를 포함해야 한다.
|
JavaScript에서의 기능 참조 JavaScript는 약결합 언어며 이 언어에서 모두 다 변수로 참조 가능하다. updatePage() 라는 이름의 함수를 선언한 경우, JavaScript는 그 함수 이름을 변수로 취급한다. 즉 updatePage() 라는 이름의 변수로 코드에 있는 함수를 참조한다. | |
이런 상황에서 바로 onreadystatechange 속성이 작용한다. 이 속성으로 콜백 메소드를 지정한다. 콜백 메소드로 서버는 웹 페이지 코드로 다시 호출한다. 그러면서 서버에 어느 정도의 제어 기능이 전달된다. 또한 서버에서 요청을 종료할 때 콜백 메소드는 XMLHttpRequest 객체, 특히 onreadystatechange 속성에서 나타난다. 그 속성에서 지정된 방법이 어떤 메소드든 모두 호출된다. 웹 페이지 자체에서 벌어지는 현상에 관계없이 웹 페이지로 다시 호출할 때 서버에서 개시하기 때문에 콜백이라 부르는 것이다. 예를 들어, 사용자가 의자에 앉아 키보드를 사용하지 않는 동안 콜백 메소드를 호출하기도 한다. 하지만 사용자가 입력하고 마우스를 움직이고, 화면 이동시키고 버튼을 클릭하는 동안에도 콜백 메소드를 호출하기도 한다. 사용자가 하는 업무는 그다지 중요하지 않다.
이런 상황에서 비동시성이 작용한다. 사용자는 다른 레벨에 있는 동안 한 레벨에 있는 형식을 작동하고 서버는 요청에 응답한 다음 onreadystatechange 속성에서 명시된 콜백 메소드를 전송한다. 따라서 Listing 11에 나온 대로 코드에 콜백 메소드를 지정해야 한다. Listing 11. 콜백 메소드 설정
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
|
특히 onreadystatechange 속성이 결정된 코드 위치에 주의를 기울인다. 그 위치는 바로 send() 가 호출되기 전의 위치다. 요청을 전송하기 전 onreadystatechange 속성을 설정해야 한다. 그래야만, 서버에서 요청 응답을 종료할 때 onreadystatechange 속성을 탐지하게 된다. 인제는 이 글의 마지막 부분에서 중점적으로 다룰 updatePage() 코드에 대해 알아보겠다.
서버 응답 처리
요청을 만들면 사용자는 웹 형식에서 여유롭게 작업하며 (서버에서 요청을 처리하는 동안에는) 서버는 요청 처리과정을 완료한다. 서버는 onreadystatechange 속성에서 나타나며 호출방법을 결정한다. 그런 일이 일어나면 비동기식/동기식 애플리케이션 등의 기타 다른 애플리케이션으로 애플리케이션을 생각할 수도 있다. 즉, 서버에 응답하는 특수 액션 작성 메소드를 취할 필요가 없다. 형식을 변환하고, 사용자를 또 다른 URL에 안내하거나 서버에 응답하는 데 필요한 것들을 하면 된다. 이 단락에서 우리는 서버 응답 및 이에 대한 일반조치 및 사용자가 아는 형식의 일부를 자유롭게 변경하는 것에 대해 중점적으로 다루겠다.
콜백 및 Ajax
이미 서버가 종료될 때의 현상을 서버가 인식하는 방법에 대해 이미 알았다. 일단 XMLHttpRequest 객체의 onreadystatechange 속성을 실행함수 이름에 설정한다. 그 다음 서버에서 요청을 처리하면 서버는 자동적으로 그 함수를 호출한다. 또한 콜백 메소드에 있는 임의의 매개변수에 대해 그리 걱정하지 않아도 된다. Listing 12와 같이 단순한 메소드에서 시작하기 때문이다. Listing 12. 콜백 메소드 코드화
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
if (!request)
alert("Error initializing XMLHttpRequest!");
function getCustomerInfo() {
var phone = document.getElementById("phone").value;
var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}
function updatePage() {
alert("Server is done!");
}
</script>
|
이렇게 하면 간단한 경고가 울리면서 서버가 종료될 때를 알려준다. 자체 페이지에 updatePage() 코드를 시험하고 페이지를 저장한 다음 브라우저에 페이지를 끌어올린다.(이 예에서 XHTML을 원할 경우, Listing 8을 참조한다.) 전화번호를 입력하고 필드를 설정하지 않을 경우 경고는 팝업되어야 한다. 하지만 확인을 클릭한 경우에도 경고는 팝업을 연속한다. 그림 3. 경고를 팝업하는 Ajax 코드
브라우저에 ‘따라 웹 형식에서 경고 팝업을 중지할 때까지 경고가 두 번, 세 번, 심지어는 네 번까지 울린다. 그러면 무슨 일이 벌어지고 있는 것인가? 요청/응답 사이클의 중요 구성요소인 HTTP 준비상태에 대해 고려하지 않았다.
HTTP 준비 상태
초기에 필자는 서버에서 요청이 종료되면 XMLHttpRequest 의 onreadystatechange 속성에서 호출되는 메소드를 탐지한다고 가르쳤다. 사실, HTTP 준비 상태가 변할 때마다 서버에서는 방금 전에 언급한 메소드를 호출한다. 그러면 그 말이 의미하는 것은 무엇인가? 일단 먼저 HTTP 준비상태에 관해 이해해야 한다.
HTTP 준비상태는 요청의 상태를 나타내며 주로 요청을 시작했는지, 요청에 응답했는지, 요청/응답 모델을 완성했는지 여부를 결정하는 데 활용된다. HTTP 준비상태는 서버에서 공급되는 모든 응답 텍스트/데이터를 읽어 들이는 데 안전한지 여부를 결정하는 데 도움이 되기도 한다. 여기서 Ajax 애플리케이션에서의 5가지 준비상태에 관해 알아야 한다.
- 0: 요청이 개시되지 않음.(
open() 을 호출하기 전)
- 1: 요청을 설정했지만 전송되지는 않았음.(
send() 를 호출하기 전)
- 2: 요청을 설정한 다음 처리 중(이 시점에서 일반적으로 응답에서 나온 컨텐트 헤더를 얻는다.)
- 3: 요청 처리 중; 종종 응답에서 부분적인 데이터를 사용할 수 있다. 하지만 서버는 자체 응답이 완료되지 않았다.
- 4: 응답 완료. 서버 응답을 얻은 다음 이를 활용한다.
거의 모든 크로스-브라우저 이슈에서도 그렇듯 예상치 못한 방식으로 이와 같은 준비 상태를 이용한다. 준비상태는 항상 0~1, 2, 3, 4로 단계적으로 이동한다고 예상할지도 모른다. 하지만 실지로는 그렇지 않다. 0/1상태를 보고하지 않고 곧바로 2로 건너뛰어 3,4까지 가는 브라우저도 있고 모든 상태를 보고하는 브라우저도 있다. 지난 단락에서 보듯, 서버에서는 몇 번이고 updatePage() 코드를 호출하고 호출 때마다 경고 상자가 팝업된다. 그건 여러분이 의도하는 바가 아닐 것이다!
Ajax 프로그래밍의 경우, 직접 다뤄야 할 상태는 오로지 상태 4다. 이는 서버 응답이 완료되었고 응답 데이터를 점검, 사용하는 데 안전하다는 것을 의미한다. 이를 설명하기 위해 콜백 메소드에 나온 첫 번째 라인은 Listing 13에서 나온 바여야 한다. Listing 13. 준비상태 점검
function updatePage() {
if (request.readyState == 4)
alert("Server is done!");
}
|
이런 변환으로 서버가 정말로 그 과정을 종료했는지 확인한다. Ajax 코드의 이 버전을 실행한다. 그러면 한 번에 경고 메시지만을 얻어야 한다.
HTTP 상태 코드
Listing 13에서의 코드의 성공에도 불구하고 여전히 문제는 상존한다. 그러면 서버가 요청에 응답하고 요청 처리과정을 완료했지만 에러를 보고한 경우는 어찌 되는가? Ajax, JSP, 정규 HTML 형식 또는 기타 형태의 코드로 서버측 코드를 호출 중인 경우에 서버측 코드를 관찰해야 한다는 점을 주목한다. 웹 세계에서는 HTTP 코드로 요청에서 발생할지도 모르는 여러 가지 상황을 다룬다.
예를 들어, URL에 관한 요청을 입력했지만, URL을 부정확하게 입력해 404 에러코드가 나와 페이지가 없어졌다고 해보자. 이 코드는 HTTP 요청을 상태로 수신하는 여러 상태 코드 가운데 하나에 지나지 않는다.(참고자료) 403, 401 코드는 둘 다 안전하거나 금지된 데이터 처리를 의미하는 것으로 역시 공통적이다. 각 경우에 있어 이런 코드들은 완전 응답에서 나오는 코드들이다. 즉, 서버는 요청을 수행하지만(HTTP 준비상태는 4임), 클라이언트가 예상한 데이터가 나오지 않을 수도 있다.
여기서 준비 상태에 덧붙여, HTTP 상태를 점검할 필요가 있다. 단순히 확인을 의미하는 상태코드 200을 탐색하는 중에 있다. 준비상태 4와 상태코드 200인 상태에서 서버 데이터를 처리할 준비가 되어 있고 그 데이터는 반드시 요청된 형태여야 한다. (에러 또는 문제가 있는 정보 단편이 아님.) Listing 14에서 보듯이 콜백 메소드에 또 다른 상태 점검기능을 추가한다. Listing 14. HTTP 상태 코드 점검
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
}
|
복잡성을 줄이고 더 강력한 에러 처리기능을 추가하려면 기타 상태코드에 관한 점검기능/두 가지 기능을 추가할지도 모른다. Listing 15에 있는 updatePage() 의 수정 버전을 점검한다. Listing 15. 간단한 에러 점검기능 추가
function updatePage() {
if (request.readyState == 4)
if (request.status == 200)
alert("Server is done!");
else if (request.status == 404)
alert("Request URL does not exist");
else
alert("Error: status code is " + request.status);
}
|
이제 getCustomerInfo() 에 있는 URL을 비실제 URL로 변환시킨 다음 일어나는 현상을 보면 요청한 URL은 존재하지 않는다는 의미의 경고가 울린다. 이런 경고 가지고도 모든 에러상태를 거의 처리하지 않는다. 하지만 웹 애플리케이션에서 발생할 수 있는 문제의 80%는 해결하는 단순한 진전이 아닐 수 없다.
응답 텍스트 읽기
인제 요청을 준비상태를 통해 완전히 처리하고, 서버로 정상적인 확인 응답을 상태 코드를 통해 받았으므로 서버에서 재전송되는 데이터를 최종적으로 처리한다. 이 데이터는 XMLHttpRequest 객체의 responseText 속성에 저장된다.
포맷/길이에 의한 responseText 속성의 텍스트 모양에 관한 상세사항은 이 장에서는 논하지 않기로 한다. 이렇게 되면 서버는 이 텍스트를 실지로 임의로 설정한다. 예를 들어, 한 스크립트로 콤마-분리 값 및 파이프-분리 값이 나오고 또 다른 파이프-분리 값은 텍스트의 긴 문자열로 나올 수도 있다. 이런 현상은 서버에 따라 다르게 된다.
이 글에서 사용된 예의 경우, 서버는 파이프 기호로 분리된 고객의 마지막 순서 및 주소가 나오게 된다. 형식의 구성요소 값을 설정하는 데 순서 및 고객의 마지막 순서 및 주소를 활용한다. Listing 16은 디스플레이를 업데이트하는 코드에 대해 나와 있다. Listing 16. 서버 응답 처리
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
} else
alert("status is " + request.status);
}
}
|
우선 JavaScript split() 메소드를 이용해 파이프 기호 상에서 responseText 를 얻고 분할한다. 값은 response 의 형태로 배열된다. 고객의 마지막 순서에 관한 첫 번째 값은 response[0] 형태의 배열로 처리되고 "순서" ID와 함께 필드 값으로 설정된다. response[1] 에서 두 번째 값은 고객 주소로 처리하는 데 좀 더 오랜 시간이 걸린다. 주소 라인은 정상 라인 분리자("\n" 문자) 로 분리되기 때문에 코드는 정상라인 분리자를 XHTML-형 라인 분리자(<br /> )로 바꾸어야 한다. 정규 식 및 replace() 함수의 활용을 통해 분리자를 바꾸는 과정이 이루어진다. 결국 변경 텍스트는 HTML 형태에서 div 의 내부 HTML로 설정된다. 결국 그림 4에도 나오듯이 텍스트 형식은 순식간에 고객정보로 업데이트된다. 그림 4. 고객 데이터 검색 후의 Break Neck 형식
이 글을 마치기 전에 XMLHttpRequest 객체의 중요한 속성 중 하나인 responseXML 속성에 대해 언급한다. 이 속성은 서버가 XML과의 응답을 선택한 경우, XML 응답을 포함한다. (상상이 되는가?) XML 응답 처리는 평범한 텍스트 처리과정과 상당히 다르며, 문장분석 및 문서 객체 모델(DOM)을 포함한다. 다음 글에서는 XML에 대해 다루게 된다. responseXML 은 공통적으로 responseText 과 관련된 논의에서 나오기 때문에 언급할 가치가 있는 것이다. 많은 단순 Ajax 애플리케이션의 경우, responseText 만 있으면 된다. 하지만 Ajax 애플리케이션을 통해 XML을 처리하는 방법에 대해서도 곧 배우게 된다.
맺음말
XMLHttpRequest 객체에 대해서는 인제 좀 지루하게 들릴지도 모른다. 필자는 단일 객체, 특히 간단한 객체에 대한 전반적인 글을 거의 읽지 못했다. 하지만 Ajax를 사용하고 작성하는 각 페이지 및 애플리케이션에서 계속 XMLHttpRequest 객체를 사용하게 된다. 아직도 XMLHttpRequest 객체에 대해 언급되지 않은 것들이 많은 건 사실이다. 다음 글에서는 요청에서 GET 및 POST 를 사용하고 서버로부터의 응답 및 요청의 컨텐트 헤더를 설정하고 읽어들이는 방법을 배운다. 그러면 요청을 코드화하고 심지어는 요청/응답 모델에서 XML을 다루는 방법을 배우게 될 것이다.
좀 더 상세하게 나가면 일반적으로 사용하는 Ajax 툴킷에 관해서도 알게 된다. 이 툴킷은 본 글에서 논의된 상세사항의 대부분을 실지로 요약한 것이다. 한편 툴킷을 손쉽게 이용하는 경우, 낮은 레벨의 상세사항을 코드화하는 이유에 대해 궁금해할 수도 있다. 사실은 애플리케이션 상에서 발생하는 현상을 이해하지 못하는 경우, 애플리케이션에서 일어나는 에러를 이해하는 게 어려워진다.
따라서, 이와 같은 사항을 간과하거나 지나치면 안 된다. 가변성 툴킷에서 에러가 발생할 경우, 머리를 끄적이면서 e-메일을 보내지 않아도 된다. 직접 XMLHttpRequest 사용법을 이해하면 가장 이상한 문제를 디버그하고 수정하는 것도 쉬워진다. 툴킷에 집중하면서 모든 문제를 해결하지 않는 한 툴킷은 그런대로 괜찮다.
따라서, XMLHttpRequest 객체에 대해 친숙해져라. 사실, 툴킷을 사용하는 Ajax 코드를 실행할 경우 XMLHttpRequest 객체 및 속성, 메소드를 사용해Ajax 코드를 재작성한다. 상당히 좋은 연습이 될 것이며 현재 이 객체에서 벌어지는 현상에 대해 더 잘 이해하게 될 것이다.
다음 글에서는 XMLHttpRequest 에 대해 좀 더 자세하게 들어간다. 이 객체에서 어려운 속성(responseXML 등의), POST 요청 사용법 및 몇 가지 다른 포맷에서의 데이터를 전송하는 방법을 조사할 것이다. 한 달 동안 코드화 작업을 시작해 코드를 다시 점검한다.
기사의 원문보기
참고자료 교육
제품 및 기술 얻기
- Head Rush Ajax by Elisabeth Freeman, Eric Freeman, and Brett McLaughlin (February 2006, O'Reilly Media, Inc.)
- Java and XML , Second Edition by Brett McLaughlin (August 2001, O'Reilly Media, Inc.)
- JavaScript: The Definitive Guide by David Flanagan (November 2001, O'Reilly Media, Inc.)
- Head First HTML with CSS & XHTML by Elizabeth and Eric Freeman (December 2005, O'Reilly Media, Inc.)
토론
필자소개
|
|
|
Brett McLaughin은 Logo 시절부터 컴퓨터 업계에서 일했다 (작은 트라이앵글 기억하는가?). 최근에 그는 Java 및 XML 커뮤니티에서 인기 저자 및 프로그래머가 되었다. Nextel Communication사에서는 복잡한 기업 시스템 실행에 관한 업무, Lutris Technologies 사에선 애플리케이션 서버를 실지로 작성하는 업무, 최근 O’Reilly Media 사에서는 이와 관련된 중요한 책을 저술, 편집했다. Brett의 최근 저서인 Head Rush Ajax는 Ajax에 관한 혁신적인 연구에 기여, 공동저자인 Eric 및 Beth Freeman과 함께 공동으로 수상했다. 그의 최근 저서인 Java 1.5 Tiger: A Developer's Notebook은 신 자바 기술 버전 상에서 이용 가능한 첫 번째 저서다. Brett의 최신 Java 및 XML은 자바언어에서 XML 기술을 활용한 명백한 업적 중 하나로 남아 있다. | |