7. Opensource Web Grid

1) 대략 javascript아니면 DHTML [javascript라면 이것도 OK]
http://www.activewidgets.com/documentation/tutorial/grid/data-jsarray.htm

하지만 이렇게 작성도 가능함 [tag lib version]
http://www.activewidgets.com/messages/1694-8.htm

2) http://www.codeproject.com/useritems/DBGrid.asp
순수 jsp타입에서 접근하기엔 이게 좋아보인다.


2-2) sand box에서는 이런식으로 component화 되어있음 tablib임.
http://jakarta.apache.org/taglibs/sandbox/doc/datagrid-doc/index.html

by 미소 | 2008/01/01 05:15 | 웹 개발 | 트랙백

1. jQuery로 Ajax 개발을 단순화 하기

출처 http://www.ibm.com/developerworks/kr/library/x-ajaxjquery.html
난이도 : 중급

Jesse Skinner, Web Developer, Freelance
2007 년 9 월 04 일
jQuery는 JavaScript 라이브러리로서 JavaScript™와 Asynchronous JavaScript + XML (Ajax) 프로그래밍을 단순화 하는데 도움이 됩니다. JavaScript 라이브러리와는 달리, jQuery는 복잡한 코드를 간결하게 표현할 수 있는 독특한 철학이 있습니다. jQuery 원리, 기능과 특징을 배우고, 일반적인 Ajax 태스크도 수행하며, 플러그인으로 jQuery를 확장하는 방법도 알아봅니다.

jQuery란 무엇인가?
2006년 초, John Resig가 만든 jQuery는 JavaScript 코드로 작업하는 사람들에게는 훌륭한 라이브러리이다. 여러분이 JavaScript 언어 초보자라서 라이브러리가 Document Object Model (DOM) 스크립팅과 Ajax의 복잡성을 다루어주기를 원하든지, 숙련된 JavaScript 구루로서 DOM 스크립팅과 Ajax의 반복성에 지루해졌다면, jQuery가 제격이다.
jQuery는 코드를 단순하고 간결하게 유지한다. 많은 반복 루프와 DOM 스크립팅 라이브러리 호출을 작성할 필요가 없다. jQuery를 사용하면 매우 적은 문자로 표현할 수 있다.
jQuery 철학은 매우 독특하다. 무엇이든 단순하고 재사용 가능한 것으로 유지하기 위해 디자인 되었다. 여러분이 이러한 철학을 이해하고 여기에 편안함을 느낀다면, jQuery가 여러분의 프로그래밍 방식을 충분히 향상시킬 수 있다.
단순화
다음은 jQuery가 여러분의 코드에 어떤 영향을 미치는지를 보여주는 예제이다. 페이지의 모든 링크에 클릭 이벤트를 붙이는 것 같이 단순하고 일반적인 것을 수행하려면, 플레인 JavaScript 코드와 DOM 스크립팅을 사용하는 것이 낫다. (Listing 1)

Listing 1. jQuery 없는 DOM 스크립팅
var external_links = document.getElementById('external_links');
var links = external_links.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
var link = links.item(i);
link.onclick = function() {
return confirm('You are going to visit: ' + this.href);
};
}


Listing 2는 같은 기능에 jQuery를 사용한 모습이다.

Listing 2. jQuery를 사용한 DOM 스크립팅
$('#external_links a').click(function() {
return confirm('You are going to visit: ' + this.href);
});


놀랍지 않은가? jQuery를 사용하면 복잡하지 않게 코드로 표현하고자 하는 것만 나타낼 수 있다. 엘리먼트를 반복할 필요가 없다. click() 함수가 이 모든 것을 관리한다. 또한, 다중 DOM 스크립팅 호출도 필요 없다. 여기에서 필요한 것은 엘리먼트가 어떻게 작동하는지를 설명하는 짧은 스트링이다.
이 코드로 어떻게 작업이 수행되는지를 이해하기는 조금 어렵다. 우선, $() 함수가 있어야 한다. 이것은 jQuery에서 가장 강력한 함수이다. 대게, 이 함수를 사용하여 문서에서 엘리먼트를 선택한다. 이 예제에서, 이 함수는 Cascading Style Sheets (CSS) 신택스를 포함하고 있는 스트링으로 전달되고, jQuery는 효율적으로 이 엘리먼트를 찾는다.
CSS 셀렉터의 기본을 이해하고 있다면, 이 신택스가 익숙할 것이다. Listing 2에서, #external_linksexternal_linksid를 가진 엘리먼트를 찾는다. a 앞에 있는 공간은 jQuery에게 external_links 엘리먼트 내의 모든 <a> 엘리먼트를 찾도록 명령한다. 영어와 DOM은 장황하지만, CSS에서는 매우 간단하다.
$() 함수는 CSS 셀렉터와 매치하는 모든 엘리먼트를 포함하고 있는 jQuery 객체를 리턴한다. jQuery 객체는 어레이와 비슷하지만, 수 많은 특별한 jQuery 함수들이 포함된다. 예를 들어, click 함수를 호출함으로써 클릭 핸들러 함수를 jQuery 객체의 각 엘리먼트에 할당할 수 있다.
또한, 엘리먼트나 엘리먼트의 어레이를 $() 함수로 전달하면, 이것은 엘리먼트 주위에 jQuery 객체를 래핑할 것이다. 이 기능을 사용하여 window 객체 같은 것에 jQuery 함수를 적용하고 싶을 것이다. 일반적으로 이 함수를 다음과 같이 로드 이벤트에 할당한다.
window.onload = function() {
// do this stuff when the page is done loading
};


jQuery를 사용하면, 같은 코드도 다음과 같이 된다.
$(window).load(function() {
// run this when the whole page has been downloaded
});


이미 알고 있었겠지만, 윈도우가 로딩하기를 기다리는 일은 매우 지루한 일이다. 전체 페이지가 로딩을 끝마쳐야 하기 때문이다. 여기에는 페이지의 모든 이미지들도 포함된다. 가끔, 이미지 로딩을 먼저 끝내고 싶지만, 대부분의 경우 Hypertext Markup Language (HTML)만 로딩해야 한다. jQuery는 문서에 특별한 ready 이벤트를 만듦으로써 이 문제를 해결한다.
$(document).ready(function() {
// do this stuff when the HTML is all ready
});


이 코드는 document 엘리먼트 주위에 jQuery 객체를 만들고, HTML DOM 문서가 준비될 때 함수를 설정하여 인스턴스를 호출한다. 이 함수를 필요한 만큼 호출할 수 있다. 진정한 jQuery 스타일에서, 지름길은 이 함수를 호출하는 것이다. 함수를 $() 함수로 전달한다.
$(function() {
// run this when the HTML is done downloading
});


지금까지, $() 함수를 사용하는 세 가지 방법을 설명했다. 네 번째 방법은, 스트링을 사용하여 엘리먼트를 만드는 것이다. 결과는, 그 엘리먼트를 포함하고 있는 jQuery 객체가 된다. Listing 3은 문단을 페이지에 추가하는 예제이다.

Listing 3. 간단한 문단을 생성하여 붙이기
$('<p></p>')
.html('Hey World!')
.css('background', 'yellow')
.appendTo("body");

이전 예제에서 파악했겠지만, jQuery의 또 다른 강력한 기능은 메소드 체인(method chaining.)이다. jQuery 객체에 대해 메소드를 호출할 때마다, 이 메소드는 같은 jQuery 객체를 리턴한다. jQuery 객체에 다중 메소드를 호출하고 싶다면 셀렉터를 다시 입력하지 않고 이를 수행할 수 있다.
$('#message').css('background', 'yellow').html('Hello!').show();


Ajax로 단순하게!
Ajax는 jQuery를 사용하면 더 단순해 질 수 있다. jQuery에는 쉬운 것도 쉽게 복잡한 것도 가능한 단순하게 만드는 유용한 함수들이 많이 있다.

Ajax에서 사용되는 방식은 HTML 청크를 페이지 영역에 로딩하는 것이다. 여러분이 필요로 하는 엘리먼트를 선택하고 load() 함수를 사용하는 것이다. 다음은 통계를 업데이트 하는 예제이다.
$('#stats').load('stats.html');



일부 매개변수들을 서버 상의 페이지로 전달해야 할 경우가 종종 있다. jQuery를 사용하면 이는 매우 간단하다. 필요한 메소드가 어떤 것인지에 따라서 $.post()$.get() 중 선택한다. 선택적 데이터 객체와 콜백 함수를 전달할 수도 있다. Listing 4는 데이터를 보내고 콜백을 사용하는 예제이다.

Listing 4. Ajax를 사용하여 데이터를 페이지로 보내기
$.post('save.cgi', {
text: 'my string',
number: 23
}, function() {
alert('Your data has been saved.');
});


복잡한 Ajax 스크립팅을 해야 한다면, $.ajax() 함수가 필요하다. xml, html,
script, json을 지정할 수 있고, 여러분이 바로 사용할 수 있도록 jQuery가 자동으로 콜백 함수에 대한 결과를 준비한다. 또한, beforeSend,
error, success, complete 콜백을 지정하여 사용자에게 Ajax에 대한 더 많은 피드백을 제공할 수 있다. 게다가, Ajax 요청의 타임아웃이나 페이지의 "최종 변경" 상태를 설정하는 매개변수들도 있다. Listing 5는 필자가 언급했던 매개변수를 사용하여 XML 문서를 검색하는 예제이다.

Listing 5. $.ajax()를 사용하여 복잡한 Ajax를 단순하게
$.ajax({
url: 'document.xml',
type: 'GET',
dataType: 'xml',
timeout: 1000,
error: function(){
alert('Error loading XML document');
},
success: function(xml){
// do something with xml
}
});



콜백 성공으로 XML을 받으면, jQuery를 사용하여 HTML에서 했던 것과 같은 방식으로 XML을 볼 수 있다. 이는 XML 문서 작업을 쉽게 하며 콘텐트와 데이터를 웹 사이트로 쉽게 통합시킨다. Listing 6은 리스트 아이템을 XML의 <item> 엘리먼트용 웹 페이지에 추가하는 success 함수에 대한 확장 모습이다.

Listing 6. jQuery를 사용하여 XML 작업하기
success: function(xml){
$(xml).find('item').each(function(){
var item_text = $(this).text();

$('<li></li>')
.html(item_text)
.appendTo('ol');
});
}


HTML 애니메이션

jQuery를 사용하여 기본적인 애니메이션과 효과를 다룰 수 있다. 애니메이션 코드의 중심에는 animate() 함수가 있는데, 이는 숫자로 된 CSS 스타일 값을 바꾼다. 예를 들어, 높이, 넓이, 폭, 위치를 움직일 수 있다. 또한, 애니메이션의 속도를 밀리초 또는 사전 정의된 속도(느림, 보통, 빠름)로 지정할 수 있다.
다음은, 엘리먼트의 높이와 넓이를 동시에 움직이게 하는 예제이다. 시작 값은 없고 종료 값만 있다. 시작 값은 엘리먼트의 현재 크기에서 가져온다. 여기에도 콜백 함수를 첨부했다.
$('#grow').animate({ height: 500, width: 500 }, "slow", function(){
alert('The element is done growing!');
});


jQuery는 빌트인 함수를 사용하여 일반적인 애니메이션도 더욱 쉽게 만든다. show()hide() 엘리먼트를 즉각적으로 또는 지정된 속도로 사용할 수 있다. fadeIn()fadeOut() 또는 slideDown()slideUp()을 사용하여 엘리먼트를 나타나게 하거나 사라지게 할 수 있다. 다음은 네비게이션의 slidedown 예제이다.
$('#nav').slideDown('slow');


DOM 스크립팅과 이벤트 핸들링
jQuery는 DOM 스크립팅과 이벤트 핸들링을 단순화하는데 제격이다. DOM의 트래버스와 조작이 쉽고, 이벤트의 첨부, 제거, 호출은 매우 자연스러운 일이며, 직접 수행하는 것보다 에러도 적게 발생한다.

기본적으로 jQuery는 DOM 스크립팅으로 수행하는 일들을 더욱 쉽게 수행할 수 있도록 해준다. 엘리먼트를 생성하고 append() 함수를 사용하여 이들을 다른 엘리먼트로 연결할 수 있고, clone()을 사용하여 엘리먼트를 중복시키고, 콘텐트를 html()로 설정하고, empty() 함수로 콘텐트를 삭제하고, remove() 함수로 엘리먼트를 삭제하고, wrap() 함수를 사용하여 또 다른 엘리먼트로 엘리먼트를 래핑한다.

DOM을 트래버스 함으로써 jQuery 객체의 콘텐트를 변경할 때 여러 함수들을 사용할 수 있다. 엘리먼트의 siblings(),
parents(), children()을 사용할 수 있다. 또한, next() 또는 prev() sibling 엘리먼트도 선택할 수 있다. 아마도 가장 강력한 것은 find() 함수일 것이다. jQuery 셀렉터를 사용하여 jQuery 객체의 엘리먼트 종속 관계들을 통해 검색할 수 있다.
이 함수는 end() 함수와 사용될 때 더욱 강력해진다. 이 함수는 실행 취소 함수와 비슷하고, find() 또는 parents() 또는 다른 트래버싱 함수들을 호출하기 전에 가졌던 jQuery 객체로 돌아간다.
메소드 체인과 함께 사용되면, 복잡한 연산도 단순하게 보이게 할 수 있다. Listing 7은 로그인 폼을 찾고 이와 관련한 여러 엘리먼트를 조작하는 예제이다.

Listing 7. DOM의 트래버스와 조작
$('form#login')
// hide all the labels inside the form with the 'optional' class
.find('label.optional').hide().end()

// add a red border to any password fields in the form
.find('input:password').css('border', '1px solid red').end()

// add a submit handler to the form
.submit(function(){
return confirm('Are you sure you want to submit?');
});



믿을 수 있는지 모르겠지만, 이 예제는, 공백을 사용한 하나의 연결된 코드 라인일 뿐이다. 우선, 로그인 폼을 선택했다. 그리고 나서, 이 안에 선택 레이블을 찾고, 이들을 숨긴 다음, end()를 호출하여 폼으로 돌아가게 하였다. 패스워드 필드를 찾았고, 보더를 빨간색으로 한 다음, 다시 end()를 호출하여 폼으로 돌아갔다. 마지막으로, 제출 이벤트 핸들러를 폼에 추가했다. 여기에서 특히 재미있는 부분은 jQuery가 모든 쿼리 연산들을 최적화 하기 때문에, 여러분은 모든 것이 서로 잘 연결될 때 엘리먼트를 두 번 찾을 필요가 없다.
공통 이벤트 핸들링은 click(),
submit(), mouseover() 같은 함수를 호출하고 여기에 이벤트 핸들러 함수를 전달하는 것만큼 단순하다. 게다가, bind('eventname', function(){})을 사용하여 커스텀 이벤트 핸들러를 할당하는 옵션도 있다. unbind('eventname')를 사용하여 특정 이벤트를 제거하거나, unbind()를 사용하여 모든 이벤트를 제거할 수 있다. 이것과 기타 함수들을 사용하는 다른 방법들은, jQuery 애플리케이션 프로그램 인터페이스(API) 문서를 참조하라. (참고자료)
jQuery 셀렉터의 힘 활용하기

#myid 같은 아이디 또는 div.myclass 같은 클래스 이름으로 엘리먼트를 선택한다. 하지만, jQuery는 하나의 셀렉터에서 거의 모든 엘리먼트 조합을 선택할 수 있도록 하는 복잡하고도 완벽한 셀렉터 신택스를 갖고 있다.

jQuery의 셀렉터 신택스는 CSS3과 XPath에 기반하고 있다. CSS3과 XPath 신택스를 더욱 잘 안다면, jQuery 사용이 더욱 수월해진다. CSS와 XPath를 포함하여 jQuery 셀렉터의 전체 리스트를 보려면 참고자료 섹션을 참조하라.

CSS3에는 모든 브라우저가 지원하지 않는 신택스가 포함되어 있기 때문에, 이를 자주 볼 수 없다. 하지만, jQuery에서 CSS3을 사용하여 엘리먼트를 선택한다. jQuery는 고유의 커스텀 셀렉터 엔진을 갖고 있다. 예를 들어, 테이블의 모든 빈 컬럼 안에 대시(dash)를 추가하려면, :empty pseudo-selector를 사용한다.
$('td:empty').html('-');



특정 클래스를 갖고 있지 않은 모든 엘리먼트를 찾는다면? CSS3은 이를 위한 신택스도 있다. :not pseudo-selector를 사용하는 것이다. 다음은 required의 클래스를 갖고 있지 않은 모든 인풋을 숨기는 방법이다.
$('input:not(.required)').hide();


또한, CSS에서처럼 다중 셀렉터를 콤마를 사용하여 하나로 연결시킬 수 있다. 다음은 이 페이지에서 모든 리스트 유형들을 동시에 숨기는 예제이다.
$('ul, ol, dl').hide();



XPath는 하나의 문서에서 엘리먼트를 찾는 강력한 신택스이다. CSS와는 다르며, CSS로 수행할 수 없는 몇 가지 일을 수행할 수 있다. 보더를 모든 체크 박스의 부모 엘리먼트에 추가하려면, XPath의 /.. 신택스를 사용할 수 있다.
$("input:checkbox/..").css('border', '1px solid #777');



가독성 있는 테이블을 만들려면, 다른 클래스 이름을 테이블의 모든 짝수 또는 홀수 행에 붙인다. 이를 다른 말로 테이블의 스트라이핑(striping)이라고 한다. jQuery를 사용하면 :odd pseudo-selector 덕택에 쉽게 수행할 수 있다. 아래 예제는 테이블의 모든 홀수 행의 백그라운드를 striped 클래스를 사용하여 변경한다.
$('table.striped > tr:odd').css('background', '#999999');


jQuery 셀렉터로 코드를 어느 정도 단순화 할 수 있는지를 보았다. 어떤 엘리먼트를 선택하든지 간에, 하나의 jQuery 셀렉터를 사용하여 이를 정의하는 방법도 찾을 수 있다.
플러그인으로 jQuery 확장하기
대부분의 소프트웨어와는 달리, jQuery용 플러그인 작성은 복잡한 API를 사용해야 하는 힘든 일이 아니다. 사실, jQuery 플러그인은 작성하기가 쉬워서 몇 가지만 작성하면 코드를 더욱 단순하게 유지할 수 있다. 다음은 여러분이 작성할 수 있는 가장 기본적인 jQuery 플러그인이다.
$.fn.donothing = function(){
return this;
};



단순하지만, 이 플러그인은 약간의 설명이 필요하다. 우선, 함수를 모든 jQuery 객체에 추가하려면, 여기에 $.fn을 할당하고, 이 함수는 this (jQuery 객체)를 리턴하여 이것이 메소드 체인을 깨트리지 않도록 해야 한다.
이 예제를 기반으로 쉽게 구현할 수 있다. css('background')를 사용하는 대신 플러그인을 작성하여 백그라운드를 바꾸려면, 다음을 사용한다.
$.fn.background = function(bg){
return this.css('background', bg);
};



css()에서 값을 리턴할 뿐이다. 이것은 이미 jQuery 객체를 리턴하기 때문이다. 따라서, 메소드 체인은 여전이 잘 작동한다.

여러분은 반복 작업이 있을 경우에 jQuery 플러그인을 사용하기 바란다. 예를 들어, 같은 일을 여러 번 수행하기 위해 each() 함수를 사용하고 있다면 플러그인을 사용해도 된다.

jQuery 플러그인을 작성이 쉽기 때문에, 여러분이 사용할 수 있는 것도 수백 가지나 존재한다. jQuery는 탭, 곡선형 코너, 슬라이드 쇼, 툴 팁, 날짜 셀렉터, 기타 여러분이 상상하고 있는 모든 것을 위한 플러그인이 있다. 플러그인 리스트는 참고자료 섹션을 참조하기 바란다.
가장 복잡하고 광범위하게 사용되는 플러그인은 Interface이다. 이것은 정렬, 드래그&드롭 기능, 복합 효과, 기타 복잡한 사용자 인터페이스(UI)를 핸들하는 애니메이션 플러그인이다. Interface가 jQuery를 위한 것이라면 Prototype에는 Scriptaculous가 있다.
또한 Form 플러그인도 대중적이고 유용하다. 이것으로 Ajax를 사용하여 백그라운드에서 폼을 쉽게 제출할 수 있다. 이 플러그인은 폼의 제출 이벤트를 하이재킹 하고, 다른 인풋 필드를 찾고, 이들을 사용하여 Ajax 호출을 구현하는 상황에 사용된다.
jQuery 이후의 삶
jQuery를 사용하여 할 수 있는 것의 표면적인 부분만 다루었다. jQuery는 기분 좋게 사용할 수 있고 새로운 트릭이나 기능도 자연스럽다. jQuery는 JavaScript와 Ajax 프로그래밍을 매우 단순화 시킬 수 있다. 새로운 것을 배울 때마다 코드는 더욱 단순해 진다.

jQuery를 배운 후에, 필자는 JavaScript 언어로 하는 프로그래밍에 재미를 발견했다. 지루한 부분은 알아서 처리되기 때문에, 필자는 중요한 코딩 부분에만 집중하면 된다. jQuery를 사용하게 되면서 지난날 for 루프를 작성하던 때는 거의 기억이 나지 않는다. 심지어, 다른 JavaScript 라이브러리를 사용할 생각도 거의 하지 않는다. jQuery는 JavaScript 프로그래밍 방식을 진정으로 바꿔 놓았다.

by 미소 | 2007/12/31 19:43 | jQuery를 익히자 | 트랙백

5. BIRT Tutorial - 프로젝트 생성


출처 : http://blog.naver.com/estern/110007681660
BIRT Tutorial - 프로젝트 생성
 
1. 프로젝트 생성
 
  이클립스는 프로젝트를 이용하여 파일들을 관리합니다. 따라서 보고서 작성에 앞서 프로젝트를 생성해야 합니다. 데모 프로젝트를 "My Reports"로 하도록하겠습니다.
 
다음과 같은 절차로 프로젝트를 만들어 보도록 하겠습니다.
 
1). File -> New -> Project 메뉴를 클릭합니다.
2). "New Project" 위져드 다이얼로그에서 "Business Intelligence 및 보고 도구" 그룹의
   "보고서프로젝트"를 선택합니다.
 

 
3). "Next" 버튼을 클릭합니다.
4). 프로젝트명에 My Reports를 입력합니다.

 
5). "Next" 버튼을 클릭합니다.
6). "Finish" 버튼을 클릭합니다.
7). 리포트 디자이너 퍼스펙티브로 스위치 할 것인지 물으면 "OK" 버튼을 클릭합니다.

 
2. 보고서 생성
이제부터 다음과 같은 절차로 보고서를 만들어 보도록 하겠습니다.
 
1). File->New->보고서 메뉴클릭하면 "새 보고서" 다이얼로그가 오픈됩니다.
2). 트리 뷰에서 프로젝트("My Reports")를 선택합니다.
3). 파일명에 "Customers.rptdesign"을 입력합니다.

 
4). "Next" 버튼을 클릭합니다.
5). 보고서 탬플릿에서 "그룹화한 목록"을 선택합니다.

 
6). "Finish" 버튼을 클릭합니다.
 
BIRT는 이클립스 워크스페이스에 새로 생성한 보고서를 오픈합니다.



by 미소 | 2007/12/31 19:24 | 웹 개발 | 트랙백(3)

4. BIRT Tutorial - 소개

출처 : http://blog.naver.com/estern/110007680671
BIRT Tutorial - 소개
 
  BIRT를 이용하여 간단한 리포트를 작성하는 방법을 알아 보도록하겠습니다. 보고서를 만들어 가면서 주요 사용자인터페이스 및 보고서를 구성하는 요소들에 대하여 설명을 드립니다. 이 튜토리얼을 마치시면 여러분의 데이터베이스를 이용하여 보고서를 작성하는 방법을 습득하게 되 실 것입니다.
 
1. BIRT 설치하기
  "BIRT 설치하기" 포스트를 참고하십시요.
 
2. BIRT Perspective 열기
  BIRT를 설치하시면 이클립스 퍼스펙티브에 "보고서" 퍼스펙티브가 추가됩니다.
"Window.-->Open Perspective --> Choose Others" 메인 메뉴를 통하여 보고서 퍼스펙티브를 오픈합니다.
 

by 미소 | 2007/12/31 19:06 | 웹 개발 | 트랙백(4)

3. BIRT 설치하기

출처http://blog.naver.com/estern/110007668877
1. BIRT 설치 환경
  사용자가 사용하고 있는 JRE 및 Eclipse 버젼을 고려하여 다음 환경중에 하나를 선택하여 설치합니다.
1.1 BIRT 2.0.1
      - Eclipse 3.1
      - GEF 3.1
      - EMF 2.1
      - JRE 1.4.2/JRE 1.5
 
1.2 BIRT 2.1 RC0 및 이후 버젼
      - Eclipse 3.2
      - GEF 3.2
      - EMF 2.2
      - JRE 1.4.2/JRE 1.5
 
2. Report Designer Full 버젼 설치하기
   BIRT 다운로드 페이지에는 모든 환경(Eclipse, GEF, EMF 그리고 BIRT)이 하나로 패키징된
패키지를 제공합니다. 이클립스를 사용하고 있지않다면, 이 페키지를 다운로드하여 사용하시면 편리합니다. 하지만 Full 버젼 이라도 iText.jar 및 prototype.js는 별도로 설치하여야합니다.  prototype.js는 2.1 RC4이후로 부터는 사용되지않습니다.

3. Report Designer 설치

3.1 Eclipse 설치
  Full 버젼을 설치하지 않을 경우는 다음 사이트에서 필요한 Eclipse 버젼을 다운로드하여 설치합니다.  Eclipse 설치는 본 문서에서 상세히 기술하지 않습니다.

Eclipse platform 다운로드 : http://download.eclipse.org/downloads/index.php

3.2 GEF 설치
  GEF(Graphic Editor Framework)은 BIRT UI에서 사용되는 플러그인 입니다.


GEF 다운로드 : http://download.eclipse.org/tools/gef/downloads/index.php


zip 파일을 다운로드하여 압축을 푼 후에 eclipse 디렉터리 밑에 있는 plugins, features, readme 폴더의 내용을 Eclipse 설치 디렉터리의 plugins, features, readme 폴더에 복사합니다.

저는 Eclipse 3.1.1 사용 중이라 GEF 3.1.1(GEF-runtime-3.1.1.zip)을 설치하였습니다.

3.3 EMF 설치
  BIRT 챠트 기능에서 EMF(Eclipse Modeling Framework)를 사용합니다. 아래 사이트에서 EMF & SDO RT version 2.1/2.2을 다운로드합니다.
 Look in the Latest Releases sections of the download page.

EMF 다운로드 : http://download.eclipse.org/tools/emf/scripts/downloads.php

zip 파일을 다운로드하여 압축을 푼 후에 eclipse 디렉터리 밑에 있는 plugins, features, readme 폴더의 내용을 Eclipse 설치 디렉터리의 plugins, features, readme 폴더에 복사합니다.

저는 Eclipse 3.1.1 사용 중이라  EMF & SDO RT 2.1.1(emf-sdo-runtime-2.1.1.zip)을 설치하였습니다.

3.4 BIRT 설치
  아래 사이트로 부터 Birt Designer 프레임워크만 다운로드하여 설치합니다.

Birt Report 프레임워크 다운로드 : http://download.eclipse.org/birt/downloads/

zip 파일을 다운로드하여 압축을 푼 후에 eclipse 디렉터리 밑에 있는 plugins, features, readme 폴더의 내용을 Eclipse 설치 디렉터리의 plugins, features, readme 폴더에 복사합니다.

저는 Eclipse 3.1.1 사용 중이라  Report Designer 2.0.2(birt-report-framework-2_0_2.zip)를 설치하였습니다.

3.5 선택사항 : Eclipse 링크 파일 생성
  나중에 정리하겠습니다.
 
3.6 iText 설치
   iText는 PDF 파일을 생성하기 위한 라이브러리로 Birt 리포트 작성에 사용됩니다. 아래 사이트에 연결하여 최신 버젼의 iText.jar 파일을 다운로드하여 "plugins/org.eclipse.birt.report.engine.emitter.pdf_version/lib" 디렉터리에 복사합니다.

iText 다운로드 : http://www.lowagie.com/iText/

itext-1.4.3.jar을 다운로드하여 설치하였습니다.

- Birt 2.1 RC0 이후 버젼 주의 사항: itext jar 파일을 plugins/com.lowagie.itext_*/lib 디렉터리에 복사하십시요.

3.7 prototype.js 설치
   Prototype은 동적인 웹 응용프로그램을 개발하기위하여 사용되는 JavaScript 프레임워크 입니다. 아래 사이트로 부터 최신 버젼의 prototype.js 파일을 다운로드하여  "plugins/org.eclipse.birt.report.viewer_version/birt/ajax/lib" 디렉터리에 복사합니다.

prototype.js 다운로드 : http://dev.conio.net/repos/prototype/dist/prototype.js

- Birt 2.1 RC2 또는 RC3 주의 사항: prototype.js를 plugins/org.eclipse.birt.report.viewer_version/birt/webcontent/birt/ajax/lib 디렉터리에 복사하십시요.

- Birt 2.1 RC4 이후 버젼 주의 사항: prototype.js이 이미 포함되어 있으므로 설치할 필요가 없습니다.

4. 설치확인
  Eclipse 구동하고 플러그인들이 잘 설치되었는지 확인합니다.

 

 
5. JDBC 드라이버

6. BIRT 신규 버젼 설치

7. J2EE 서버에 배포

8. 윈도우즈에서 BIRT 언어 팩 설치

9. 일반적인 문제들

by 미소 | 2007/12/31 19:00 | 웹 개발 | 트랙백(3)

2. war파일


by 미소 | 2007/12/31 18:36 | 웹 개발 | 트랙백

1. Aptana 웹 IDE 설치하기

Aptana 웹 IDE 설치는 플러그인을 앞서 설치했던 Eclipse PDT에 추가하는 것과 다름 아니다. 이 글을 쓰는 현재, Aptana Web IDE 버전은 0.2.8.14433이다. 다음 단계를 따라, 최신 버전의 Ajax 개발용 Aptana Web IDE를 설치한다.

  1. Eclipse의 Help 메뉴에서 Software Updates->Find and Install...를 선택하여 Install/Update 팝업 창을 연다.
    1. Install/Update 팝업 창에서, Search for new features to install 옵션을 선택하고 Next를 클릭한다.
    2. 업데이트를 검사할 새로운 원격 사이트를 설정하려면, New Remote Site... 버튼을 클릭하여 New Update Site 팝업 창을 연다.
    3. New Update Site 팝업 창에서, Site Name 텍스트 박스에서 Aptana를 입력한다.
    4. URL 텍스트 박스에서, Aptana 업데이트 사이트 URL: http://update.aptana.com/install/3.2/로 가고 OK를 클릭한다.
    5. Finish를 클릭하여 Updates 창을 연다.
    6. Update 창에서, Aptana update 체크 박스를 선택하고 Next를 클릭한다.
    7. 옵션을 선택하여 라이센스 동의 조건을 수락하고 Next를 클릭한다.
    8. Finish를 클릭한다.
    9. Feature Verification 다이얼로그 박스가 뜨면 Install All을 선택한다.
    10. Eclipse를 재시작 하라는 프롬프트에서, Yes를 클릭하여 재시작 한다.
  2. Eclipse IDE에서, Window->Open Perspective->Other를 선택하고 OK를 클릭한다. Aptana 퍼스펙티브가 활성화 되면 설치는 성공한 것이다.

지금까지 모든 것이 실행된다면, XHTML, JavaScript, XHR, PHP, MySQL 코드 같은 은행 시나리오 애플리케이션 생성물들을 개발, 테스트, 디버깅 할 때 사용할 수 있는 Eclipse IDE가 생긴 것이다. 여러분의 Eclipse IDE는 Zend Core PHP 서버에서 작동하도록 설정된다. Eclipse와 Zend Core의 동적인 결합은 모든 Ajax 개발과 전개의 필요를 채운다. 이제, 이러한 강력한 환경에서의 개발 및 전개 시나리오를 살펴보도록 하자.

by 미소 | 2007/12/31 17:47 | 웹 개발 | 트랙백

11. Ajax 마스터하기, Part 11: 서버 측의 JSON

난이도 : 고급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2007 년 10 월 09 일


지난 기술자료에서는 JavaScript의 객체를 JSON으로 변환하는 방법을 배웠습니다. 이 포맷은 객체들 또는 객체 어레이들로 매핑하는 데이터를 보내는데(그리고 받는데) 사용할 수 있습니다. 본 시리즈 마지막 기술자료에서는, JSON 포맷으로 서버에 보내진 데이터를 핸들하는 방법과, 같은 포맷을 사용하여 스크립트에 응답하는 방법을 설명합니다.

JSON의 실질적 가치


본 시리즈의 이전 기술자료에서 언급된 것처럼, JSON은 Ajax 애플리케이션을 위한 유용한 포맷으로서 JavaScript 객체와 스트링 값 사이를 빠르게 변환할 수 있게 해준다. Ajax 애플리케이션들은 플레인 텍스트를 서버 측 프로그램으로 보내고 받는데 가장 알맞고, 텍스트를 생성하는 API는 텍스트를 생성하지 않는 API를 통해서 이루어진다. JSON은 네이티브 JavaScript 객체들과 함께 작동할 수 있도록 해주며, 이러한 객체들이 표현되는 방식에 대해서는 걱정하지 않는다.



developerWorks Ajax 리소스 센터


Ajax 리소스 센터는 Ajax 프로그래밍 모델 관련 튜토리얼, 디스커션 포럼, 블로그, wiki, 이벤트, 뉴스 등을 제공하고 있습니다. Ajax에 관한 모든 것이 집대성 되어 있습니다.

XML은 똑같은 텍스트 효과를 제공하지만, JavaScript 객체를 XML로 변환하는 API는 JSON API만큼 성숙하지 못했다. JavaScript 객체를 생성하여 여러분이 선택했던 XML 변환 API로 실행될 수 있는지를 확인해야 한다. JSON은 다르다. 매우 인지 가능한 객체 유형을 핸들하고 훌륭한 JSON 데이터 표현을 제공한다.


JSON의 가장 큰 가치는 JavaScript가 데이터-포맷 언어로서가 아닌 JavaScript로서 역할을 할 수 있다는 점이다. JavaScript 객체를 사용하면서 배운 모든 것을 코드에 적용할 수 있고, 이러한 객체들이 텍스트로 변환되는 방식에 대해서는 걱정할 필요가 없다. 이제 간단한 JSON 메소드 호출을 해보자.


String myObjectInJSON = myObject.toJSONString();


이제 여러분은 결과 텍스트를 서버에 보낼 준비가 되었다.



JSON을 서버로 보내기


JSON을 서버로 보내는 것은 특별히 어려운 것은 아니지만, 중요한 문제이고 몇 가지 선택권이 있다. 하지만, 일단 JSON을 사용하기로 결정했다면, 선택은 훨씬 단순하고 제한되어 있다. 따라서 이 부분에 대해 크게 염려할 필요가 없다. 최소한, JSON 스트링을 서버에 가능한 빠르고 간단하게 보내기만 하면 된다.


GET을 통해 이름/값 쌍으로 JSON 보내기


JSON 데이터를 서버로 보내는 가장 쉬운 방법은 이것을 텍스트로 변환하고, 이름/값 쌍의 값으로 보내는 것이다. JSON으로 포맷된 데이터는 하나의 긴 객체일 뿐이며, Listing 1처럼 보인다.



Listing 1. JSON 포맷으로 된 JavaScript 객체
var people =  { "programmers": [    { "firstName": "Brett", "lastName":"McLaughlin",
"email": "brett@newInstance.com" }, { "firstName": "Jason", "lastName":"Hunter",
"email": "jason@servlets.com" }, { "firstName": "Elliotte", "lastName":"Harold",
"email": "elharo@macfaq.com" } ], "authors": [ { "firstName": "Isaac",
"lastName": "Asimov", "genre": "science fiction" }, { "firstName": "Tad",
"lastName": "Williams", "genre": "fantasy" }, { "firstName": "Frank",
"lastName": "Peretti", "genre": "christian fiction" } ], "musicians": [
{ "firstName": "Eric", "lastName": "Clapton", "instrument": "guitar" },
{ "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" } ] }


이것을 이름/값 쌍으로서 서버 측 스크립트로 보낼 수 있다.


var url = "organizePeople.php?people=" + people.toJSONString();
xmlHttp.open("GET", url, true);
xmlHttp.onreadystatechange = updatePage;
xmlHttp.send(null);



잘 되는 것처럼 보이지만, 한 가지 문제가 있다. 웹 브라우저가 인터프리팅을 시도할 수도 있는 공간과 모든 종류의 문자들이 JSON 데이터에 생겼다. 이러한 문자들이 서버에서(또는 데이터를 서버로 전송할 때) 혼란을 일으키지 못하게 하려면, JavaScript escape() 함수를 추가한다.


var url = "organizePeople.php?people=" + escape(
people.toJSONString()
);
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);



이 함수는 공백, 사선(slash) 등을 처리하고, 이것들을 안전한 문자로 변환한다. (예를 들면, 공백은 %20으로 변환되어, 브라우저는 이것을 공백으로 취급하지 않고, 변경되지 않은 채로 서버로 전달한다. 그리고 나면, 서버는 (일반적으로 자동으로) 이것을 전송 후에 다시 변환한다.


이러한 방식의 단점은 두 가지이다.



  • GET 요청을 사용하여 많은 데이터 청크를 보내는데, 이는 URL 스트링에 길이 제한을 갖고 있다. 이것은 허용되는 길이이지만, 객체의 JSON 스트링 표현의 길이는 가늠할 수 없다. 특히 매우 복잡한 객체를 사용할 때는 더욱 알 수가 없다.

  • 텍스트로서 네트워크를 통해서 모든 데이터를 보낼 때, 이는 매우 불안한 상태로 데이터를 보내는 것이다.


간단히 말해서, 이러한 문제들은 JSON 데이터와 구체적으로 관련이 있다기 보다는 GET 요청의 제한 요소라고 볼 수 있다. 하지만, 사용자의 이름과 성 이외의 것을 보낼 때 큰 문제가 된다. 원격으로 확인이 되었거나, 매우 긴 경우를 다룰 경우에는 POST를 고려해 볼 수 있다.


JSON 데이터의 포스팅(POST)


JSON 데이터를 서버로 보낼 때 POST 요청을 사용하기로 결정했다면, 코드를 크게 변경할 필요가 없다. 다음을 보자.


var url = "organizePeople.php?timeStamp=" + new Date().getTime();
request.open("POST", url, true);
request.onreadystatechange = updatePage;
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send(people.toJSONString());


이 코드 대부분이 POST 요청을 보내는데 초점을 맞추었던 "

Ajax 마스터하기, Part 3: Ajax의 고급 요청과 응답
"에서 보았던 것이다. 요청은 GET 대신 POST를 사용하여 열리고, Content-Type 헤더는 서버가 어떤 종류의 데이터를 기대하는지 알 수 있도록 설정된다. 이 경우, application/x-www-form-urlencoded인데, 이것은 서버가 텍스트를 통해 보낸 것이 일반 HTML 폼에서 온 것처럼 알도록 한다.


한 가지 주지할 점은 URL에 현재 시간을 붙였다는 점이다. 이로써 요청이 처음 보내진 후에 캐싱되지 않고, 메소드가 호출될 때마다 재생성 및 재전송 된다는 것이 확실해 진다. 이 URL은 변화하는 타임 스탬프 때문에 미묘하게 다르다. 이것은 스크립트로 POST 하는 것이 반복적으로 새로운 요청을 매번 생성하고 웹 브라우저는 서버에서 응답을 시도하고 저장하지 않는다.


JSON은 텍스트일 뿐이다!


GET 또는 POST를 사용하든지 간에, 중요한 것은 JSON은 단순한 텍스트 데이터일 뿐이라는 점이다. 특별한 인코딩을 필요로 하지 않으므로 쉽게 조작할 수 있고 서버로 보낼 수 있고, 모든 서버 측 스크립트는 텍스트 데이터를 처리할 수 있다. JSON이 바이너리 포맷이거나 이상한 텍스트 인코딩을 갖고 있다면, 상황은 달라졌을 것이다. 다행히도, JSON은 일반적인 텍스트 데이터이다. POST 섹션과 Content-Type 헤더에서 보았던 것처럼 이것을 서버로 보낼 때 많은 걱정을 할 필요가 없다.


서버에서 JSON 인터프리팅 하기


클라이언트 측 JavaScript 코드를 작성했고, 사용자들이 웹 폼과 페이지와 상호 작동 할 수 있도록 했고, 서버 측 프로그램으로 보내야 하는 정보를 수집했다면, 서버가 이제 중요한 역할을 할 차례이다. (비동기식으로 사용하는 서버 측 프로그램에 호출한다고 가정한다면, "Ajax 애플리케이션"으로 생각할 수도 있다.)


JSON 두 단계


서버에서 어떤 언어를 사용하든지 간에, 서버 측에서 JSON을 작동시키는 것은 두 단계이다.



  1. 자신이 사용하고 있는 언어용 JSON parser/toolkit/helper API를 찾아서 서버 측 프로그램을 작성한다.

  2. JSON parser/toolkit/helper API를 사용하여 클라이언트에서 요청 데이터를 가져다가 이것을 스크립트가 이해할 수 있는 것으로 바꾼다.


이것이 전부이다. 이제 이러한 단계를 보다 상세히 알아보자.


JSON 파서 찾기


JSON 파서나 툴킷을 찾을 수 있는 최상의 리소스는 JSON 웹 사이트이다. (참고자료) 포맷에 대한 많은 것을 배우는 것은 물론이고, ASP부터 Erlang, Pike, Ruby 등을 위한 JSON 툴과 파서에 대한 링크도 제공한다. 스크립트가 작성된 언어를 찾고 툴킷을 다운로드 한다. 이것을 제거, 확장, 설치하여(서버에서 C#, PHP, 또는 Lisp를 사용할 때 여러 변수들이 있다.) 서버 상의 스크립트와 프로그램이 이 툴킷을 사용할 수 있도록 한다.



예를 들어, PHP를 사용하고 있다면, PHP 5.2로 업그레이드 하고 이것을 사용하여 수행될 수 있도록 한다. 최신 버전의 PHP에는 JSON 확장이 포함되어 있다. 사실, 이것은 PHP로 작업할 때 JSON을 다룰 수 있는 최상의 방법이다. 자바 서블릿을 사용한다면, json.org에 호스팅 된 org.json 패키지가 제격이다. 이 경우, JSON 웹 사이트에서 json.zip을 다운로드 하고 포함된 소스 파일을 프로젝트의 빌드 디렉토리에 추가한다. 이 파일을 컴파일 하면 실행할 준비가 된 것이다. 다른 지원 언어도 과정은 비슷하다. 여러분이 사용하는 언어에 대한 전문적인 지식을 갖추는 것이 중요하다.


JSON 파서 사용하기


프로그램에 사용할 수 있는 리소스가 있다면, 호출 할 올바른 메소드를 찾는 것이 중요하다. 예를 들어, PHP와 함께 JSON-PHP 모듈을 사용하고 있다면,


// This is just a code fragment from a larger PHP server-side script
require_once('JSON.php');

$json = new Services_JSON();

// accept POST data and decode it
$value = $json->decode($GLOBALS['HTTP_RAW_POST_DATA']);

// Now work with value as raw PHP



여기에서, 여러분은 모든 데이터 -- 배열 포맷, 다중 행, 단일 값, JSON 데이터 구조에 들어갈 것들 -- 를 $value 변수에 네이티브 PHP 포맷으로 한다.


서블릿에서 org.json 패키지를 사용한다면, 다음과 같이 한다.


public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) { //report an error }

try {
JSONObject jsonObject = new JSONObject(jb.toString());
} catch (ParseException e) {
// crash and burn
throw new IOException("Error parsing JSON request string");
}

// Work with the data using methods like...
// int someInt = jsonObject.getInt("intParamName");
// String someString = jsonObject.getString("stringParamName");
// JSONObject nestedObj = jsonObject.getJSONObject("nestedObjName");
// JSONArray arr = jsonObject.getJSONArray("arrayParamName");
// etc...
}


자세한 내용은 org.json 패키지 문서를 참조하라. (참고자료). (주: org.json 또는 기타 JSON 툴킷에 대한 상세한 내용을 원한다면, 필자에게 이메일을 보내도 좋다. 여러분의 의견을 다음 기술자료에 반영하겠다!).



결론


이제는 서버 측에서 JSON을 다루는 방법에 익숙해 질 때이다. 본 기술자료와 Part 10에서는 단순한 기술적인 도움을 주는 것을 넘어서 JSON이 진정으로 유연하고 강력한 데이터 포맷이라는 것을 증명했다. 이것이 모든 애플리케이션에 사용되는 것이 아니더라도, 훌륭한 Ajax와 JavaScript 프로그래머라면 툴박스에 JSON 정도는 갖추고 있을 것이고, 사용할 수도 있을 것이다.



필자는 여러분의 JSON 사용 경험이나, 여러분이 서버에서 사용하기에 좋았던 혹은 좋지 않았던 언어에 대한 의견도 듣고 싶다. 자바와 XML 뉴스 그룹에 등록하여 알려주기 바란다. (참고자료) JSON과 텍스트 데이터 포맷의 힘을 누리기 바란다.


by 미소 | 2007/12/31 00:06 | AJAX 기본 | 트랙백

10. Ajax 마스터하기, Part 10: 데이터 전송에 JSON 사용하기

난이도 : 초급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2007 년 5 월 02 일

평이한 텍스트와 XML은 비동기식 애플리케이션에서 정보를 송수신 할 때 사용할 수 있는 데이터 포맷입니다. Ajax 마스터하기 시리즈에서는 또 다른 유용한 데이터 포맷인 JavaScript Object Notation (JSON)에 대해 알아보고, 이것이 애플리케이션에서 데이터와 객체들을 어떻게 이동시키는지를 설명합니다.



여러분이 본 시리즈를 오랫동안 읽어왔다면, 데이터 포맷에 대한 지식도 많이 쌓였을 것이다. 이전 기술자료에서는 평이한 텍스트와 간단한 name/value 쌍이 많은 비동기식 애플리케이션에서 어떤 역할을 하는지를 살펴보았다. 여러분은 다음과 같은 데이터를 조합할 수 있다.


firstName=Brett&lastName=McLaughlin&email=brett@newInstance.com


더 이상 무엇을 할 필요가 없다. 사실, 웹 고수들은 이것이 GET 요청을 통해 보내지는 정보용 포맷이라는 것을 알고 있다.



그리고 나서, 본 시리즈에서는 XML에 대해서 다루었다. 확실한 것은 XML은 부정적인 측면과 긍정적인 측면 모두에서 정당한 평가의 몫을 받았고, 이러한 것이 Ajax 애플리케이션에서도 나타나는 것은 그리 놀랄 일이 아니다. 본 시리즈의 지난 기술자료들에서 XML이 대안 데이터 포맷들을 어떻게 제공하는지를 파악하기 바란다.


<request>
<firstName>Brett</firstName>
<lastName>McLaughlin</lastName>
<email>brett@newInstance.com</email>
</request>


이것은 여러분이 위해서 보았던 것과 같은 데이터이지만, XML 포맷으로 저장되어 있다. 그 외에 특징적인 것은 없다. 평이한 텍스트와 name/value 쌍 대신에 XML을 사용할 수 있는 또 하나의 데이터 포맷일 뿐이다.


이 글에서는 또 다른 데이터 포맷인 JavaScript Object Notation(JSON)에 대해 살펴보겠다. JSON은 여러분이 이미 보았던 것과 비슷한 부분이 있고, 어떤 부분은 생소하다. 이것은 여러분에게 또 다른 선택권을 줄 것이며, 그 선택은 그렇게 나쁘지 않다.



선택할 수 있는 권한


JSON 포맷에 대해 상세히 설명하기 전에, 특히, 평이한 텍스트에서 XML과 name/value 쌍으로 작업하는 방법을 이미 알고 있다면, 다양한 데이터 포맷을 다룬 기술자료들을 먼저 읽어보기 바란다.(다음 글에서도 JSON에 대해 다룰 것이다.) 이유는 간단하다. 여러분에게 선택권이 많이 주어지고 문제에 대한 옵션들이 많아질수록, 단 하나의 솔루션 보다는 문제에 대한 최상의 솔루션을 찾을 수 있는 더 좋은 기회들이 생기기 때문이다.


name/value 쌍과 XML


본 시리즈에서는 name/value 쌍이 적합한 상황과 XML을 사용하기에 적합한 상황에 대해 이미 다루었다. 거듭 말하지만, 여러분은 우선 name/value 쌍을 사용하는 것을 늘 생각해야 한다. 이것은 대부분의 비동기식 애플리케이션에서 생기는 문제들에 대한 가장 간단한 솔루션이고, JavaScript에 대한 기본적인 지식만 있어도 간단히 해결된다.


여러분은 XML로 전향하는데 제약이 없다면 대안 데이터 포맷을 사용하는 것에 관해 걱정할 필요가 없다. 확실히, XML 포맷의 입력을 요하는 서버 측 프로그램으로 데이터를 보낸다면, 데이터 포맷으로 XML을 사용해야 한다. 대부분의 경우, XML은 애플리케이션에 여러 정보 조각들을 보내야 하는 서버용으로 적합하다. 다시 말해서, XML은 Ajax 애플리케이션으로부터의 요청 보다는, Ajax 애플리케이션으로의 응답으로 더 일반적으로 사용된다.


JSON 추가하기


name/value 쌍 또는 XML을 사용할 때, JavaScript를 사용하여 애플리케이션에서 데이터를 가져다가, 이것을 데이터 포맷에 채워 넣는다. 이 경우, JavaScript는 데이터 조작 언어로서의 역할을 하고, 웹 폼에서 데이터를 이동시키고 조작하며, 이것을 서버 측 프로그램에 쉽게 보내질 수 있는 포맷에 넣는다.


하지만, 단순한 포맷 언어 이상으로 JavaScript를 사용해야 할 때가 있다. 이러한 경우에, JavaScript 언어의 객체를 사용하여 데이터를 표현하고, 웹 폼에서 데이터를 가져다가 요청을 한다. 이러한 상황에서, JavaScript에 있는 객체에서 데이터를 추출하고 그 데이터를 name/value 쌍 또는 XML로 채우는 것이 일이다. 이때에 JSON이 빛을 발한다. JavaScript 객체를 (동기식 또는 비동기식) 요청의 일부로 보내질 수 있는 데이터로 쉽게 변환할 수 있다.


기본적으로 JSON은 문제해결의 특효약은 아니다. 하지만, 매우 특수한 상황에 있어서 훌륭한 옵션이 된다. 그러한 상황에 절대로 놓이지 않을 것이라고 가정하지 말고, 이 글과 다음 글을 읽으면서 JSON을 배워가기 바란다. 그러한 유형의 문제에 직면할 때를 대비한다고 생각하면 좋을 것이다.



JSON 기초


JSON으로는 JavaScript 객체로 표현된 데이터를 한 함수에서 다른 함수로, 또는 비동기식 애플리케이션의 경우, 웹 클라이언트에서 서버 측 프로그램으로 쉽게 전달할 수 있는 스트링으로 변형할 수 있다. 문자열이 약간 이상해 보이지만 JavaScript에 의해 쉽게 인터프리팅 되고, JSON은 name/value 쌍 보다 복잡한 구조도 표현한다. 예를 들어, 단순한 키와 값 리스트 대신, 배열과 복합 객체들을 나타낼 수 있다.


JSON 예제


JSON에서는 name/value 쌍이 다음과 같이 표현된다.


{ "firstName": "Brett" } 


이는 매우 기본적인 것이고, 텍스트의 name/value 쌍 보다는 더 많은 공간을 차지한다.


firstName=Brett


하지만, JSON은 여러 name/value 쌍들을 하나로 연결할 때 진가를 발휘한다. 우선, 다음과 같이, 여러 name/value 쌍으로 데이터 레코드를 생성할 수 있다.


{ "firstName": "Brett", "lastName":"McLaughlin", "email": "brett@newInstance.com" }


신택스의 관점에서 보면 name/value 쌍 보다 나은 점이 없어 보이지만, 이 경우, JSON은 훨씬 더 사용하기 쉬우며, 가독성도 놀랍다. 예를 들어, 위 세 개의 값들은 같은 레코드의 일부이다. 중괄호는 그 값이 같은 커넥션을 갖고 있다는 것을 표시하고 있다.


값들의 배열



값을 나타내야 할 때, JSON은 보다 읽기 쉽고, 덜 장황하다. 예를 들어, 인명부가 있다고 해보자. XML에서는, 수 많은 오프닝/클로징 태그에 둘러싸이게 되고, 전형적인 name/value 쌍을 사용한다면, 독점적인 데이터 포맷을 사용해야 하거나, person1-firstName처럼 키 네임을 수정해야 한다.


JSON에서는 다음과 같이 괄호를 사용하여 레코드를 그룹핑 할 수 있다.


{ "people": [
{ "firstName": "Brett", "lastName":"McLaughlin", "email": "brett@newInstance.com" },
{ "firstName": "Jason", "lastName":"Hunter", "email": "jason@servlets.com" },
{ "firstName": "Elliotte", "lastName":"Harold", "email": "elharo@macfaq.com" }
]}


이해하기도 어렵지 않다. 이 경우, people이라는 하나의 변수가 있고, 값은 세 개의 아이템을 포함하고 있는 배열이며, 각각의 레코드에는 이름, 성, 이메일 주소가 있다. 위 예제는 레코드들을 모두 투입하는 방법과, 아이템을 괄호가 있는 싱글 값으로 그룹핑 하는 방법을 설명하고 있다. 물론, 같은 신택스를 사용할 수 있지만, 여러 값들(각각 여러 레코드들을 갖고 있음)을 갖고 있다.


{ "programmers": [
{ "firstName": "Brett", "lastName":"McLaughlin", "email": "brett@newInstance.com" },
{ "firstName": "Jason", "lastName":"Hunter", "email": "jason@servlets.com" },
{ "firstName": "Elliotte", "lastName":"Harold", "email": "elharo@macfaq.com" }
],
"authors": [
{ "firstName": "Isaac", "lastName": "Asimov", "genre": "science fiction" },
{ "firstName": "Tad", "lastName": "Williams", "genre": "fantasy" },
{ "firstName": "Frank", "lastName": "Peretti", "genre": "christian fiction" }
],
"musicians": [
{ "firstName": "Eric", "lastName": "Clapton", "instrument": "guitar" },
{ "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" }
]
}


여기에서 주목해야 할 점은 각각이 다수의 값을 내포하고 있는 많은 값들을 표현할 수 있는 당신의 능력이다. 또 하나 주목해야 할 점은 레코드의 name/value 쌍들은 다른 메인 항목(programmers, authors, musicians)에 따라 변한다는 것이다. JSON은 완전히 동적이고, JSON 구조 안에서 데이터를 표현하는 방식을 바꿀 수 있다.


또 한 가지 알아두어야 할 것은 JSON 포맷 데이터로 작업할 때 제약 조건이 없다는 점이다. 따라서, 어떤 것을 표현하는 방식을 바꿀 수 있고, 심지어는 같은 데이터 구조 내에서 같은 것을 다른 방식으로 나타낼 수도 있다.



JavaScript에서 JSON 사용하기


JSON 포맷을 다룬 후라면, JavaScript에서 이것을 사용하는 것은 간단하다. JSON은 네이티브 JavaScript 포맷이고, JavaScript 내에서 JSON 데이터와 작업하기 위해 특별한 API나 툴킷이 필요 없다.


JSON 데이터를 변수에 할당하기


예를 들어, 새로운 JavaScript 변수를 만들고, JSON 포맷 데이터 스트링을 여기에 직접 할당한다고 해보자.


var people =
{ "programmers": [
{ "firstName": "Brett", "lastName":"McLaughlin", "email": "brett@newInstance.com" },
{ "firstName": "Jason", "lastName":"Hunter", "email": "jason@servlets.com" },
{ "firstName": "Elliotte", "lastName":"Harold", "email": "elharo@macfaq.com" }
],
"authors": [
{ "firstName": "Isaac", "lastName": "Asimov", "genre": "science fiction" },
{ "firstName": "Tad", "lastName": "Williams", "genre": "fantasy" },
{ "firstName": "Frank", "lastName": "Peretti", "genre": "christian fiction" }
],
"musicians": [
{ "firstName": "Eric", "lastName": "Clapton", "instrument": "guitar" },
{ "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" }
]
}


복잡하지 않다. people에는 이제 JSON 포맷 데이터가 포함되어 있다. 하지만, 많은 것을 수행하지 않는다. 데이터는 여전히 유용한 포맷으로 되어있지 않다.


데이터에 액세스 하기


위의 매우 긴 스트링은 단순한 배열일 뿐이고, JavaScript 변수에 그 배열이 있다면 쉽게 액세스 할 수 있다. 사실, 그 배열을 마침표로 구분할 수 있다. 따라서, programmers 리스트의 첫 번째 엔트리의 성(last name)에 액세스 하려면, JavaScript에서 다음과 같은 코드를 사용해야 한다.


people.programmers[0].lastName;


인덱싱은 '0' 부터 시작한다. 따라서, 이것은 people 변수에 있는 데이터로 시작한다. 그리고 나서, programmers라고 하는 아이템으로 이동하고, 첫 번째 레코드([0])에 액세스 한다. 마지막으로, lastName 키의 값에 액세스 한다. 결과는 "McLaughlin"이라는 스트링 값이다.


다음은 같은 변수를 사용하는 예제들이다.


people.authors[1].genre			// Value is "fantasy"

people.musicians[3].lastName // Undefined. This refers to the fourth entry,
and there isn't one

people.programmers.[2].firstName // Value is "Elliotte"


이 신택스로도, 다양한 JSON 포맷 데이터로 작업할 수 있고, 추가 JavaScript 툴킷이나 API가 필요 없다.


JSON 데이터 수정하기


위에 나타난 점과 괄호 표기법을 사용하여 데이터에 액세스 하듯, 같은 방식으로 데이터를 쉽게 수정할 수 있다.


people.musicians[1].lastName = "Rachmaninov";


문자열에서 JavaScript 객체로 변환했다면 변수에 있는 데이터를 수정하면 된다.


문자열로 변환하기


물론, 모든 데이터 수정은 텍스트 포맷으로 쉽게 변환할 수 없다면 가치가 없다. 이것 역시 JavaScript에서는 간단하다.


String newJSONtext = people.toJSONString();


이것이 다이다. 원하는 곳 어디에서나 사용할 수 있는 텍스트 문자열이 생겼으니, 이것을 Ajax 애플리케이션에서 요청 스트링으로 사용할 수 있다.



훨씬 더 중요한 것은, 어떤 JavaScript 객체라도 JSON 텍스트로 변환할 수 있다. 원래 JSON 포맷 스트링으로 할당된 변수로만 작업할 필요가 없다. myObject라는 객체를 변형하려면, 같은 종류의 명령어를 실행하면 된다.


String myObjectInJSON = myObject.toJSONString();


이것이 JSON과 다른 데이터 포맷과의 가장 큰 차이이자, 이 시리즈를 통해 탐구할 주제이다. JSON에서는, 단순한 함수를 호출하면, 포맷팅 되어 사용하기에 알맞은 데이터를 얻을 수 있다. 다른 데이터 포맷의 경우, 미가공 데이터와 포맷 데이터간 변환은 여러분의 몫이다. Document Object Model 같이 데이터 구조를 텍스트로 변환하는 함수를 제공하는 API를 사용할 때도, 네이티브 JavaScript 객체와 신택스 보다는, API를 배우고 그 API의 객체를 알아야 한다.


여러분이 많은 JavaScript 객체들과 작업할 때, JSON은 데이터를 서버 측 프로그램으로 요청을 보내기에 알맞은 포맷으로 쉽게 변환할 수 있다.



결론


본 시리즈에서는 데이터 포맷에 대한 내용을 많은 부분을 할애했다. 여러분의 비동기식 애플리케이션이 거의 비동기식이기 때문이다. 모든 종류의 데이터를 송수신 할 수 있는 다양한 툴과 기술이 있다면, 각 데이터 유형에 가장 잘 맞는 방식으로 수행하면 되고, Ajax 전문가의 반열에도 오를 것이다. 여기에 JSON을 추가하면, JavaScript에서 보다 복잡한 데이터 구조도 능숙히 다룰 준비가 된다.


다음 기술자료에서는 단순히 데이터를 보내는 것 이상으로, 서버 측 프로그램이 JSON 포맷 데이터를 수신하여 처리하는 부분을 설명할 것이다. 또한, 서버 측 프로그램이 스크립트와 서버 측 컴포넌트에 JSON 포맷으로 된 데이터를 보내는 방법도 설명할 것이다. XML, 평이한 텍스트, JSON 요청과 응답을 혼합하는 부분에 대해서도 설명할 것이다. 목표는 어떤 환경에서도 유연하게 이러한 툴들을 능숙히 조작할 수 있도록 하는 것이다.


by 미소 | 2007/12/31 00:01 | AJAX 기본 | 트랙백

9. Ajax 마스터하기, Part 9: Google Ajax Search API 사용하기

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2007 년 3 월 20 일

비동기식 요청은 서버 측 프로그램과의 통신에 대한 것만은 아닙니다. Google 또는 Amazon 같은 퍼블릭 API와 통신할 수 있고, 여러분이 갖고 있는 스크립트와 서버 측 프로그램이 제공하는 것 이상의 기능을 웹 애플리케이션에 추가할 수 있습니다. 이 글에서, Brett McLaughlin은 Google 같은 퍼블릭 API를 통해 요청을 하고 응답을 받는 방법을 설명합니다.



지금까지 본 시리즈에서는 클라이언트 웹 페이지가 서버 측 스크립트와 프로그램에 요청을 하는 상황을 주로 다루었다. 바로 이것이 80~90%의 Ajax 애플리케이션- XMLHttpRequest 객체를 사용하는 비동기식 웹 애플리케이션-이 작동하는 방식이다. 하지만, 이러한 방식에도 심각한 한계가 있다. 여러분 개개인의 창의력과 프로그래밍 기술, 또는 기업 내 팀 소속 프로그래머들의 창의성과 프로그래밍 기술력 역시도 제한 요소로 작용할 수 있다.


여러분은 무엇을 해야 하는지는 알고 있지만, 그 목표를 달성하는데 필요한 기술적 지식이 부족한 경우가 있다. 아마도, 신택스를 잘 모르거나, 올바른 알고리즘을 이해하지 못할 경우일 것이다. 또 어떤 경우에는, 자신의 필요에 맞는 데이터나 리소스(인력 또는 데이터 리소스)가 없을 수도 있다. "다른 사람의 코드를 사용할 수만 있다면….." 이라고 생각하고 있는 자신을 발견할 때가 종종 있다. 이 글에서 바로 이러한 문제에 대하여 다룰 것이다.


오픈 소스 스크립트와 프로그램


웹 애플리케이션에서 퍼블릭 API를 사용하는 문제를 논하기 전에, 오픈 소스 스크립트와 프로그램에 대해 잠깐 이야기 해보자. 오픈 소스는 무료로 사용 및 재사용될 수 있는 애플리케이션 코드를 칭하는 용어이다. 참고자료 섹션에서 관련 링크들을 제공하고 있다. 누군가가 이미 작성해 놓은 오픈 소스 코드를 가져다가, 비용이나 많은 규제 없이 자신의 환경으로 적용하여 사용할 수 있다.


여러분이 오픈 소스 코드를 사용한다면, 가끔은 애플리케이션에 문서를 추가하거나, 오픈 소스 프로그램이나 스크립트를 수정한 것을 커뮤니티에 기여해야 한다. 커뮤니티 안에서는 어떤 프로그램을 사용하든지 간에, 결과적으로 여러분이 작성하지 않았던 코드 또는 많은 도움과 리소스가 없었다면 작성되지 못했을 코드를 사용할 수 있다. Apache와 같은 프로젝트는 다른 사람들이 했던 작업을 쉽게 활용할 수 있다. 그리고 당신이 그러한 사용권에 대한 걱정할 필요도 없이, 그들은 기꺼이 여러분이 사용하기를 바란다!


기술자료와 튜토리얼 온라인


IBM developerWorks는 기술자료, 튜토리얼, 백서 등을 온라인으로 제공하고 있다. 양도 방대하고, Ajax와 관련한 기술자료만 해도 1000여건에 달한다. 심지어, 실력이 미천한 필자조차도 본 시리즈를 10회까지 진행했다. 이러한 기술자료에는 여러분이 사용할 수 있는 실행 코드, 예제, 다운로드 등이 포함되어 있다.


서버 측 프로그램이나 스크립트를 사용할 능력이 없거나 오픈 소스 프로그램이나 스크립트를 찾을 수 없다면 Google을 검색하여 자신이 찾고 있는 것에 대한 기본적인 설명 글만 입력하면 된다. developerWorks를 검색해도 같은 결과를 얻는다. 정확히 코드 조각을 찾거나, 전체 스크립트를 발견하기도 할 것이다. 도움이 되는 코멘트와 작동 방법에 대한 설명까지 곁들여지면 더욱 완벽하다.


퍼블릭 API 사용하기


때로는 기술적이지 않는 문제에도 직면하게 될 것이다. 특정 스크립트나 코드 조각을 작성하는 데에는 도움이 필요하지 않다. 오히려, 여러분이 갖고 있지 않은 데이터나 리소스가 필요하다. 이러한 경우에, 튜토리얼이나 오픈 소스 스크립트가 여러분에게 도움이 되겠지만, 이것으로 여러분의 니즈(needs)가 다 채워지는 것은 아니다. 웹 페이지에 검색 엔진을 설치하는 경우를 생각해 보자. 검색하고 싶은 데이터가 있는데, 그 데이터가 여러분의 기업이나 조직이 갖고 있는 것이 아닐 경우 어떻게 하겠는가?


기술이 아닌 데이터에 의해 제약을 받는 경우, public API가 해당 문제 해결에 도움이 된다. 퍼블릭 API를 사용해서는 다른 서버에 호스팅 된 프로그램을 사용할 수 있고, 다른 사람들의 데이터를 사용할 수 있다. 일반적으로, API 자체는 프로그램과 인터랙팅 하는 방식을 정의한다. 예를 들어, Google 검색 엔진에 대한 퍼블릭 API로 검색을 할 수 있지만, Google의 코드는 Google의 데이터를 검색하고 그 결과를 프로그램에 리턴 할 것이다. 여러분은 이러한 프로그램을 작성할 때 다른 사람의 기술력의 도움을 받을 뿐만 아니라, 여러분 또는 여러분 기업 이외의 데이터 스토어를 활용할 수 있는 혜택도 누리게 된다.


Google Ajax Search API 설정하기


Google은 온라인 시대의 대단한 발명품이라는 점은 두말할 나위 없다. 온라인에 대해 모르는 할머니와 4살 먹은 아이들도 Google에 대해 알고 있다. Google은 매우 대중적이고 유용한 검색 엔진을 구축하고, 여러분 각자의 프로그램에서 사용할 수 있는 퍼블릭 API를 가진 무료 서비스들을 제공하는데 헌신하고 있다. 이 섹션에서는, Google API를 사용할 수 있도록 설정하고, Google로의 비동기식 애플리케이션 통신을 만들 수 있는 방법을 설명하겠다.


Google에서 개발자 키 얻기


이 글은 Google의 Ajax Search API에 특히 초점을 맞출 것이다. Google Ajax Search API 홈 페이지(그림 1)를 방문하여 특정 API에 대한 내용을 찾아보기 바란다. (참고자료)




그림 1. Google의 Ajax Search API 페이지

Google's Ajax Search API page


우선 Sign up for a Google AJAX Search API key 링크를 클릭한다. 클릭하면, Google API를 사용을 위한 가입 페이지로 간다. 사용 조건에 동의하고 애플리케이션이 실행 될 웹 사이트의 URL을 제공한다. (그림 2)




그림 2. Google의 Ajax Search API 가입하기

Signing up for Google's Ajax Search APIn



어떤 URL을 사용해야 할까?

Google이 요구하는 URL은 다름아닌 사이트를 실행하는 도메인이다. 만일 여러분이 고유의 도메인을 갖고 있다면, http://www.newinstance.com을 사용할 수 있다. (이것은 내 개인 도메인이므로 여러분의 것을 사용해야 한다.) 여러분은 여러분의 사이트가 더 큰 도메인 내의 하위 도메인이나 특정 경로를 사용한다면 보다 구체적으로 지정해야 한다. 그러한 경우에, 여러분은 http://www.earthlink.net/~bmclaugh 또는 http://brett.earthlink.net 같은 URL을 사용해야 한다. 이 같은 특별한 경우를 제외하고는, 지나치게 구체적일 필요는 없다. 그저, 여러분의 전체 웹 사이트에 액세스 하는데 사용하는 기본 URL을 제공하고, 그 URL 내 어디에나 존재하는 API를 사용할 수 있다.



동의서를 읽고 체크 박스에 체크를 했다면, URL에 입력하고, Generate API Key를 클릭한 다음 1~2초 정도 기다린다. 이 시점에서 여러분은 Google로 로그인 하거나, 계정을 만들어야 한다. 이것이 기본 프로세스이고 여러분 스스로 쉽게 할 수 있다. 이것이 다 끝나면 응답 스크린이 나타날 것이고, 매우 긴 키가 제공된다. URL을 반복하고, 심지어 샘플 페이지도 제공한다. 키는 다음과 같은 양상을 띤다.


ABQIAAAAjtuhyCXrSHOFSz7zK0f8phSA9fWLQO3TbB2M9BRePlYkXeAu8lHeUgfgRs0eIWUaXg



Google의 API 문서


여러분에게 할당된 키를 사용하기 전에, Google의 API 문서를 읽어두는 것이 좋다. (키를 제공했던 페이지 하단에 링크가 있고, 아래 참고자료 섹션에서도 제공한다.) 이 글도 충분히 도움이 되겠지만, Google의 API 문서 역시 좋은 참고 자료이며, Google을 사용하는 방법에 대한 훌륭한 통찰력도 얻을 수 있을 것이다.


가장 단순한 Google 검색 웹 애플리케이션


Google이 제공하는 샘플 웹 페이지를 수정해보고, 이것이 어떻게 작동하는지를 보자.


검색 박스 만들기


Listing 1은 매우 단순한 웹 페이지이다. 여러분이 좋아하는 에디터에 타이핑하여, 저장하고 Google에 제공했던 도메인이나 URL에 업로딩 한다.



Listing 1. Google 검색 애플리케이션의 HTML


<html>
<head>
<title>My Google AJAX Search API Application</title>
<link href="http://www.google.com/uds/css/gsearch.css"
type="text/css" rel="stylesheet" />
<script
src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=YOUR KEY HERE"
type="text/javascript"> </script>
<script language="Javascript" type="text/javascript">
function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();

// These allow you to customize what appears in the search results
var localSearch = new GlocalSearch();
searchControl.addSearcher(localSearch);
searchControl.addSearcher(new GwebSearch());
searchControl.addSearcher(new GvideoSearch());
searchControl.addSearcher(new GblogSearch());

// Tell Google your location to base searches around
localSearch.setCenterPoint("Dallas, TX");

// "Draw" the control on the HTML form
searchControl.draw(document.getElementById("searchcontrol"));
}
</script>
</head>

<body onload="OnLoad()">
<div id="searchcontrol" />
</body>
</html>



볼드체로 되어 있는 부분을 Google에서 받은 여러분의 개발자 키로 대체하는 것을 잊지 말라. 페이지를 로딩하면, 그림 3과 같은 모습이 된다.




그림 3. Google의 단순한 검색 폼

The simplest search form for Google


특별한 것은 업지만, 이러한 작은 컨트롤 뒤에 Google의 모든 파워가 숨어있다.


검색 실행하기


검색어를 입력하고 Search를 클릭하여 Google을 실행한다. 여러 개의 결과들이 빠르게 나타나는 것을 볼 수 있다. (그림 4)




그림 4. Google의 검색 결과

Google's search results


pre-search 추가하기


확실히, 일단 검색을 수행하면 페이지는 좀더 유려하게 보이며, 비디오, 블로그, 검색 결과들이 이를 장식한다. 여러분이 정의한 검색어인 pre-search를 추가할 수 있다. 이것은 사용자가 페이지를 로딩하면 처음에 나타나는 결과이다. 이를 수행하려면, Listing 2의 굵은 라인을 JavaScript에 추가한다.



Listing 2. pre-search 용어 추가하기


function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();

// These allow you to customize what appears in the search results
var localSearch = new GlocalSearch();
searchControl.addSearcher(localSearch);
searchControl.addSearcher(new GwebSearch());
searchControl.addSearcher(new GvideoSearch());
searchControl.addSearcher(new GblogSearch());

// Tell Google your location to base searches around
localSearch.setCenterPoint("Dallas, TX");

// "Draw" the control on the HTML form
searchControl.draw(document.getElementById("searchcontrol"));

searchControl.execute("Christmas Eve");
}



고유의 초기 검색어를 코드에 삽입하여 페이지가 로딩될 때 나타나는 부분을 커스터마이징 할 수 있다.


JavaScript 분해


더 진행하기 전에, 이러한 기본적인 명령어가 어떤 일을 하는지 보자. 먼저, 새로운 GSearchControl이 만들어진다. (Listing 3) 이것은 모든 검색을 수행할 수 있도록 해주는 구조이다.



Listing 3. 새로운 GSearchControl 생성하기


function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();


...
}



그런 다음 이 코드는 GlocalSearch를 사용하여 새로운 로컬 검색을 설정한다. 이것은 특정 위치에 기반한 검색을 수행하는 특별한 Google 구조이다. 로컬 검색은 Listing 4에 설명되어 있다.



Listing 4. 새로운 로컬 검색 설정하기


function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();

// These allow you to customize what appears in the search results
var localSearch = new GlocalSearch();
...

// Tell Google your location to base searches around
localSearch.setCenterPoint("Dallas, TX");

...
}



일단 여러분은 호출할 객체와 메소드를 배우기만 하면 충분히 이해할 수 있다. Listing 4의 코드는 새로운 로컬 검색자를 만든 다음, 검색의 중심 포인트를 설정한다.


Listing 5에서는 검색 컨트롤에게 어떤 유형의 검색을 수행해야 하는지를 지시하고 있다.



Listing 5. 검색 유형


function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();

// These allow you to customize what appears in the search results
var localSearch = new GlocalSearch();
searchControl.addSearcher(localSearch);
searchControl.addSearcher(new GwebSearch());
searchControl.addSearcher(new GvideoSearch());
searchControl.addSearcher(new GblogSearch());

// Tell Google your location to base searches around
localSearch.setCenterPoint("Dallas, TX");

...
}



여러 가지 검색 유형들을 요약하면 다음과 같다.




  • GwebSearch: 웹 검색용 객체이고, Google은 이것으로 유명해졌다.


  • GvideoSearch: 검색어와 관련된 비디오를 찾는다.


  • GblogSearch: 이 객체는 블로그를 통한 검색에 특별한 초점을 맞추고 있는데, 다른 웹 콘텐트 유형과는 약간 다르게 구성되고, 다른 태그가 붙는다.


여러분은 이미 특정 검색을 사전 로딩(pre-load)하는 방법을 배웠다. 이제 남은 것은 Listing 6에 나와있는 draw() 메소드 호출이다. 이 호출을 HTML에 있는 DOM 엘리먼트에 준다. (DOM에 대해서는 이전 기술자료를 참조하라. 참고자료) 컨트롤이 폼에 나타나고 사용할 준비가 된다.



Listing 6. 검색 컨트롤 가져오기


function OnLoad() {
// Create the Google search control
var searchControl = new GSearchControl();

// These allow you to customize what appears in the search results
var localSearch = new GlocalSearch();
searchControl.addSearcher(localSearch);
searchControl.addSearcher(new GwebSearch());
searchControl.addSearcher(new GvideoSearch());
searchControl.addSearcher(new GblogSearch());

// Tell Google your location to base searches around
localSearch.setCenterPoint("Dallas, TX");

// "Draw" the control on the HTML form
searchControl.draw(document.getElementById("searchcontrol"));

searchControl.execute("Christmas Eve");
}




Ajax는 어디에 있는가?


이렇게 간단한 검색 박스에서 비동기 특성이 어디에 나타나있는지 확실치 않다. 확실한 것은, 여러분 고유의 웹 애플리케이션 어딘가에 Google 검색 박스를 두긴 했지만, 이것은 Google 검색에 대한 내용이 아닌 Ajax 애플리케이션 시리즈 글이다. 그렇다면, Ajax는 어디에 있는가?


검색어를 입력하고 Search 버튼을 클릭하면, 매우 Ajax 다운 응답을 보게 된다. 검색 결과는 페이지를 재 로딩하지 않아도 나타난다. 바로 이것이 대부분의 Ajax 애플리케이션의 특징이다. 페이지를 다시 로딩하지 않고 시각적인 콘텐트 변화가 이루어 진다. 일반적인 요청/응답 모델 그 이상이라는 점은 확실하다. 그렇다면, XMLHttpRequest? Where is the request 객체는 어디에 있는가? 그리고 getElementById() 메소드 외에, DOM과 페이지 조작은 어떻게 되었는가? 이것은 HTML의 단 두 줄이면 해결된다.


Google과 JavaScript.


첫 번째 줄은 그 동안 많이 다루지 못했던 부분이다. (Listing 7)



Listing 7. 가장 중요한 JavaScript 파일


<script
src="http://www.google.com/uds/api?file=uds.js
&v=1.0&key=
[YOUR GOOGLE KEY]"
type="text/javascript"> </script>



신택스는 특별한 것은 없지만, 중요한 것은 Google이 uds.js라는 파일을 호스팅하는데, 이 안에 검색 박스가 작동하는데 필요한 모든 JavaScript가 들어있다는 점이다. 엄밀히 말하면 다른 사람들의 코드를 사용하고 있는 것이다. 여러분은 서드 파티가 여러분의 애플리케이션이 사용하는 코드를 호스팅 하도록 할 수 있다. Google은 보존과 관리까지 다루기 때문에 이것은 매우 중요하고, 기업이 JavaScript 파일을 업그레이드 할 때 자동적으로 혜택이 미친다. Google은 여러분 모르게 API를 변경하지 않기 때문에 여러분의 코드는 JavaScript 파일이 수정되더라도 계속 실행된다.


GSearchControl 객체


여기에서 숨겨진 한 가지는 GSearchControl 객체용 코드로서 onLoad() JavaScript 함수에서 생성된다. 이 객체를 생성하려면 Listing 8의 코드를 호출한다.



Listing 8. GSearchControl 객체 생성하기


// Create the Google search control
var searchControl = new GSearchControl();



이 HTML 역시 매우 간단하다. JavaScript가 참조할 수 있는 div 태그와 ID가 전부이다. (Listing 9)



Listing 9. 검색 컨트롤을 만드는데 필요한 모든 HTML


<div id="searchcontrol" />




Google의 JavaScript

Google의 JavaScript는 이해하기가 어렵다. 우선, uds.js JavaScript 파일은 일부 로컬 설정을 파악하고, Google 스팩의 태스크를 처리하며, Google 키를 확인한 다음 두 개의 다른스크립트: http://www.google.com/uds/js/locale/en/uistrings.js와 http://www.google.com/uds/js/uds_compiled.js를 로딩한다. 이러한 두 개의 파일들을 실제로 가져다가 관심 있는 부분을 보는 것은 좋다. 고급 코드를 파악하는 것은 힘든 일이며 포맷팅은 끔찍하므로 조심하여야 한다. 대부분의 경우, 이러한 파일을 사용하는 방법만 알면 되며, 라인 마다 이들이 무엇을 수행하는지 알려고 고심할 필요까지는 없다.



하지만, 안 보이는 곳에서 Google의 코드는 거의 모든 종류의 일들을 수행하고 있다. 새로운 텍스트 박스, 일부 아이콘 그래픽, JavaScript 함수를 호출하는 버튼을 만든다. 따라서 여러분은 모든 종류의 작동을 거저 얻게 되는 것이다. 하지만 여러분은 이러한 일이 어떻게 발생하는지에 대한 기초는 이해하고 있으면, 코드를 사용하고 애플리케이션을 실행시키는데 편리하다.


Ajax는 여러분이 작성한 코드 그 이상이다.


Ajax 애플리케이션은 단순히 XmlHttpRequest를 사용하는 것에 관한 것만은 아니다. 이것은 웹 애플리케이션에 접근하는 방식에 관한 것이고, 비동기성에 기반하고 있다. 여러분이 Ajax 스팩의 코드를 작성하지 않았지만, Google 덕택으로 Ajax 애플리케이션이 만들어졌다. Google이 대부분의 작업을 했고, 여러분은 그 수혜자가 된 것이다.


Google의 Ajax Search API 미래


이러한 단계들을 거쳐 이것을 여러분 고유의 애플리케이션에 적용하는 것은 여러분에 달려있다. 가장 간단하게는 div를 자신의 웹 페이지에 추가하고 Listing 1의 JavaScript를 웹 페이지에 추가하는 것이다. 이것으로 Google 검색을 사용할 준비가 된 것이다.


하지만 이것이 전부가 아니며, 이러한 옵션이나 컨트롤에만 국한될 필요가 없다. 웹 결과, 블로그 결과, 비디오 결과를 웹 애플리케이션에 통합할 수 있다. 각각 특정 유형의 결과에 초점을 맞춘 검색 컨트롤을 여러 개 제공할 수도 있다. div에서 측면을 벗어나는 대신, 애플리케이션 콘텐트의 나머지 중간 오른쪽에 span 엘리먼트에 Google 검색 컨트롤을 삽입할 수 있다. 그 어떤 경우이든, Google 검색은 여러분의 필요에 맞춰 구성될 수 있고, Google에 맞추기 위해 애플리케이션을 수정할 필요는 없다.


맺음말


이 글에서 배운 것을 토대로 구현하고, Google 검색 박스와 기타 Google API를 여러분의 Ajax 애플리케이션에 추가해 보기 바란다. 여러분은 이제 퍼블릭 API를 사용하는 방법을 알았다. 예를 들어, Amazon.com은 퍼블릭 API를 제공하면, Amazon 서점의 책과 다른 상품들에 대해서도 Google과 똑 같은 웹 검색을 수행할 수 있다. 여러분이 선호하는 퍼블릭 API를 선택하여 여러분의 코딩 스킬로 할 수 있는 그 이상을 할 수 있다. 사실, Google, Amazon.com, Flickr 등등의 결과물이 통합된 사이트를 쉽게 만들 수 있다.


Google이 제공하는 검색 알고리즘과 수 많은 데이터 스토어 때문에 Google을 사용하는 방법을 익히는 것은 중요하지만, 중요한 것은 퍼블릭 API를 사용하는 방법을 배우는 것이다. 코딩 스킬 이상으로 애플리케이션에 대해 생각해야 한다. 이것은 다양한 데이터 조각에 대한 게이트웨이가 될 수 있다. 그 데이터는 Google, Amazon.com, del.icio.us의 서버에 저장될 수 있다. 이렇게 하는 것 만으로도 여러분 스스로 코딩 할 수 있는 것 이상으로 매우 강력한 솔루션을 얻을 수 있다.



애플리케이션을 만들어 보자. 온갖 장소에 있는 데이터를 사용하고, 여러분이 코딩한 것에만 국한하지 말자. 다음 글에서는 데이터 포맷 같은 보다 기술적인 사안을 가지고 다시 돌아오겠다.


by 미소 | 2007/12/30 23:52 | AJAX 기본 | 트랙백(3) | 핑백(1)

8. Ajax 마스터하기, Part 8: 요청과 응답에 XML 사용하기

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2007 년 1 월 02 일


지난 시리즈에서는, Ajax 애플리케이션인 서버로 가는 요청을 XML로 포맷팅 하는 방법을 설명했습니다. 그리고 대부분의 경우, 이것이 좋은 방법이 아닌지를 설명했습니다. 이번에는, 좋은 방법을 소개합니다. XML 응답을 클라이언트로 리턴하는 방법을 설명합니다.



여러분이 해서는 안될 일에 대한 글을 쓰는 것을 별로 좋아하지 않는다. 그것은 매우 바보 같은 일이다. 지금까지 어떤 것을 설명하는데 할애한 시간만큼, 나머지는 그 동안 배웠던 그 기술을 사용하는 것이 왜 좋은 생각이 아닌지를 설명하는데도 시간을 할애하곤 한다. 바로 지난달 지난 달 기술자료(참고자료)가 그 경우이다. Ajax 애플리케이션 요청에 데이터 포맷으로서 XML을 사용하는 방법을 설명했다.


모쪼록 이 글이, XML 요청에 대해 배우느라 여러분이 보냈던 시간을 보상해 주기를 바란다. Ajax 애플리케이션에서, 송신 데이터 포맷으로서 XML을 사용하는 경우는 드물지만, 서버에서 XML을 클라이언트보내야 하는 이유는 충분하다. 따라서, 지난 글에서 여러분이 XML에 대해 배웠던 모든 것이 이번 글에서 비로서 가치를 지니기 시작했다.


(가끔씩) 서버는 많은 말을 하지 못한다.


서버에서 XML 응답을 받는 것에 대한 기술적인 상세를 보기 전에, 서버가 요청에 대한 응답으로 XML을 보내는 것이 좋은 것인지를 이해해야 한다. (그리고 클라이언트가 요청을 XML로 보내는 것과는 어떻게 다른지도 이해해야 한다.)


클라이언트는 이름/값 쌍으로 말한다.


클라이언트는 대부분의 경우에 XML을 사용할 필요가 없다. 이름값 쌍을 사용하여 요청을 보낼 수 있기 때문이다. 따라서, 다음과 같이 이름을 보내게 된다: name=jennifer. 또한 이어지는 이름값 쌍들은 앰퍼샌드(&)를 사용하여 추가할 수 있다: name=jennifer&job=president. 간단한 텍스트와 이러한 이름값 쌍을 사용하면, 클라이언트는 여러 값을 가진 요청을 서버로 쉽게 보낼 수 있다. XML이 제공하는 추가 구조(오버헤드)가 필요 없다.


사실, XML을 서버로 보내야 하는 대부분의 이유들이 다음 두 개의 범주로 나뉠 수 있다.




  • 서버는 XML 요청을 수락하기만 한다. 이 경우, 선택권이 없다. 지난 달 기술자료에서, 이러한 유형의 요청들을 보낼 때 필요한 모든 툴을 제공했다.


  • XML 요청이나 SOAP 요청만 수락하는 원격 API를 호출할 경우. 이것은 매우 특별한 케이스이지만, 짚고 넘어가야 한다. 비동기식 요청에서 Google이나 Amazon에서 API를 사용할 경우, 특별히 고려해야 할 것이 있다. 다음 달 기술자료에서 API로 요청하는 문제를 예제를 통해 설명하겠다.


서버는 (표준 방식으로) 이름값 쌍을 보낼 수 없다.


이름값 쌍을 보낼 때, 요청을 보내는 웹 브라우저와 요청에 응답하고 서버 프로그램을 호스팅 하는 플랫폼은 이런 이름값 쌍들을 서버 프로그램이 쉽게 작업할 수 있는 데이터로 변환한다. 실제로, 모든 서버 측 기술 -- Java™ servlets, PHP, Perl, Ruby on Rails- 덕택에 다양한 메소드를 호출하여 이름에 기반한 값들을 얻을 수 있다. 따라서 name 애트리뷰트를 얻는 것은 간단하다.


서버가 name=jennifer&job=president 스트링으로 애플리케이션에 응답한다면, 클라이언트는 두 개의 이름값 쌍을 나누고, 각 쌍을 이름과 값으로 나눌 표준화 된 쉬운 방법이 없다. 리턴된 데이터를 직접 파싱해야 할 것이다. 서버가 이름값 쌍으로 구성된 응답을 리턴하면, 응답은 세미콜론, 파이프 심볼, 기타 비표준 포맷팅 문자로 구분된 엘리먼트를 가진 응답만큼이나 해석하기 어렵다.



싱글 스페이스(single space)!

대부분의 HTTP 요청에서, %20은 싱글 스페이스를 나타내는데 사용된다. 따라서 "Live Together, Die Alone"이라는 텍스트는 Live%20Together,%20Die%20Alone 형태로 HTTP로 보내진다.



응답에 플레인 텍스트를 사용하고, 적어도 응답에 여러 값이 포함되어 있을 때, 클라이언트가 그 응답을 받아서 표준 방식으로 인터프리팅 해야 하는 쉽지 않은 방식이다. 서버가 넘버 42를 보냈다면, 플레인 텍스트로도 충분하다. 하지만, TV 쇼 Lost, Alias,, Six Degrees,의 최근 시청률을 보냈다면? 여러분은 플레인 텍스트를 사용하여 이 응답을 보낼 많은 방법들을 선택할 수 있지만(Listing 1), 클라이언트에 의한 작업 없이는 쉬운 인터프리팅 방법이 없고, 표준화된 방법도 없다.



Listing 1. TV 시청률에 대한 서버 응답 (다양한 버전)


show=Alias&ratings=6.5|show=Lost&ratings=14.2|show=Six%20Degrees&ratings=9.1

Alias=6.5&Lost=14.2&Six%20Degrees=9.1

Alias|6.5|Lost|14.2|Six%20Degrees|9.1


이러한 응답 스트링들을 나누는 방법을 알아내는 것이 어려운 일은 아니지만, 클라이언트는 세미콜론, 등가 표시, 파이프, 앰퍼샌드에 따라 스트링을 파싱하고 나누어야 한다. 이는 다른 개발자들이 쉽게 이해하고 관리할 수 있는 강력한 코딩 방법이 아니다.


XML


이름값 쌍으로 서버가 클라이언트에 응답하는 표준 방법이 없다는 것을 깨달았다면, XML을 사용해야 하는 이유가 분명해진다. 데이터를 서버로 보낼 때, 이름값 쌍은 최상의 선택이다. 서버와 서버 측 언어는 그 쌍들을 쉽게 인터프리팅 할 수 있기 때문에다. 마찬가지로, 데이터를 클라이언트로 리턴할 때 XML을 사용하면 일이 쉬워진다. 이전 기술자료들에서 XML을 파싱할 때 DOM을 사용하는 것에 대해 다루었고, 앞으로의 기술자료에서는 JSON이 XML을 파싱하는 옵션을 어떻게 제공하는지를 설명할 것이다. 무엇보다도, 여러분은 XML을 플레인 텍스트로서 취급할 수 있고, 이러한 방식으로 값을 얻어낼 수 있다. 따라서, 서버에서 XML 응답을 얻을 수 있는 여러 가지 방식이 있고, 매우 표준적인 방식으로 클라이언트에 있는 데이터를 가져다가 사용할 수 있다.


게다가 XML은 이해하기도 쉽다. 프로그래밍을 하는 사람이라면 Listing 2의 데이터를 이해할 수 있을 것이다.



Listing 2. TV 시청률에 대한 서버 응답 (XML)



<ratings>
<show>
<title>Alias</title>
<rating>6.5</rating>
</show>
<show>
<title>Lost</title>
<rating>14.2</rating>
</show>
<show>
<title>Six Degrees</title>
<rating>9.1</rating>
</show>
</ratings>



Listing 2의 코드는 특정 세미콜론이나 어포스트로피 때문에 생기는 혼란이 없다.



서버에서 XML 받기


이 시리즈의 초점은 Ajax의 클라이언트 측에 맞춰져 있기 때문에, 서버 측 프로그램이 XML로 응답을 생성하는 방법에 대해서는 자세히 들어가지 않겠다. 하지만, 클라이언트가 XML을 받을 때 몇 가지 특별한 고려 사항이 있다.


서버에서 오는 XML 응답을 두 가지 기본 방식으로 취급할 수 있다.



  • XML로 포맷팅 되는 플레인 텍스트

  • DOM Document 객체로 나타나는, XML 문서


서버에서 오는 간단한 응답 XML을 보자. Listing 3은 위와 똑 같은 TV 리스팅을 보여주고 있다. (Listing 2와 같은 XML이지만, 여러분의 편의를 돕고자 다시 기재한다.)



Listing 3. XML-포맷 TV 시청률 예제



<ratings>
<show>
<title>Alias</title>
<rating>6.5</rating>
</show>
<show>
<title>Lost</title>
<rating>14.2</rating>
</show>
<show>
<title>Six Degrees</title>
<rating>9.1</rating>
</show>
</ratings>


XML을 플레인 텍스트로서 처리하기


새로운 프로그래밍 기술을 배우는 관점에서 볼 때, XML을 다루는 가장 쉬운 옵션은 서버로부터 리턴된 텍스트 조각처럼 다루는 것이다. 다시 말해서, 데이터 포맷은 기본적으로 무시하고, 서버에서 응답만 취하면 된다.


이러한 상황에서, 마치, 서버가 비 XML 응답을 보낼 때처럼, 요청 객체의 responseText 속성을 사용한다. (Listing 4).



Listing 4. XML을 정상적인 서버 응답으로서 처리하기


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText;

// response has the XML response from the server
alert(response);
}
}
}



이전 기술자료 리뷰

코드 중복을 피하기 위해, 후반 기술자료에서는 논의되는 주제와 관련된 코드 부분만 제시하겠다. 따라서, Listing 4에서는 Ajax 클라이언트 코드의 콜백 메소드만 보인다. 비동기식 애플리케이션에 이 부분이 어떻게 적용되는지 잘 모르겠다면, 본 시리즈의 기술자료들을 복습하기 바란다. 참고자료 섹션에 이전 기술자료 링크가 제공된다.



이 코드에서, updatePage()는 콜백이고, requestXMLHttpRequest 객체이다. 모든 것이 response 변수 안에 이어진 XML 응답이 될 것이다. 이 변수를 프린트하면, Listing 5와 같이 된다. (Listing 5의 코드는 하나의 연속적인 라인이다. 여기에서는 디스플레이를 위해서 여러 줄로 표현했다.)



Listing 5. 응답 변수의 값


<ratings><show><title>Alias</title><rating>6.5</rating>
</show><show><title>Lost</title><rating>14.2</rating></show><show>
<title>Six Degrees</title><rating>9.1</rating></show></ratings>


가장 중요한 것은 XML이 모두 함께 실행된다는 것이다. 대부분의 경우, 서버는 스페이스와 캐리지 리턴으로 XML을 포맷팅 하지 않는다. 단지 하나로 연결할 뿐이다. (Listing 5) 물론, 애플리케이션은 스페이싱(spacing)에 대해서 신경 쓰지 않는다. 다만 읽기가 힘들어질 뿐이다.


이때, JavaScript split 함수를 사용하여 데이터를 나누고, 기본 스트링 조작을 통해서 엘리먼트 이름과 값을 얻을 수 있다. 물론, 이것은 엄청난 작업이고, 이전 시리즈에서 다루었던 DOM(Document Object Model)을 보는데 많은 시간을 보내야 한다. 따라서, 나는 여러분에게 responseText를 사용하여 서버의 XML 응답을 사용 및 출력할 것을 권하고 싶지만, 그 코드 모두를 보여줄 수는 없다. DOM을 사용할 수 있다면, 이러한 방식을 사용하여 XML 데이터를 얻어서는 안된다.


XML을 XML로 취급하기


서버의 XML 포맷 응답을 다른 텍스트 응답처럼 취급할 수 있지만, 이것은 좋은 방법이 아니다. 우선, 이 시리즈를 잘 읽어보았다면, XML을 조작할 수 있는, JavaScript 친화적인 API인 DOM을 사용하는 방법을 익혔을 것이다. JavaScript와 XMLHttpRequest 객체는 서버의 XML 응답을 완벽하게 얻고 이것을 DOM Document 객체 폼으로 얻을 수 있는 속성을 제공한다.



Listing 6을 보자. 이 코드는 Listing 4와 비슷하지만, responseText 속성을 사용하기 보다는, 콜백이 responseXML 속성을 사용한다. XMLHttpRequest에 사용할 수 있는 이 속성은 DOM 문서의 형태로 서버 응답을 리턴한다.



Listing 6. XML을 XML로 취급하기


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var xmlDoc = request.responseXML;

// work with xmlDoc using the DOM
}
}
}


DOM Document가 생겼으니, 다른 XML처럼 작업할 수 있다. 예를 들어, Listing 7에서처럼, 모든 show 엘리먼트를 얻을 수 있다.



Listing 7. 모든 show 엘리먼트 얻기


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var xmlDoc = request.responseXML;

var showElements = xmlDoc.getElementsByTagName("show");
}
}
}


여러분이 DOM에 익숙하다면, 이것도 익숙해야 한다. 이미 배웠던 모든 DOM 메소드를 사용할 수 있고, 서버에서 받은 XML을 쉽게 조작할 수 있다.


물론, 정상적인 JavaScript 코드로 혼합할 수도 있다. 예를 들어, 모든 show 엘리먼트들을 통해 반복할 수 있다. (Listing 8)



Listing 8. 모든 show 엘리먼트들을 통해 반복하기


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var xmlDoc = request.responseXML;

var showElements = xmlDoc.getElementsByTagName("show");
for (var x=0; x<showElements.length; x++) {
// We know that the first child of show is title, and the second is rating
var title = showElements[x].childNodes[0].value;
var rating = showElements[x].childNodes[1].value;

// Now do whatever you want with the show title and ratings
}
}
}
}


비교적 간단한 코드를 사용하여, XML 응답을 XML로 취급할 수 있고, 약간의 DOM과 단순한 JavaScript를 사용하여 서버 응답을 다룰 수 있다. 더 중요한 것은, 콤마로 분리된 값이나 파이프로 구분된 이름값 쌍 대신, 표준화된 포맷인 XML로 작업했다는 점이다. 다시 말해서, 서버로 요청을 보내는 것 같은, 합법적인 곳에서 XML을 사용할 수 있었다는 점이다.


서버의 XML: 간단한 예제


서버에서 XML을 생성하는 방법에 대해 많이 설명하지는 않았지만, 많은 설명 필요 없이, 간단한 예제를 보면서 이러한 상황을 다루는 방법에 대해 알아보자. Listing 9는 요청에 대한 응답으로 XML을 출력하는 PHP 스크립트이다. 비동기식 클라이언트에서 온 것이다.


이것은 과격한 접근 방식이다. PHP 스크립트는 실제로 XML 아웃풋을 직접 작성하고 있다. PHP와 XML 응답을 만들 수 있는 서버 측 언어를 위한 다양한 툴킷과 API들이 있다. XML로 응답을 생성 및 보내는 서버 측 스크립트가 어떤 모습인지를 파악할 수 있다.



Listing 8. XML을 리턴하는 PHP 스크립트


<?php

// Connect to a MySQL database
$conn = @mysql_connect("mysql.myhost.com", "username", "secret-password");
if (!conn)
die("Error connecting to database: " . mysql_error());

if (!mysql_select_db("television", $conn))
die("Error selecting TV database: " . mysql_error());

// Get ratings for all TV shows in database
$select = 'SELECT title, rating';
$from = ' FROM ratings';
$queryResult = @mysql_query($select . $from);
if (!$queryResult)
die("Error retrieving ratings for TV shows.');

// Let the client know we're sending back XML
header("Content-Type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
echo "<ratings>";

while ($row = mysql_fetch_array($queryResult)) {
$title = $row['title'];
$rating = $row['rating'];

echo "<show>
echo "<title>" . $title . "</title>";
echo "<rating>" . $rating . "</rating>";
echo "</show>";
}

echo "</ratings>";

mysql_close($conn);

?>


각자가 선호하는 서버 측 언어를 사용하여, XML을 만들 수 있다. IBM developerWorks의 많은 기술자료들에서 서버 측 언어를 사용하여 XML 문서를 만드는 방법을 설명하고 있다. (참고자료)



XML을 인터프리팅 하는 기타 옵션


XML을 다루는 한 가지 매우 일반적인 옵션들은, 포맷팅 되지 않은 텍스트로서 취급하는 것 또는 DOM을 사용하는 것 이상으로 중요하다. 이것이 바로 JSON, (JavaScript Object Notation)이고, JavaScript에 포함된 무료 텍스트 포맷이다. 향후 시리즈에서는 JSON을 다루도록 하겠다.


일반적으로, JSON을 사용하여 할 수 있는 모든 것은, DOM을 사용해서도 할 수 있다. 그 반대의 경우도 마찬가지다. 이것은 선호도의 문제이고, 특정 애플리케이션에 알맞은 방식을 선택하는 문제이다. 지금까지는 DOM을 설명했고, 서버의 응답을 받는 상황에서 이를 사용하는 방법을 익혔다. 앞으로 남은 두 건의 기술자료에서는 JSON에 대해 설명할 것이다. 두 가지 중에서 한 가지를 선택할 준비를 해야 할 것이다. 다음 기술자료를 기다려주기 바란다.



맺음말


지난 번 기술자료부터, 지금까지 거의 논스톱으로 XML에 대해 설명했다. 하지만 Ajax에 기여하는 XML의 빙산의 일각을 다루었을 뿐이다. 다음 기술자료에서는, XML을 보내는 특수한 상황에 대해 자세히 설명하겠다. (XML을 받는 상황에 대해서도 설명하겠다.) 특히, 사용 웹 서비스와 Google 같은 API 등 Ajax 인터랙션 관점으로, 웹 서비스에 대해서도 설명하겠다.


가장 중요한 것은 XML이 자신의 애플리케이션에 잘 맞는지를 생각하는 것이다. 많은 경우, 애플리케이션이 잘 작동한다면, XML은 그냥 유행어에 지나지 않고, XML을 사용해야 할 것 같은 유혹과 싸워야 한다.


서버가 보내는 데이터가 제한되어 있거나, 이상한 콤마 또는 파이프로 구분된 포맷으로 되어있다면, XML이 확실히 도움이 될 것이다. 서버 측 컴포넌트로 작업하거나 이를 변경하는 것을 고려하여, XML을 사용하여, XML만큼 강력하지 않은 상용 포맷을 사용하는 것 보다는, 표준 방식으로 응답을 리턴할 수 있다.


Ajax와 관련한 기술을 배우면 배울수록, 여러분의 결정에 신중을 더욱 기해야 한다. Web 2.0 애플리케이션을 작성하는 것은 재미있는 일이지만, 친구의 관심을 끌 요량으로 웹 페이지를 실행할 때 기술을 포기하지 않도록 조심해야 한다. 여러분이 좋은 애플리케이션을 작성할 것임을 믿고 있다. 다 만들고 나서 다음 글을 기대해주기 바란다.


by 미소 | 2007/12/30 23:42 | AJAX 기본 | 트랙백(3)

7. Ajax 마스터하기, Part 7: 요청과 응답에 XML 사용하기

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 12 월 19 일

평범한 Ajax 개발자들도 Ajax 단어에 있는 xXML.을 의미한다는 것 정도는 알고 있습니다. XML은 가장 인기 있는 데이터 포맷들 중 하나이고, 실제로, 비동기식 애플리케이션에서 서버 응답에 효력을 발휘합니다. 이 글에서, 서버가 XML로 된 응답을 보내는 방법을 설명합니다.


XML을 통하지 않고서는 중요한 프로그래밍을 수행할 수 없다. XHTML로 옮기려고 하는 웹 페이지 디자이너, JavaScript로 작업하는 웹 프로그래머, 전개 디스크립터와 데이터 바인딩을 사용하는 서버 측 프로그래머, XML 기반 데이터베이스를 분석하는 백엔드 개발자..등 이 모든 사람들은 이 확장성 있는 마크업 언어를 사용한다. 이쯤 되면, XML이 Ajax를 지탱하고 있는 핵심 기술로 간주되는 것쯤은 놀랄 일도 아니다.


하지만, Ajax 애플리케이션에서 사용되는 핵심 객체(XMLHttpRequest)의 의미를 생각해 봐야 한다. 대부분의 사람들은, XMLHttpRequest 객체가 실제로 언제나 XML을 사용한다고 생각하기 때문에, XML을 Ajax의 핵심 부분으로 생각한다. 이는 사실이 아니며, 그 이유를 밝히는 것이 이 글의 목적이다. 사실, 대부분의 Ajax 애플리케이션에서, XML이 거의 나타나지 않는다.


XML은 Ajax에서 사용되고, XMLHttpRequest는 이를 허용한다. XML은 서버로 보내질 수도 있다. 이 시리즈의 이전 기술자료들에서, 플레인 텍스트와 이름/값 매개변수들을 사용하여 데이터를 보냈지만, XML 역시도 사용 가능한 포맷이다. 이 글에서는, 그 사용 방법을 설명할 것이다. 요청 포맷으로 XML을 사용하는 이유를 설명하고, 대부분의 경우 XML을 사용하지 말아야 하는 이유를 설명하겠다.


XML: 너무나 작은 의미?


Ajax 애플리케이션과 XML의 사용에 대해 추측하기란 쉽다. 기술의 이름(Ajax)과 이것이 사용하는 핵심 객체(XMLHttpRequest)를 보면 XML이 사용되고 있음을 짐작할 수 있고, Ajax 애플리케이션과 XML을 연결 지어 말하는 이야기도 듣게 된다. 그러나, 이러한 인식은 잘못된 것이고, 특히, 비동기식 애플리케이션을 작성할 경우라면, 이러한 인식이 그릇된 것인지를 알아야 한다.


XMLHttpRequest: 의미와 HTTP


기술적인 문제에 발생할 수 있는 가장 나쁜 일은 이것인 너무 굳어져서 기본적인 부분을 변경하는 것이 불가능할 경우가 있다. Ajax 애플리케이션에서 사용되는 기본 객체인 XMLHttpRequest에서도 이 같은 일이 발생한다. 이것은 HTTP 요청을 통해서 XML을 보내거나, 일종의 XML 포맷으로 HTTP 요청을 만드는 것처럼 여겨진다. 객체 이름이 어떤 뉘앙스를 풍기든 상관없이, 이것은 실제로 클라이언트 코드(대게 웹 페이지에 있는 JavaScript)가 HTTP 요청을 보낼 수 있는 방식을 제공한다. 이것이 전부이다. 더 이상 어떤 것도 없다.


따라서, XMLHttpRequest의 이름을 HttpRequest나 간단히 Request 같이, 보다 정확한 단어로 바꾸는 것도 좋을듯하다. 하지만, 수 백만 명의 개발자들이 Ajax에 뛰어들었고, 대다수의 사용자들이 Internet Explorer 7.0이나 Firefox 1.5같은 새로운 브라우저 버전으로 이동하는데 수년이 걸린다는 알고 있기에, 이름을 변경하는 것은 사실상 불가능하다.



XMLHttpRequest를 지원하지 않는 브라우저(특히 Windows)를 다룰 때, 대체 시스템으로는 Microsoft IFRAME 객체를 사용한다. XML, HTTP, 심지어 request 라는 뉘앙스는 어디에서도 풍기지 않는다. 확실히, XMLHttpRequest 객체는 XML이나 HTTP 그 자체 보다는, 페이지 리로드 없이 요청을 할 수 있는 것 이상의 의미를 풍긴다.


요청은 XML이 아니라 HTTP이다.


자주 실수하는 부분 중에, XML이 막후에서 사용된다고 생각하는 것이다. 사실, 나도 그랬다. 하지만, 이 같은 견해는 이 기술을 잘 이해하지 못함을 증명하는 것이다. 사용자가 브라우저를 열고, 서버에서 웹 페이지를 요청할 때, http://www.google.com 또는 http://www.headfirstlabs.com를 타이핑한다. http://를 타이핑 하지 않아도 브라우저는 브라우저 주소창에 그 부분을 채울 것이다. http://는 통신이 어떻게 이루어지는지를 알려주는 단서이다. 바로 HTTP, Hypertext Transfer Protocol을 통한 통신이다. 여러분이 웹 페이지에 코드를 작성하여 서버와 통신할 때, Ajax든, 평범한 형식의 POST이든, 하이퍼링크이든 간에, 무조건 HTTP이다.



HTTPS도 HTTP이다.

웹에 생소한 사람이라면 https://intranet.nextel.com 같은 URL이 낯설 것이다. https는 보안 HTTP이고, 일반 웹 요청에 사용되는 HTTP 프로토콜에서 보다 안전한 형식을 사용하는 것뿐이다. 따라서, HTTPS에 추가 보안 레이어가 추가되었더라도 HTTP와 통신할 수 있다.



브라우저와 서버간 모든 웹 통신은 HTTP를 통해 이루어진다고 할 때, XML은 XMLHttpRequest에 의해 사용되는 전송 또는 기술이라는 개념은 이치에 맞지 않는다. XML이 HTTP 요청으로 보내질 수 있지만, HTTP는 매우 정교하게 정의된 표준이다. 요청에 XML을 명확하게 사용하거나, 서버가 XML로 된 응답을 보내지 않는 한, XMLHttpRequest 객체에서 사용되는 것은 평범하고 오래된 HTTP 뿐이다. 따라서, "막후에서 XML을 사용하기 때문에 이것이 XMLHttpRequest로 호출되었다” 라고 말하는 사람이 있다면, HTTP가 무엇인지, XML이 HTTP를 통해 보내질 수 있고, XML은 전송 프로토콜이 아닌 데이터 포맷이라는 점을 친절하게 알려줘야 한다.







XML 사용하기



지금까지는, XML이 Ajax에 사용되지 않는다는 것을 증명했다. 하지만 Ajax의 xXMLHttpRequestXML은 무엇을 말하는가? 웹 애플리케이션에서 XML을 사용할 수 있는 여러 옵션들이 있다. 이 섹션에서는 기본 옵션들을 자세히 검토해 보겠다.


XML용 옵션


비동기식 애플리케이션에서, 두 개의 기본적인 XML 애플리케이션이 있다:



  • 웹 페이지에서 서버로 웹 페이지 포맷으로 된 요청 보내기

  • 서버에서 온 요청을 XML 포맷으로 웹 페이지에서 받기


첫 번째 항목인, XML로 된 요청을 보낼 때는 요청을 XML로 포맷팅 해야 한다. API를 사용하거나 텍스트를 연결하여 그 결과를 서버로 보내는 것이다. 이 옵션에서, 기본적인 작업은 XML 규칙에 순응하는 방식으로, 그리고 서버가 이해할 수 있도록 요청을 만드는 것이다. 이 부분에서의 초점은 XML 포맷이다. 보낼 데이터가 있다면, 이를 XML 문법으로 래핑해야 한다. 이 글 나머지 부분에서는 Ajax 애플리케이션에서의 XML 사용에 대해 설명하겠다.


두 번째 옵션인, XML로 된 요청을 받을 때에는, 서버에서 응답을 가져다가, XML에서 데이터를 추출해야 한다. (API나 다른 방식을 사용한다.) 이 경우, 서버에서 온 데이터에 집중하여, XML에서 데이터를 가져와서 이를 사용한다. 이 부분은 다음 기술자료의 주제이다.


경고


XML 사용법을 자세히 설명하기 전에, 한가지 주의를 주고 싶다. XML은 작고, 빠르며, 공간 절약형 포맷이 아니다. 다음 여러 섹션들과 다음 기술자료에서 보겠지만, 어떤 정황에서 XML을 사용해야 하는 몇 가지 이유가 있고, XML이 플레인 텍스트 요청과 응답보다 더 나은 점도 갖고 있다. 하지만, XML은 플레인 텍스트 보다 더 많은 공간을 차지하고, 더 느리다. XML에 필요한 포든 태그와 문법을 메시지에 추가해야 하기 때문이다.


매우 빠른 애플리케이션을 작성하고 싶다면, XML은 올바른 선택이 아니다. 플레인 텍스트로 시작하고, 특정 부분에서 XML이 필요할 경우, 그때 XML을 선택하는 것이 좋다. 하지만, 처음부터 XML을 사용한다면, 애플리케이션의 반응이 늦어진다. 대부분의 경우, 다음과 같이 텍스트를 XML로 전환하는 것 보다는 name=jennifer 같은 이름/값 쌍을 사용하여 플레인 텍스트를 보내는 것이 더 빠르다:




<name>jennifer</name>



XML을 사용하면 시간이 오래 걸리는 모든 상황을 생각해 보자. 텍스트를 XML로 래핑(wrap)할 때, 추가 정보(실제 요청의 일부인 주변 엘리먼트, XML 헤더 등은 포함하지 않았다.)를 통해 보낼 때, 서버에서 XML을 파싱하고, 응답을 만들고, XML로 응답을 래핑하고, 이것을 다시 웹 페이지로 보낼 때 페이지에서 응답을 파싱하여 이를 사용할 때 등이 있다. 따라서, XML을 사용해야 할 때를 배우고, 많은 상황에서 애플리케이션을 빠르게 실행하려는 생각으로 시작해서는 안된다.





XML을 클라이언트에서 서버로


포맷으로서 XML을 사용하여 클라이언트에서 서버로 데이터를 보내는 방법을 보자. 우선, 기술적인 관점에서 그 방법을 규명하고, 어떤 것이 좋은 생각인지를 검토해보자.


이름/값 쌍 보내기


여러분이 작성하는 웹 애플리케이션의 90퍼센트 정도가 이름/값 쌍을 서버로 보낸다. 예를 들어, 사용자가 이름과 주소를 웹 페이지의 폼에 입력하면, 폼에서 다음과 같은 데이터를 얻는다:




firstName=Larry
lastName=Gullahorn
street=9018 Heatherhorn Drive
city=Rowlett
state=Texas
zipCode=75080



서버로 보낼 데이터에 플레인 텍스트를 사용하면, Listing 1과 같은 코드를 사용한다. (본 시리즈의 첫 번째 글에 사용했던 예제와 비슷하다. (참고자료)



Listing 1. 플레인 텍스트로 된 이름/값 쌍 보내기


function callServer() {
// Get the city and state from the Web form
var firstName = document.getElementById("firstName").value;
var lastName = document.getElementById("lastName").value;
var street = document.getElementById("street").value;
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
var zipCode = document.getElementById("zipCode").value;

// Build the URL to connect to
var url = "/scripts/saveAddress.php?firstName=" + escape(firstName) +
"&lastName=" + escape(lastName) + "&street=" + escape(street) +
"&city=" + escape(city) + "&state=" + escape(state) +
"&zipCode=" + escape(zipCode);

// Open a connection to the server
xmlHttp.open("GET", url, true);

// Set up a function for the server to run when it's done
xmlHttp.onreadystatechange = confirmUpdate;

// Send the request
xmlHttp.send(null);
}



이름/값 쌍을 XML로 변환하기


다음과 같이, 데이터용 포맷으로서 XML을 사용하고 싶다면, 데이터를 저장한 기본적인 XML 포맷을 따라야 한다. 이름/값 쌍은 모두 XML 엘리먼트로 바뀔 수 있고, 여기에서 엘리먼트 이름은 그 쌍에서 이름이 되고, 엘리먼트의 콘텐트는 값이 된다:




<firstName>Larry</firstName>
<lastName>Gullahorn</lastName>
<street>9018 Heatherhorn Drive</street>
<city>Rowlett</city>
<state>Texas</state>
<zipCode>75080</zipCode>



물론, 여러분은 루트 엘리먼트를 갖거나, 단순히 문서 조각 (XML 문서의 일부)으로 작업하는 것이라면, 인클로징(enclosing) 엘리먼트가 필요하다. 위 XML을 다음과 같이 변환한다:




<address>
<firstName>Larry</firstName>
<lastName>Gullahorn</lastName>
<street>9018 Heatherhorn Drive</street>
<city>Rowlett</city>
<state>Texas</state>
<zipCode>75080</zipCode>
</address>



이제 웹 클라이언트에 구조를 만들고, 이것을 서버로 보낼 준비가 얼추 되었다.


구두상의 통신


네트워크를 통해서 XML을 전달하기 전에, 데이터를 보낼 서버, 그리고 스크립트가 XML을 실제로 받아들이는지를 확인해야 한다. 물론 여러분에게는 이렇게 말하는 것 자체가 미안하지만, 신참 프로그래머들은 네트워크를 통해서 XML을 보내면 올바르고 받아서 인터프리팅 되는 것으로 착각하기도 한다.


여러분이 보내는 XML로 된 데이터가 올바르게 수신되도록 하기 위해서는 두 단계를 거쳐야 한다:



  1. 전송할 스크립트가 데이터 포맷으로서 XML을 수락하는지를 확인한다.

  2. 스크립트가 여러분이 보내는 데이터의 특정 XML 포맷과 구조를 수락하는지를 확인한다.


이 두 단계 모두 사람들과 실제로 이야기 해봐야 한다. XML로 데이터를 보내는 것이 중요한 문제라면, 스크립트 라이터는 여러분에게 그렇게 할 것을 명령할 것이다. XML을 받아들일 스크립트를 찾는 것은 어려운 일이 아니다. 하지만, 여러분이 가진 포맷이 스크립트가 기대하고 있는 것과 맞는지를 확인해야 한다. 예를 들어, 서버가 다음과 같은 데이터를 받아들인다고 하자:




<profile>
<firstName>Larry</firstName>
<lastName>Gullahorn</lastName>
<street>9018 Heatherhorn Drive</street>
<city>Rowlett</city>
<state>Texas</state>
<zip-code>75080</zip-code>
</profile>



두 가지를 제외하고는 위 XML과 비슷해 보인다:



  1. 클라이언트에서 온 XML은 address 엘리먼트 내에서 래핑되지만, 서버는 데이터가 profile 엘리먼트 내에서 래핑될 것을 기대하고 있다.

  2. 클라이언트에서 온 XML은 zipCode 엘리먼트를 사용하지만, 서버는 집 코드가 zip-code 엘리먼트에 있을 것을 기대하고 있다.


데이터를 수락하고 처리하는 서버와 페이지를 엉망으로 만들 서버는 이 작은 차이로 만들어진다. 따라서, 여러분은 서버가 기대하고 잇는 것이 무엇인지를 파악하고, 데이터와 포맷을 혼합해야 한다. 그리고 나서야, 클라이언트에서 서버로 XML을 보낼 수 있는 기술을 다룰 준비가 되는 것이다.


서버로 XML 보내기


서버로 XML을 보낼 때, 데이터를 실제로 전송하는 것 보다, 데이터를 취해서 이를 XML로 래핑하는데 코드 작업을 더 많이 해야 한다. 사실, 서버로 보낼 준비가 된 XML 스트링이 있다면, 다른 플레인 텍스트를 보내는 것과 똑같이 보낸다. Listing 2에서, 이것이 어떻게 작동하는지를 보자.



Listing 2. XML로 된 이름/값 쌍 보내기



function callServer() {
// Get the city and state from the Web form
var firstName = document.getElementById("firstName").value;
var lastName = document.getElementById("lastName").value;
var street = document.getElementById("street").value;
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
var zipCode = document.getElementById("zipCode").value;

var xmlString = "<profile>" +
" <firstName>" + escape(firstName) + "</firstName>" +
" <lastName>" + escape(lastName) + "</lastName>" +
" <street>" + escape(street) + "</street>" +
" <city>" + escape(city) + "</city>" +
" <state>" + escape(state) + "</state>" +
" <zip-code>" + escape(zipCode) + "</zip-code>" +
"</profile>";

// Build the URL to connect to
var url = "/scripts/saveAddress.php";

// Open a connection to the server
xmlHttp.open("POST", url, true);

// Tell the server you're sending it XML
xmlHttp.setRequestHeader("Content-Type", "text/xml");

// Set up a function for the server to run when it's done
xmlHttp.onreadystatechange = confirmUpdate;

// Send the request
xmlHttp.send(xmlString);
}



코드만 봐도 충분히 설명되지만, 몇 가지 중요한 포인트가 있다. 우선, 요청 데이터는 XML로 직접 포맷팅 되어야 한다. Document Object Model을 사용하는 방법을 설명한 세 개의 기술자료를 읽어서 그런지 약간 실망한 것 같다. JavaScript를 사용하는 XML 문서를 만들기 위해 DOM을 사용하지 말란 법은 없지만, GET 또는 POST 요청으로 네트워크를 통해서 이것을 보내기 전에 DOM 객체를 텍스트로 변환해야 한다. 따라서, 일반적인 스트링 조작을 통해 데이터를 포맷팅 하는 것이 훨씬 더 쉽다. 물론, 에러와 타이핑 실수가 있을 수 있으므로, XML을 다루는 코드를 작성할 때 특별히 주의를 기울여야 한다.


XML을 구현하면, 텍스트를 보낼 때와 거의 같은 방법으로 연결을 구축한다. 나는 XML에 POST 요청을 사용하는 것을 더 좋아한다. 어떤 브라우저는 GET 쿼리 스트링에 길이 제한을 두고, 게다가 XML은 길이가 매우 길어질 수 있기 때문이다. 따라서 Listing 2는 GET에서 POST로 변환되었다. 게다가, 여러분이 요청한 URL의 끝에 고정된 매개변수로서가 아닌, send() 메소드를 통해 XML이 보내진다. 이 모두가 매우 사소한 차이이지만 조절하기 쉽다.


하지만, 완전히 새로운 코드를 작성해야 한다:




xmlHttp.setRequestHeader("Content-Type", "text/xml");



이해하기 어렵지 않다: 평이한 이름/값 쌍이 아닌 XML로 보낼 것을 서버에 명령하고 있다. 두 경우 모두, 데이터를 텍스트로 보내지만, 여기에서는 text/xml을 사용하거나 플레인 텍스트로 보내진 XML을 보낸다. 이름/값 쌍을 사용한다면, 이 라인은 다음과 같이 읽힌다:




xmlHttp.setRequestHeader("Content-Type", "text/plain");



XML로 보낼 것을 서버에 명령하지 않으면 문제가 생길 수 있다. 그러므로 주의하기 바란다.


여기까지 다 했다면, 이제는 send()를 호출하고 XML 스트링으로 패스해야 한다. 서버는 XML 요청을 받게 되고, (여러분이 사전 작업을 수행한 상태라면) XML을 수락하여, 이를 파싱하고, 응답을 보낸다. 몇 가지 코드가 변경된 XML 요청이 전부이다.





XML 보내기: 좋은 것인가, 나쁜 것인가?


XML 요청 전에, 요청에 XML을 사용하는 문제에 대해 생각해 보자. 앞서 언급했듯이, XML은 전송의 관점에서 볼 때 가장 빠른 데이터 포맷일 뿐, 그 이상도 이하도 아니다.


XML은 구현이 간단하지 않다.


요청에 사용할 XML은 구성하기가 쉽지 않다. Listing 2에서 보듯, 데이터는 XML 문법으로 인해 빠르게 복잡해진다:




var xmlString = "<profile>" +
" <firstName>" + escape(firstName) + "</firstName>" +
" <lastName>" + escape(lastName) + "</lastName>" +
" <street>" + escape(street) + "</street>" +
" <city>" + escape(city) + "</city>" +
" <state>" + escape(state) + "</state>" +
" <zip-code>" + escape(zipCode) + "</zip-code>" +
"</profile>";



그렇게 심각한 문제는 아니지만, XML은 단 여섯 개의 필드를 갖고 있다. 여러분이 개발 할 대부분의 웹 폼들은 10개에서 15개의 필드를 갖고 있다. 모든 요청에 Ajax를 사용하지 않더라도, 중요한 문제이다. 실제 데이터만큼 많은 대괄호와 태그 이름들을 처리하는데 많은 시간을 허비해야 하고, 오타의 가능성도 크다.


또 다른 문제는 XML을 직접 구현해야 한다는 점이다. DOM 객체를 요청으로 보낼 수 있는 스트링으로 전환하는 것은 간단한 방법이 아니므로, DOM을 사용하는 것은 좋지 않다. 따라서, 이와 같이 스트링으로 작업하는 것이 최고의 방법이다. 하지만, 관리하기 어렵고, 새로운 개발자들이 이해하기 힘든 방식이기도 하다. 이 경우, 한 줄로 모든 XML을 만들었다. 여러 단계에 걸쳐 이를 수행하면 상황은 더 복잡해진다.


XML은 요청에 어떤 것도 추가하지 않는다.


복잡성 문제 외에도, 요청에 XML을 사용하면 플레인 텍스트와 이름/값 쌍 보다 더 나은 점도 없다. 이 글의 초점은, 이름/값 쌍을 사용하여(Listing 1) 보냈던 데이터를, XML을 사용하여 보내는 것에 맞춰져 있다. 플레인 텍스트를 사용하여 보낼 수 없는 것을 XML을 사용하여 보낼 수 있다라고 말하는 것이 아니다. 플레인 텍스트를 사용하여 보낼 수 없는 것을 XML을 사용한다고 해서 가능해지는 것은 결코 아니기 때문이다.


이것이 XML과 요청의 본질이다. 여기에 중요한 이유는 없다. 다음 글에서는, 플레인 텍스트를 사용할 때 다루기 어려웠던 몇 가지 사안들을 XML로 해결하는 방법에 대해 설명하겠다. 오직 XML만 받아들일 것을 스크립트에 명령하지 않을 것이라면, 거의 모든 요청 상황에서 플레인 텍스트를 사용하는 것이 더 낫다.





맺음말


Ajax에서 XML의 의미에 대해 감이 잡혔으리라 생각한다. Ajax 애플리케이션은 XML을 사용할 필요가 없고, XML은 데이터 전송에 있어서 만병 통치약도 아니라는 것도 이제 알게 되었다. 웹 페이지에서 서버로 XML을 보내는 것에도 익숙해졌을 것이다. 서버가 요청을 처리하고 응답을 보낼 때 어떤 것들이 개입되어 있는지도 알게 되었다. 서버 스크립트가 XML을 허용하도록 하고, 데이터를 보내려면 여러분이 사용하는 포맷으로 이를 수락해야 한다.


요청용 데이터 포맷에 XML이 좋은 선택이 아니라는 이유도 알았다. 다음 글에서는, XML이 효과적인 경우와, 대부분의 요청에서는 모든 것을 느려지게 만들고, 복잡해진다는 사실을 알게 될 것이다. 이 글에서 배운 것을 직접 실행에 옮겨 보고, 매우 신중히 수행할 것을 말해주고 싶다. XML 요청은 Ajax 애플리케이션에서 역할이 있지만, 그 역할은 여러분이 생각하는 것만큼 크지 않다.


다음 글에서는, XML을 사용하여 서버가 응답하는 방법, 웹 애플리케이션이 응답을 처리하는 방법을 설명하겠다. 서버가 XML을 웹 애플리케이션으로 돌려보내는 경우가 많기 때문에 자세한 기술적인 부분들까지 배우게 될 것이다. 지금까지는, 요청을 보낼 때 XML이 좋은 선택이 아닌지를 이해하는 것으로 충분하다. 요청용 데이터 포맷으로서 XML을 사용하여 웹 애플리케이션을 구현하고, 이를 다시 플레인 텍스트로 변환하여, 어떤 것이 더 빠르고 쉬운지를 파악할 수 있다. 다음 글을 기대해주기 바란다.

by 미소 | 2007/12/30 23:31 | AJAX 기본 | 트랙백

6. Ajax 마스터하기, Part 6: DOM-기반 웹 애플리케이션 구현하기

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 12 월 12 일

Document Object Model (DOM)와 JavaScript 코드를 결합하여 인터랙티브 Ajax 애플리케이션을 구현해봅시다. 이전 글에서는, DOM 프로그래밍의 개념과 웹 브라우저가 어떻게 웹 페이지를 트리로서 보는지를 설명했습니다. 이제는 DOM에 사용되는 프로그래밍 구조를 이해할 차례입니다. 여러분이 배운 모든 것을 실제로 적용하여, 간단한 웹 페이지를 구현해 봅시다. 웹 페이지의 모든 효과들은 JavaScript를 사용하여 구현됩니다.


Document Object Model 또는 DOM을 소개한 두 개의 기술자료가 있다. 이제는 DOM이 어떻게 작동하는지 잘 알 수 있다. (참고자료) 이 글에서는 우리가 이해한 것을 실행에 옮길 차례이다. 사용자 액션에 기반하여 변하는 사용자 인터페이스를 가진 기본적인 웹 애플리케이션을 개발할 것이다. 물론, DOM을 사용하여 인터페이스도 변경할 것이다. 이 글을 다 마치면, DOM 기술과 개념을 실전에 적용할 정도의 실력을 갖추게 될 것이다.


지난 두 개의 기술자료를 다 읽었을 것이라고 간주하겠다. 그렇지 않다면, 잘 읽어보고 DOM과 웹 브라우저가 HTML과 CSS를 웹 페이지를 나타내는 하나의 트리 구조로 전환하는 방법을 이해하기 바란다. 지금까지, 내가 이야기했던 모든 DOM 원리들은 단순한 DOM 기반 동적 웹 페이지를 구현하는데 사용될 것이다. 이 글을 읽다가 조금이라도 이해가 되지 않는 부분이 있다면 이전 기술자료들을 다시 읽어보기 바란다.


샘플 애플리케이션으로 시작하기



코드에 대하여

DOM과 JavaScript 코드에 집중하기 위해, 인라인 스타일(예를 들어, h1p 엘리먼트에 대한 align 애트리뷰트 같은)로 HTML을 작성했다. 이렇게 하는 것도 좋지만, 시간을 들여서 여러분의 모든 스타일을 여러분이 개발하는 실행 애플리케이션용 외부 CSS 스타일시트에 두기를 권하고 싶다.



매우 기본적인 애플리케이션에 작은 DOM 매직을 추가해 보자. DOM이 폼을 제출하지 않고 웹 페이지 상에서 이동할 수 있다는 것을 유념하고(이것은 Ajax에 확실히 대조되는 부분이다.) 뒤집어진 모자와 Hocus Pocus!라는 레이블 버튼만 보여주는 페이지를 만들어 보자.


첫 HTML



Listing 1은 이 페이지에 대한 HTML이다. 헤딩과 폼을 가진 바디와 단순한 이미지와 누를 수 있는 버튼이 있을 뿐이다.



Listing 1. 샘플 애플리케이션용 HTML


<html>
<head>
<title>Magic Hat</title>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



아래 다운로드 섹션에서는, 이 글에 사용되는 모든 이미지와 HTML이 제공된다. 하지만, 이미지만 다운로드하고, 내가 이 글에서 구현한 애플리케이션 샘플을 보면서 여러분이 직접 HTML을 작성하길 바란다. 이렇게 하면 DOM 코드를 훨씬 더 잘 이해할 수 있을 것이다.


샘플 웹 페이지 보기


여기에는 트릭이 전혀 없다. 페이지를 열면 그림 1과 같은 모습이 보인다.




그림 1. 평범한 모자

A rather boring-looking top hat


HTML의 마지막 포인트


여러분이 기억해야 할 한 가지 중요한 포인트는 Listing 1그림 1의 폼에 있는 버튼이 제출 버튼이 아닌 button 유형이라는 것이다. 제출 버튼을 사용할 경우, 버튼을 누르면 브라우저가 폼을 제출하게 된다. 물론, 이 폼은 어떤 action 애트리뷰트(완전히 의도적인)가 없기 때문에, 이것은 어떤 액티비티도 없는 무한 루프만을 만들게 된다. (직접 실험해 봐도 좋다.) 정상적인 인풋 버튼을 사용하고, 제출 버튼을 피하면, JavaScript 함수를 버튼에 연결하고 폼을 제출하지 않고 브라우저와 인터랙팅 한다.







샘플 애플리케이션에 더 많은 것 추가하기


이제 JavaScript, DOM 조작, 이미지 마법사를 가진 웹 페이지로 꾸며보자.


getElementById() 함수 사용하기


마법사 모자에는 토끼가 빠지면 안되는 법이다. 이 경우, 기존 페이지(그림 1에 있는 이미지를 토끼 이미지(그림 2)로 바꾼다.




그림 2. 똑 같은 모자에 토끼가 생겼다.

The same top hat, this time with a rabbit


DOM에 트릭을 추가하는 첫 번째 단계에는 웹 페이지에 img 엘리먼트를 나타내는 DOM 노드를 찾는 것도 포함된다. 일반적으로, 가장 쉬운 방법은 getElementById() 메소드를 사용하는 것이다. 이는 웹 페이지를 나타내는 document 객체에 대해 사용할 수 있다. 여러분은 전에 이 메소드를 보았다. 다음과 같이 작동한다.




var elementNode = document.getElementById("id-of-element");



HTML에 id 애트리뷰트 추가하기


이것은 매우 기본적인 JavaScript이지만, HTML에서는 몇 가지 작업이 필요하다. id 애트리뷰트를 액세스 하고자 하는 엘리먼트에 추가한다. 이것은 대체하고자 하는 img 엘리먼트이다. (토끼를 포함하고 있는 새로운 이미지이다.) 따라서 Listing 2처럼, HTML을 변경해야 한다.



Listing 2. id 애트리뷰트 추가하기



<html>
<head>
<title>Magic Hat</title>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



페이지를 리로드(reload)하면, 아무런 변화도 볼 수 없다. id 애트리뷰트를 추가한다고 해서 웹 페이지에 시각적인 효과가 생기지 않는다. 하지만, JavaScript와 DOM을 보다 쉽게 사용하여 엘리먼트로 작업할 수 있다.


img 엘리먼트


이제 getElementById()를 쉽게 사용할 수 있다. 원하는 엘리먼트의 아이디인 -- topHat-- 이 생겼고, 새로운 JavaScript 변수에 이것을 저장할 수 있다. Listing 3의 코드를 HTML 페이지에 추가한다.



Listing 3. img 엘리먼트에 액세스 하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



이 지점에서 웹 페이지를 로딩 또는 재로딩 한다 해도 별다른 차이를 볼 수 없다. 이 이미지에 액세스 하더라도 어던 것도 할 수 없다.







이미지 변경하기: 어려운 길


두 가지 방법으로 변경할 수 있다. 어려운 길과 쉬운 길이 있다. 모든 프로그래머가 그렇겠지만, 쉬운 방법을 선호한다. 하지만, 어려운 방법일 수록 DOM을 연습하는 최상의 방법이다. 우선 어려운 방법으로 이미지를 변경하는 방법부터 살펴보자. 나중에, 더 쉬운 방법으로 같은 작업을 하는 방법도 설명하겠다.


다음은 기존 이미지를 새로운, 토끼가 있는 이미지로 대체하기 위해서는;



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 즉, 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 기존 img 엘리먼트 바로 전에 컨테이너의 자식으로서 새로운 img 엘리먼트를 삽입한다.

  4. 이전 img 엘리먼트를 제거한다.


  5. 사용자가
    Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.


새로운 img 엘리먼트 만들기


여러분도 기억하겠지만, DOM의 핵심은 document 객체이다. 이것은 전체 웹 페이지를 나타내고, getElementById() 같은 강력한 메소드로 액세스 할 수 있도록 해주며, 새로운 노드를 만들 수 있도록 한다. 이것이 바로 지금 사용 할 마지막 속성이다.


새로운 img 엘리먼트를 만들어야 한다. 돔에서, 모든 것은 노드이지만, 노드들은 세 개의 기본적인 그룹으로 나뉜다:



  • 엘리먼트

  • 애트리뷰트

  • 텍스트 노드


다른 그룹들도 있지만 이 세 가지가 99퍼센트를 차지한다. 이 경우, img 유형의 새로운 엘리먼트가 필요하다. 따라서, JavaScript에 이 코드가 필요하다:




var newImage = document.createElement("img");



이것은 img라는 엘리먼트 이름을 가진 element 유형의 새로운 노드를 만든다. HTML에서는 다음과 같은 것이 된다:


<img />


DOM은 잘 구성된 HTML을 만들 것이고, 엘리먼트는 시작 태그와 엔딩 태그와 함께, 현재 비어있다. 이 엘리먼트에 콘텐트와 애트리뷰트를 추가하고, 이를 웹 페이지에 삽입한다.


콘텐트의 경우, img 엘리먼트는 빈 엘리먼트이다. 하지만 애트리뷰트를 추가해야 한다: src 애트리뷰트는 로딩 할 이미지를 지정한다. 여기에서 addAttribute() 같은 메소드를 사용해야 한다고 생각하겠지만, 틀렸다. DOM 스팩 작성자는, 프로그래머들은 지름길(shortcut)을 좋아한다고 생각했기 때문에, 새로운 애트리뷰트를 추가하고 기존 애트리뷰트의 값을 변경할 수 있는 하나의 메소드인 setAttribute()를 만들었다.



setAttribute()를 호출하고 기존 애트리뷰트를 제공한다면, 값은 여러분이 제공한 값으로 변한다. 하지만, setAttribute()를 호출하고 존재하지 않는 애트리뷰트를 제공하면, DOM은 여러분이 제공한 값을 사용하여 애트리뷰트를 추가한다. 한 가지 메소드로 두 가지 목적을 달성했다. 따라서, 다음을 JavaScript에 추가해야 한다.




var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");



이것은 이미지를 만들고, 소스를 알맞게 설정한다. 이제 여러분의 HTML은 Listing 4와 같을 것이다.



Listing 4. DOM을 사용하여 새로운 이미지 만들기


<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



이 페이지를 로딩할 수 있지만, 어떤 액션도 기대할 수는 없다. 실제 웹 페이지에 효과를 줄 어떤 것도 수행하지 않았다. step 5를 돌아보면, JavaScript 함수가 아직 호출되지도 않았다는 것을 알 수 있다.


원래 이미지의 부모에 액세스 하기


이미지를 삽입할 준비가 되었으니, 이를 어딘가에는 삽입해야 한다. 하지만, 기존 이미지에 삽입하지 않는다. 대신, 이것을 기존 이미지 앞에 두고, 기존 이미지를 제거한다. 이렇게 하려면, 기존 이미지의 부모가 필요하다. 이는 삽입과 제거의 핵심이다.


DOM은 웹 페이지를 노드의 계층인 트리로 간주한다. 모든 노드는 부모를 갖고 있고, 부모에는 자식 노드들이 있다. 이 이미지의 경우, 어떤 자식들도 없다. 이미지가 비어있는 엘리먼트라는 것을 기억하는가? 하지만 확실히 부모는 있다. 부모가 어떤 것인지에 대해서 신경 쓰지 않는다. 다만 여기에 액세스 할 뿐이다.


이렇게 하려면 모든 DOM 노드가 갖고 있는 parentNode속성을 사용한다:




var imgParent = hatImage.parentNode;



아주 간단하다. 부모는 자식들을 가질 수 있다는 것을 알고 있다. 이미 하나를 갖고 있다. 바로 기존 이미지이다. 부모가 div 인지 p 인지, 또는 페이지의 body 인지 알 필요가 없다; 전혀 문제가 되지 않는다!


새로운 이미지 삽입하기


오랜 이미지의 부모가 있으므로, 새로운 이미지를 삽입할 수 있다. 매우 쉽다. 자식을 추가 할 여러 메소드를 사용할 수 있기 때문이다:




  • insertBefore(newNode, oldNode)


  • appendChild(newNode)


오래된 이미지가 있는 곳에 새로운 이미지가 나타나도록 할 것이기 때문에 insertBefore()가 필요하다. (removeChild() 메소드도 사용해야 한다.) 다음은 기존 이미지 앞에 새로운 이미지 엘리먼트를 사용할 JavaScript 라인이다:




var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);



오래된 이미지의 부모는 두 개의 자식 이미지를 갖고 있다: 새로운 이미지 바로 다음에 오랜 이미지가 따라온다. 이러한 이미지들 주위에 있는 콘텐트는 변하지 않고, 콘텐트의 순서는 삽입되기 전과 똑같다. 이제 부모는 오랜 이미지 바로 앞에 새로운 이미지를 더 갖게 된다.


오랜 이미지 제거하기


이제 제거해야 하는 것은 오랜 이미지이다. 웹 페이지에 새로운 이미지만 필요하다. 이 샘플에서, 이미 오랜 이미지 엘리먼트의 부모가 있다. removeChild()를 호출하고 제거하기 원하는 노드로 전달한다:




var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);
imgParent.removeChild(hatImage);



여기에서, 오랜 이미지를 새로운 이미지로 대체해야 한다. HTML은 Listing 5처럼 된다.



Listing 5. 오랜 이미지를 새로운 이미지로 대체하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);
imgParent.removeChild(hatImage);
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



JavaScript 연결하기


마지막 단계이자, 가장 쉬운 단계는 HTML 폼을 여러분이 작성한 JavaScript 함수로 연결하는 것이다. 사용자가 Hocus Pocus! 버튼을 클릭할 때마다 실행 될 showRabbit() 함수가 필요하다. onClick 이벤트 핸들러를 HTML에 추가한다:




<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />



JavaScript 프로그래밍에서, 이는 매우 일상적인 일이다. 이것을 HTML 페이지에 추가하고, 페이지를 저장하고, 페이지를 웹 브라우저로 로딩한다. 이 페이지는 처음에는 그림 1처럼 보인다; Hocus Pocus!를 클릭하면 그림 3과 같이 된다.




그림 3. 토끼가 등장하다!

The rabbit has come out to play







이미지 변경하기: 보다 쉬운 방법


이미지를 변경할 때 여러 가지 방법을 사용할 수 있다고 배웠다. replaceNode() 메소드를 사용하여 하나의 노드를 다른 노드로 대체할 수 있다. 다음과 같은 순서로 실행한다:



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 기존 img 엘리먼트 바로 전에 컨테이너의 자식으로서 새로운 img 엘리먼트를 삽입한다.

  4. 이전 img 엘리먼트를 제거한다.

  5. 사용자가 Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.



replaceNode()를 사용하면, 위 단계들을 줄일 수 있다. 3단계와 4 단계를 통합하여 다음과 같이 수행한다.



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 오랜 img 엘리먼트를 새로운 것으로 대체한다.

  4. 사용자가 Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.


대단한 것 같지는 않지만, 코드가 확실히 간단해 졌다. Listing 6은 변경 방법이다: insertBefore()removeChild() 메소드 호출을 제거했다.



Listing 6. 오랜 이미지를 새로운 이미지로 대체하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
var imgParent = hatImage.parentNode;
imgParent.replaceChild(newImage, hatImage);
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />
</p>
</form>
</body>
</html>



큰 변화가 없어 보이지만, DOM 코딩에서는 다소 중요한 포인트이다. 여러분은 주어진 태스크를 수행할 때 몇 가지 방법을 모색한다. DOM 메소드를 신중히 검토한다면, 네 개 또는 다섯 단계를 두 단계 또는 세 단계로 줄일 수 있다.







이미지 변경하기: 가장 쉬운 방법


태스크를 수행하는데 더 쉬운 방법은 늘 있다고 말해왔기 때문에, 이제는 이미지를 변경하는 가장 쉬운 방법을 설명하겠다. 어떤 방법일지 알 수 있겠는가? 힌트는 애트리뷰트이다.


이미지 엘리먼트는 src 애트리뷰트로 제어된다. 이는 어딘가에 있는(로컬 URI 또는 외부 URL) 파일을 참조한다. 지금까지, 이미지 노드를 새로운 이미지로 대체했다. 하지만, 기존 이미지의 src 애트리뷰트를 변경하는 것이 훨씬 쉬운 방법이다! 새로운 노드를 만들고, 부모를 찾고, 오랜 노드를 대체하는 작업을 한 단계로 줄인다:




hatImage.setAttribute("src", "rabbit-hat.gif");



이것이 끝이다. Listing 7에서, 전체 웹 페이지의 정황에서 이 해법을 볼 수 있다.



Listing 7. src 애트리뷰트 변경하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />
</p>
</form>
</body>
</html>



이것은 DOM의 가장 훌륭한 측면이다. 애트리뷰트를 업데이트 할 때, 웹 페이지도 즉시 변한다. 이미지가 새로운 파일을 가리키면, 브라우저는 그 파일을 로딩하고, 페이지가 업데이트 된다. 리로딩이 필요 없고, 새로운 이미지 엘리먼트를 만들 필요가 없다. 결과는 그림 3과 동일하다. -- 단지 코드만 단순해졌을 뿐이다.







토끼 숨기기


현재, 웹 페이지는 너무 단순하고 엉성하다. 토끼가 모자에서 나왔지만, 스크린 밑에 있는 버튼은 여전히 Hocus Pocus!이다. 토끼가 나왔는데도 showRabbit()을 호출하는 형국이다. 토끼가 나온 후에도 버튼을 클릭하면, 프로세싱 시간만 낭비하는 것이다. 이것은 불필요한 것이다. DOM을 사용하면. 토끼가 안에 있거나 밖으로 나왔을 경우 유용한 버튼을 만들 수 있다.


버튼 레이블 변경하기


가장 쉬운 방법은 사용자가 클릭을 한 후에 버튼의 레이블을 변경하는 것이다. 이렇게 한다고 해서, 더 이상의 마법이 발생하지 않는 것은 아니다. 웹 페이지에서 발생할 수 있는 최악의 상황은 정확하지 않은 어떤 것을 삽입하는 것이다. 버튼 레이블을 변경하기 전에, 노드에 액세스 해야 한다. 그 이전에, 버튼을 참조할 ID가 필요하다. 그것이 바로 오랜 모자이다. Listing 8은 버튼에 id 애트리뷰트를 추가하는 모습이다.



Listing 8. id 애트리뷰트 추가하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



JavaScript의 버튼에 액세스 하는 것은 쉽다:




function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
}



물론, JavaScript의 다음 라인에 버튼 레이블의 값을 변경하기 위해 타이핑을 했을 것이다. setAttribute()가 작동한다:




function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Get back in that hat!");
}



이렇게 간단한 DOM 조작으로, 버튼의 레이블은 토끼가 나타나자마자 변한다. HTML과 완료된 showRabbit() 함수는 Listing 9처럼 보인다.



Listing 9. 완료된 웹 페이지



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
button.setAttribute("value", "Get back in that hat!");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



토끼를 다시 집어넣기


새로운 버튼 레이블을 보면 알겠지만, 토끼를 다시 모자 속으로 집어넣어야 한다. 토끼를 나타내게 했던 프로세스를 거꾸로 실행하면 된다. 이미지의 src 애트리뷰트를 오랜 이미지로 바꾼다. 새로운 JavaScript 함수를 만들어야 한다:




function hideRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "topHat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Hocus Pocus!");
}




showRabbit() 함수가 했던 모든 것을 역으로 하면 된다. 이미지를 토끼가 없었던 모자 이미지로 설정하고, 버튼을 그랩(grab)하고, 레이블을 다시 Hocus Pocus!로 바꾼다.







이벤트 핸들러 다루기


샘플 애플리케이션에 한 가지 큰 문제가 생겼다. 버튼 레이블이 변하더라도, 버튼을 클릭할 때 발생하는 액션은 변하지 않았다. 다행히, DOM을 사용하면 사용자가 버튼을 클릭할 때, 이벤트(발생하는 액션)를 변경할 수 있다. 버튼이 Get back in that hat!,을 읽으면, 클릭될 때 hideRabbit()을 실행한다. 토끼가 숨겨지면, 버튼은 showRabbit()을 실행하는 것으로 리턴된다.



addEventHandler() 피하기


onclick 속성 이외에도, onClick이나 onBlur 같은 이벤트 핸들러를 추가하는데 사용되는 메소드가 있다; 바로 addEventHandler()이다. 하지만, Microsoft™ Internet Explorer™는 이 메소드를 지원하지 않으므로, JavaScript에서 이를 사용한다면, 수많은 Internet Explorer 사용자들은 에러 밖에 얻을 것이 없다. 이 메소드를 사용하지 않도록 한다. 이 글에서는 이 방식으로 같은 결과를 얻을 수 있지만, Internet Explorer에서는 그럴 수 없다.



HTML을 보면, 여기에서 여러분이 다루는 이벤트가 onClick이라는 것을 알 수 있다. JavaScript에서, 이 이벤트는 버튼의 onclick 속성을 사용하여 이벤트를 참조할 수 있다. (HTML에서, 이 속성은 onClick으로 참조된다. 대문자 C라는 것을 주목하라. JavaScript에서는 소문자 c를 사용한 onclick 이다.) 따라서, 버튼에 대해 실행되는 이벤트를 변경할 수 있다: 새로운 함수를 onclick 속성으로 할당하면 된다.



onclick 속성에는 함수의 스트링 이름이 아닌 함수 레퍼런스가 입력되어야 한다. 하지만 함수 자체에 대한 레퍼런스이다. JavaScript에서는 함수를 괄호가 없는 이름으로 참조할 수 있다. 따라서 버튼이 클릭될 때 실행되는 함수를 다음과 같이 변경할 수 있다:




button.onclick = myFunction;



HTML에서, 변경은 매우 단순하다. Listing 10에서, 버튼 실행 함수를 보자.



Listing 10. 버튼의 onClick 함수 변경하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Get back in that hat!");
button.onclick = hideRabbit;
}

function hideRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "topHat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Hocus Pocus!");
button.onclick = showRabbit;
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



이제 DOM 애플리케이션이 완성되었다. 직접 시험해 보기 바란다!







맺음말


이제는 DOM에 어느 정도 익숙해 졌으리라 믿는다. 이전 글에서는, DOM의 기본적인 개념과 API에 대해 설명했다. 오늘은 단순한 DOM 기반 애플리케이션을 설명했다. 이 글을 다시 복습하기 바란다.


이것으로 본 시리즈에서 Document Object Model을 다루는 일은 더 이상 없겠지만, 여러분의 임무는 끝나지 않았다. 사실, 여러분은 DOM을 사용하지 않고 Ajax와 JavaScript로 훨씬 더 많은 작업을 해야 할 상황에 처하게 될 것이다. 복잡한 하이라이팅과 움직임 효과를 만들든지, 텍스트 블록이나 이미지로 작업하든지 간에, DOM 방식을 사용하면 매우 쉽게 수행할 수 있다.


DOM을 사용하는 것이 아직 어색하다면, 세 개의 기술자료들을 복습하기 바란다. 나머지 기술자료에서는 DOM에 대해서는 자세히 설명하지 않을 것이다. DOM을 사용하고, DOM 기반 애플리케이션을 직접 작성해보면 여러분도 데이터 포맷에 좀더 익숙해 질 것이다.


by 미소 | 2007/12/30 23:26 | AJAX 기본 | 트랙백

5. Ajax 마스터하기, Part 5: DOM 다루기

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 12 월 12 일

Document Object Model (DOM)와 JavaScript 코드를 결합하여 인터랙티브 Ajax 애플리케이션을 구현해봅시다. 이전 글에서는, DOM 프로그래밍의 개념과 웹 브라우저가 어떻게 웹 페이지를 트리로서 보는지를 설명했습니다. 이제는 DOM에 사용되는 프로그래밍 구조를 이해할 차례입니다. 여러분이 배운 모든 것을 실제로 적용하여, 간단한 웹 페이지를 구현해 봅시다. 웹 페이지의 모든 효과들은 JavaScript를 사용하여 구현됩니다.


Document Object Model 또는 DOM을 소개한 두 개의 기술자료가 있다. 이제는 DOM이 어떻게 작동하는지 잘 알 수 있다. (참고자료) 이 글에서는 우리가 이해한 것을 실행에 옮길 차례이다. 사용자 액션에 기반하여 변하는 사용자 인터페이스를 가진 기본적인 웹 애플리케이션을 개발할 것이다. 물론, DOM을 사용하여 인터페이스도 변경할 것이다. 이 글을 다 마치면, DOM 기술과 개념을 실전에 적용할 정도의 실력을 갖추게 될 것이다.


지난 두 개의 기술자료를 다 읽었을 것이라고 간주하겠다. 그렇지 않다면, 잘 읽어보고 DOM과 웹 브라우저가 HTML과 CSS를 웹 페이지를 나타내는 하나의 트리 구조로 전환하는 방법을 이해하기 바란다. 지금까지, 내가 이야기했던 모든 DOM 원리들은 단순한 DOM 기반 동적 웹 페이지를 구현하는데 사용될 것이다. 이 글을 읽다가 조금이라도 이해가 되지 않는 부분이 있다면 이전 기술자료들을 다시 읽어보기 바란다.


샘플 애플리케이션으로 시작하기



코드에 대하여

DOM과 JavaScript 코드에 집중하기 위해, 인라인 스타일(예를 들어, h1p 엘리먼트에 대한 align 애트리뷰트 같은)로 HTML을 작성했다. 이렇게 하는 것도 좋지만, 시간을 들여서 여러분의 모든 스타일을 여러분이 개발하는 실행 애플리케이션용 외부 CSS 스타일시트에 두기를 권하고 싶다.



매우 기본적인 애플리케이션에 작은 DOM 매직을 추가해 보자. DOM이 폼을 제출하지 않고 웹 페이지 상에서 이동할 수 있다는 것을 유념하고(이것은 Ajax에 확실히 대조되는 부분이다.) 뒤집어진 모자와 Hocus Pocus!라는 레이블 버튼만 보여주는 페이지를 만들어 보자.


첫 HTML



Listing 1은 이 페이지에 대한 HTML이다. 헤딩과 폼을 가진 바디와 단순한 이미지와 누를 수 있는 버튼이 있을 뿐이다.



Listing 1. 샘플 애플리케이션용 HTML


<html>
<head>
<title>Magic Hat</title>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



아래 다운로드 섹션에서는, 이 글에 사용되는 모든 이미지와 HTML이 제공된다. 하지만, 이미지만 다운로드하고, 내가 이 글에서 구현한 애플리케이션 샘플을 보면서 여러분이 직접 HTML을 작성하길 바란다. 이렇게 하면 DOM 코드를 훨씬 더 잘 이해할 수 있을 것이다.


샘플 웹 페이지 보기


여기에는 트릭이 전혀 없다. 페이지를 열면 그림 1과 같은 모습이 보인다.




그림 1. 평범한 모자

A rather boring-looking top hat


HTML의 마지막 포인트


여러분이 기억해야 할 한 가지 중요한 포인트는 Listing 1그림 1의 폼에 있는 버튼이 제출 버튼이 아닌 button 유형이라는 것이다. 제출 버튼을 사용할 경우, 버튼을 누르면 브라우저가 폼을 제출하게 된다. 물론, 이 폼은 어떤 action 애트리뷰트(완전히 의도적인)가 없기 때문에, 이것은 어떤 액티비티도 없는 무한 루프만을 만들게 된다. (직접 실험해 봐도 좋다.) 정상적인 인풋 버튼을 사용하고, 제출 버튼을 피하면, JavaScript 함수를 버튼에 연결하고 폼을 제출하지 않고 브라우저와 인터랙팅 한다.





샘플 애플리케이션에 더 많은 것 추가하기


이제 JavaScript, DOM 조작, 이미지 마법사를 가진 웹 페이지로 꾸며보자.


getElementById() 함수 사용하기


마법사 모자에는 토끼가 빠지면 안되는 법이다. 이 경우, 기존 페이지(그림 1에 있는 이미지를 토끼 이미지(그림 2)로 바꾼다.




그림 2. 똑 같은 모자에 토끼가 생겼다.

The same top hat, this time with a rabbit


DOM에 트릭을 추가하는 첫 번째 단계에는 웹 페이지에 img 엘리먼트를 나타내는 DOM 노드를 찾는 것도 포함된다. 일반적으로, 가장 쉬운 방법은 getElementById() 메소드를 사용하는 것이다. 이는 웹 페이지를 나타내는 document 객체에 대해 사용할 수 있다. 여러분은 전에 이 메소드를 보았다. 다음과 같이 작동한다.




var elementNode = document.getElementById("id-of-element");



HTML에 id 애트리뷰트 추가하기


이것은 매우 기본적인 JavaScript이지만, HTML에서는 몇 가지 작업이 필요하다. id 애트리뷰트를 액세스 하고자 하는 엘리먼트에 추가한다. 이것은 대체하고자 하는 img 엘리먼트이다. (토끼를 포함하고 있는 새로운 이미지이다.) 따라서 Listing 2처럼, HTML을 변경해야 한다.



Listing 2. id 애트리뷰트 추가하기



<html>
<head>
<title>Magic Hat</title>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



페이지를 리로드(reload)하면, 아무런 변화도 볼 수 없다. id 애트리뷰트를 추가한다고 해서 웹 페이지에 시각적인 효과가 생기지 않는다. 하지만, JavaScript와 DOM을 보다 쉽게 사용하여 엘리먼트로 작업할 수 있다.


img 엘리먼트


이제 getElementById()를 쉽게 사용할 수 있다. 원하는 엘리먼트의 아이디인 -- topHat-- 이 생겼고, 새로운 JavaScript 변수에 이것을 저장할 수 있다. Listing 3의 코드를 HTML 페이지에 추가한다.



Listing 3. img 엘리먼트에 액세스 하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



이 지점에서 웹 페이지를 로딩 또는 재로딩 한다 해도 별다른 차이를 볼 수 없다. 이 이미지에 액세스 하더라도 어던 것도 할 수 없다.





이미지 변경하기: 어려운 길


두 가지 방법으로 변경할 수 있다. 어려운 길과 쉬운 길이 있다. 모든 프로그래머가 그렇겠지만, 쉬운 방법을 선호한다. 하지만, 어려운 방법일 수록 DOM을 연습하는 최상의 방법이다. 우선 어려운 방법으로 이미지를 변경하는 방법부터 살펴보자. 나중에, 더 쉬운 방법으로 같은 작업을 하는 방법도 설명하겠다.


다음은 기존 이미지를 새로운, 토끼가 있는 이미지로 대체하기 위해서는;



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 즉, 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 기존 img 엘리먼트 바로 전에 컨테이너의 자식으로서 새로운 img 엘리먼트를 삽입한다.

  4. 이전 img 엘리먼트를 제거한다.


  5. 사용자가
    Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.


새로운 img 엘리먼트 만들기


여러분도 기억하겠지만, DOM의 핵심은 document 객체이다. 이것은 전체 웹 페이지를 나타내고, getElementById() 같은 강력한 메소드로 액세스 할 수 있도록 해주며, 새로운 노드를 만들 수 있도록 한다. 이것이 바로 지금 사용 할 마지막 속성이다.


새로운 img 엘리먼트를 만들어야 한다. 돔에서, 모든 것은 노드이지만, 노드들은 세 개의 기본적인 그룹으로 나뉜다:



  • 엘리먼트

  • 애트리뷰트

  • 텍스트 노드


다른 그룹들도 있지만 이 세 가지가 99퍼센트를 차지한다. 이 경우, img 유형의 새로운 엘리먼트가 필요하다. 따라서, JavaScript에 이 코드가 필요하다:




var newImage = document.createElement("img");



이것은 img라는 엘리먼트 이름을 가진 element 유형의 새로운 노드를 만든다. HTML에서는 다음과 같은 것이 된다:


<img />


DOM은 잘 구성된 HTML을 만들 것이고, 엘리먼트는 시작 태그와 엔딩 태그와 함께, 현재 비어있다. 이 엘리먼트에 콘텐트와 애트리뷰트를 추가하고, 이를 웹 페이지에 삽입한다.


콘텐트의 경우, img 엘리먼트는 빈 엘리먼트이다. 하지만 애트리뷰트를 추가해야 한다: src 애트리뷰트는 로딩 할 이미지를 지정한다. 여기에서 addAttribute() 같은 메소드를 사용해야 한다고 생각하겠지만, 틀렸다. DOM 스팩 작성자는, 프로그래머들은 지름길(shortcut)을 좋아한다고 생각했기 때문에, 새로운 애트리뷰트를 추가하고 기존 애트리뷰트의 값을 변경할 수 있는 하나의 메소드인 setAttribute()를 만들었다.



setAttribute()를 호출하고 기존 애트리뷰트를 제공한다면, 값은 여러분이 제공한 값으로 변한다. 하지만, setAttribute()를 호출하고 존재하지 않는 애트리뷰트를 제공하면, DOM은 여러분이 제공한 값을 사용하여 애트리뷰트를 추가한다. 한 가지 메소드로 두 가지 목적을 달성했다. 따라서, 다음을 JavaScript에 추가해야 한다.




var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");



이것은 이미지를 만들고, 소스를 알맞게 설정한다. 이제 여러분의 HTML은 Listing 4와 같을 것이다.



Listing 4. DOM을 사용하여 새로운 이미지 만들기


<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



이 페이지를 로딩할 수 있지만, 어떤 액션도 기대할 수는 없다. 실제 웹 페이지에 효과를 줄 어떤 것도 수행하지 않았다. step 5를 돌아보면, JavaScript 함수가 아직 호출되지도 않았다는 것을 알 수 있다.


원래 이미지의 부모에 액세스 하기


이미지를 삽입할 준비가 되었으니, 이를 어딘가에는 삽입해야 한다. 하지만, 기존 이미지에 삽입하지 않는다. 대신, 이것을 기존 이미지 앞에 두고, 기존 이미지를 제거한다. 이렇게 하려면, 기존 이미지의 부모가 필요하다. 이는 삽입과 제거의 핵심이다.


DOM은 웹 페이지를 노드의 계층인 트리로 간주한다. 모든 노드는 부모를 갖고 있고, 부모에는 자식 노드들이 있다. 이 이미지의 경우, 어떤 자식들도 없다. 이미지가 비어있는 엘리먼트라는 것을 기억하는가? 하지만 확실히 부모는 있다. 부모가 어떤 것인지에 대해서 신경 쓰지 않는다. 다만 여기에 액세스 할 뿐이다.


이렇게 하려면 모든 DOM 노드가 갖고 있는 parentNode속성을 사용한다:




var imgParent = hatImage.parentNode;



아주 간단하다. 부모는 자식들을 가질 수 있다는 것을 알고 있다. 이미 하나를 갖고 있다. 바로 기존 이미지이다. 부모가 div 인지 p 인지, 또는 페이지의 body 인지 알 필요가 없다; 전혀 문제가 되지 않는다!


새로운 이미지 삽입하기


오랜 이미지의 부모가 있으므로, 새로운 이미지를 삽입할 수 있다. 매우 쉽다. 자식을 추가 할 여러 메소드를 사용할 수 있기 때문이다:




  • insertBefore(newNode, oldNode)


  • appendChild(newNode)


오래된 이미지가 있는 곳에 새로운 이미지가 나타나도록 할 것이기 때문에 insertBefore()가 필요하다. (removeChild() 메소드도 사용해야 한다.) 다음은 기존 이미지 앞에 새로운 이미지 엘리먼트를 사용할 JavaScript 라인이다:




var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);



오래된 이미지의 부모는 두 개의 자식 이미지를 갖고 있다: 새로운 이미지 바로 다음에 오랜 이미지가 따라온다. 이러한 이미지들 주위에 있는 콘텐트는 변하지 않고, 콘텐트의 순서는 삽입되기 전과 똑같다. 이제 부모는 오랜 이미지 바로 앞에 새로운 이미지를 더 갖게 된다.


오랜 이미지 제거하기


이제 제거해야 하는 것은 오랜 이미지이다. 웹 페이지에 새로운 이미지만 필요하다. 이 샘플에서, 이미 오랜 이미지 엘리먼트의 부모가 있다. removeChild()를 호출하고 제거하기 원하는 노드로 전달한다:




var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);
imgParent.removeChild(hatImage);



여기에서, 오랜 이미지를 새로운 이미지로 대체해야 한다. HTML은 Listing 5처럼 된다.



Listing 5. 오랜 이미지를 새로운 이미지로 대체하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
var imgParent = hatImage.parentNode;
imgParent.insertBefore(newImage, hatImage);
imgParent.removeChild(hatImage);
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" />
</p>
</form>
</body>
</html>



JavaScript 연결하기


마지막 단계이자, 가장 쉬운 단계는 HTML 폼을 여러분이 작성한 JavaScript 함수로 연결하는 것이다. 사용자가 Hocus Pocus! 버튼을 클릭할 때마다 실행 될 showRabbit() 함수가 필요하다. onClick 이벤트 핸들러를 HTML에 추가한다:




<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />



JavaScript 프로그래밍에서, 이는 매우 일상적인 일이다. 이것을 HTML 페이지에 추가하고, 페이지를 저장하고, 페이지를 웹 브라우저로 로딩한다. 이 페이지는 처음에는 그림 1처럼 보인다; Hocus Pocus!를 클릭하면 그림 3과 같이 된다.




그림 3. 토끼가 등장하다!

The rabbit has come out to play





이미지 변경하기: 보다 쉬운 방법


이미지를 변경할 때 여러 가지 방법을 사용할 수 있다고 배웠다. replaceNode() 메소드를 사용하여 하나의 노드를 다른 노드로 대체할 수 있다. 다음과 같은 순서로 실행한다:



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 기존 img 엘리먼트 바로 전에 컨테이너의 자식으로서 새로운 img 엘리먼트를 삽입한다.

  4. 이전 img 엘리먼트를 제거한다.

  5. 사용자가 Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.



replaceNode()를 사용하면, 위 단계들을 줄일 수 있다. 3단계와 4 단계를 통합하여 다음과 같이 수행한다.



  1. 새로운 img 엘리먼트를 만든다.

  2. 부모 엘리먼트에 액세스 한다. 현재 img 엘리먼트의 컨테이너에 액세스 한다.

  3. 오랜 img 엘리먼트를 새로운 것으로 대체한다.

  4. 사용자가 Hocus Pocus! 버튼을 클릭하면 여러분이 만들었던 JavaScript 함수가 호출될 수 있도록 설정한다.


대단한 것 같지는 않지만, 코드가 확실히 간단해 졌다. Listing 6은 변경 방법이다: insertBefore()removeChild() 메소드 호출을 제거했다.



Listing 6. 오랜 이미지를 새로운 이미지로 대체하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
var newImage = document.createElement("img");
newImage.setAttribute("src", "rabbit-hat.gif");
var imgParent = hatImage.parentNode;
imgParent.replaceChild(newImage, hatImage);
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />
</p>
</form>
</body>
</html>



큰 변화가 없어 보이지만, DOM 코딩에서는 다소 중요한 포인트이다. 여러분은 주어진 태스크를 수행할 때 몇 가지 방법을 모색한다. DOM 메소드를 신중히 검토한다면, 네 개 또는 다섯 단계를 두 단계 또는 세 단계로 줄일 수 있다.





이미지 변경하기: 가장 쉬운 방법


태스크를 수행하는데 더 쉬운 방법은 늘 있다고 말해왔기 때문에, 이제는 이미지를 변경하는 가장 쉬운 방법을 설명하겠다. 어떤 방법일지 알 수 있겠는가? 힌트는 애트리뷰트이다.


이미지 엘리먼트는 src 애트리뷰트로 제어된다. 이는 어딘가에 있는(로컬 URI 또는 외부 URL) 파일을 참조한다. 지금까지, 이미지 노드를 새로운 이미지로 대체했다. 하지만, 기존 이미지의 src 애트리뷰트를 변경하는 것이 훨씬 쉬운 방법이다! 새로운 노드를 만들고, 부모를 찾고, 오랜 노드를 대체하는 작업을 한 단계로 줄인다:




hatImage.setAttribute("src", "rabbit-hat.gif");



이것이 끝이다. Listing 7에서, 전체 웹 페이지의 정황에서 이 해법을 볼 수 있다.



Listing 7. src 애트리뷰트 변경하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" onClick="showRabbit();" />
</p>
</form>
</body>
</html>



이것은 DOM의 가장 훌륭한 측면이다. 애트리뷰트를 업데이트 할 때, 웹 페이지도 즉시 변한다. 이미지가 새로운 파일을 가리키면, 브라우저는 그 파일을 로딩하고, 페이지가 업데이트 된다. 리로딩이 필요 없고, 새로운 이미지 엘리먼트를 만들 필요가 없다. 결과는 그림 3과 동일하다. -- 단지 코드만 단순해졌을 뿐이다.





토끼 숨기기


현재, 웹 페이지는 너무 단순하고 엉성하다. 토끼가 모자에서 나왔지만, 스크린 밑에 있는 버튼은 여전히 Hocus Pocus!이다. 토끼가 나왔는데도 showRabbit()을 호출하는 형국이다. 토끼가 나온 후에도 버튼을 클릭하면, 프로세싱 시간만 낭비하는 것이다. 이것은 불필요한 것이다. DOM을 사용하면. 토끼가 안에 있거나 밖으로 나왔을 경우 유용한 버튼을 만들 수 있다.


버튼 레이블 변경하기


가장 쉬운 방법은 사용자가 클릭을 한 후에 버튼의 레이블을 변경하는 것이다. 이렇게 한다고 해서, 더 이상의 마법이 발생하지 않는 것은 아니다. 웹 페이지에서 발생할 수 있는 최악의 상황은 정확하지 않은 어떤 것을 삽입하는 것이다. 버튼 레이블을 변경하기 전에, 노드에 액세스 해야 한다. 그 이전에, 버튼을 참조할 ID가 필요하다. 그것이 바로 오랜 모자이다. Listing 8은 버튼에 id 애트리뷰트를 추가하는 모습이다.



Listing 8. id 애트리뷰트 추가하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



JavaScript의 버튼에 액세스 하는 것은 쉽다:




function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
}



물론, JavaScript의 다음 라인에 버튼 레이블의 값을 변경하기 위해 타이핑을 했을 것이다. setAttribute()가 작동한다:




function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Get back in that hat!");
}



이렇게 간단한 DOM 조작으로, 버튼의 레이블은 토끼가 나타나자마자 변한다. HTML과 완료된 showRabbit() 함수는 Listing 9처럼 보인다.



Listing 9. 완료된 웹 페이지



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
button.setAttribute("value", "Get back in that hat!");
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



토끼를 다시 집어넣기


새로운 버튼 레이블을 보면 알겠지만, 토끼를 다시 모자 속으로 집어넣어야 한다. 토끼를 나타내게 했던 프로세스를 거꾸로 실행하면 된다. 이미지의 src 애트리뷰트를 오랜 이미지로 바꾼다. 새로운 JavaScript 함수를 만들어야 한다:




function hideRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "topHat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Hocus Pocus!");
}




showRabbit() 함수가 했던 모든 것을 역으로 하면 된다. 이미지를 토끼가 없었던 모자 이미지로 설정하고, 버튼을 그랩(grab)하고, 레이블을 다시 Hocus Pocus!로 바꾼다.





이벤트 핸들러 다루기


샘플 애플리케이션에 한 가지 큰 문제가 생겼다. 버튼 레이블이 변하더라도, 버튼을 클릭할 때 발생하는 액션은 변하지 않았다. 다행히, DOM을 사용하면 사용자가 버튼을 클릭할 때, 이벤트(발생하는 액션)를 변경할 수 있다. 버튼이 Get back in that hat!,을 읽으면, 클릭될 때 hideRabbit()을 실행한다. 토끼가 숨겨지면, 버튼은 showRabbit()을 실행하는 것으로 리턴된다.



addEventHandler() 피하기


onclick 속성 이외에도, onClick이나 onBlur 같은 이벤트 핸들러를 추가하는데 사용되는 메소드가 있다; 바로 addEventHandler()이다. 하지만, Microsoft™ Internet Explorer™는 이 메소드를 지원하지 않으므로, JavaScript에서 이를 사용한다면, 수많은 Internet Explorer 사용자들은 에러 밖에 얻을 것이 없다. 이 메소드를 사용하지 않도록 한다. 이 글에서는 이 방식으로 같은 결과를 얻을 수 있지만, Internet Explorer에서는 그럴 수 없다.



HTML을 보면, 여기에서 여러분이 다루는 이벤트가 onClick이라는 것을 알 수 있다. JavaScript에서, 이 이벤트는 버튼의 onclick 속성을 사용하여 이벤트를 참조할 수 있다. (HTML에서, 이 속성은 onClick으로 참조된다. 대문자 C라는 것을 주목하라. JavaScript에서는 소문자 c를 사용한 onclick 이다.) 따라서, 버튼에 대해 실행되는 이벤트를 변경할 수 있다: 새로운 함수를 onclick 속성으로 할당하면 된다.



onclick 속성에는 함수의 스트링 이름이 아닌 함수 레퍼런스가 입력되어야 한다. 하지만 함수 자체에 대한 레퍼런스이다. JavaScript에서는 함수를 괄호가 없는 이름으로 참조할 수 있다. 따라서 버튼이 클릭될 때 실행되는 함수를 다음과 같이 변경할 수 있다:




button.onclick = myFunction;



HTML에서, 변경은 매우 단순하다. Listing 10에서, 버튼 실행 함수를 보자.



Listing 10. 버튼의 onClick 함수 변경하기



<html>
<head>
<title>Magic Hat</title>
<script language="JavaScript">
function showRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "rabbit-hat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Get back in that hat!");
button.onclick = hideRabbit;
}

function hideRabbit() {
var hatImage = document.getElementById("topHat");
hatImage.setAttribute("src", "topHat.gif");
var button = document.getElementById("hocusPocus");
button.setAttribute("value", "Hocus Pocus!");
button.onclick = showRabbit;
}
</script>
</head>

<body>
<h1 align="center">Welcome to the DOM Magic Shop!</h1>
<form name="magic-hat">
<p align="center">
<img src="topHat.gif" id="topHat" />
<br /><br />
<input type="button" value="Hocus Pocus!" id="hocusPocus"
onClick="showRabbit();" />
</p>
</form>
</body>
</html>



이제 DOM 애플리케이션이 완성되었다. 직접 시험해 보기 바란다!





맺음말


이제는 DOM에 어느 정도 익숙해 졌으리라 믿는다. 이전 글에서는, DOM의 기본적인 개념과 API에 대해 설명했다. 오늘은 단순한 DOM 기반 애플리케이션을 설명했다. 이 글을 다시 복습하기 바란다.


이것으로 본 시리즈에서 Document Object Model을 다루는 일은 더 이상 없겠지만, 여러분의 임무는 끝나지 않았다. 사실, 여러분은 DOM을 사용하지 않고 Ajax와 JavaScript로 훨씬 더 많은 작업을 해야 할 상황에 처하게 될 것이다. 복잡한 하이라이팅과 움직임 효과를 만들든지, 텍스트 블록이나 이미지로 작업하든지 간에, DOM 방식을 사용하면 매우 쉽게 수행할 수 있다.


DOM을 사용하는 것이 아직 어색하다면, 세 개의 기술자료들을 복습하기 바란다. 나머지 기술자료에서는 DOM에 대해서는 자세히 설명하지 않을 것이다. DOM을 사용하고, DOM 기반 애플리케이션을 직접 작성해보면 여러분도 데이터 포맷에 좀더 익숙해 질 것이다.


by 미소 | 2007/12/30 23:23 | AJAX 기본 | 트랙백(2)

4. Ajax 마스터하기, Part 4: 웹 응답에 DOM 활용하기

난이도 : 초급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 3 월 14 일
2006 년 3 월 14 일 수정

프로그래머(백엔드 애플리케이션)와 웹 프로그래머(주로 HTML, CSS, JavaScript를 작성)사이에는 오래 전부터 엄격한 구분이 있었습니다. 하지만 Document Object Model (DOM)이 그 틈을 메우면서 백 엔드에서는 XML과, 프론트 엔드에서는 HTML과의 작업이 가능해 졌습니다.


많은 웹 프로그래머들과 마찬가지로 여러분도 HTML로 작업을 해봤을 것이다. HTML은 프로그래머들이 웹 페이지 상에서 작업할 때 사용한다. HTML은 애플리케이션이나 사이트를 마감하면서 수행하는 마지막 작업이고, 배치, 색상, 스타일 등을 끝까지 작업한다. 웹 페이지의 디자인과 공급과 관련한 프로세스를 명확히 파악할 필요가 있다.



  1. 누군가가(대개는 여러분이) 텍스트 에디터나 IDE에서 HTML을 만든다.

  2. 그런 다음, HTML을 Apache HTTPD 같은 웹 서버에 업로딩하고 이것을 인터넷이나 인트라넷에 퍼블리시 한다.

  3. 사용자는 Firefox 또는 Safari 같은 브라우저를 사용하여 웹 페이지에 요청한다.

  4. 사용자의 브라우저는 여러분의 웹 브라우저에 HTML용 요청을 만든다.

  5. 브라우저는 서버에서 받는 페이지를 그래픽 또는 테스트로 렌더링 한다. 사용자는 웹 페이지를 보고 활성화 한다.


매우 기본적인 것처럼 보이지만 실상은 매우 흥미롭다. 사실, 엄청난 양의 “성분(stuff)”들이 있다. 이것은 주로 4 단계와 5 단계 사이에 발생하고 바로, 이 부분을 이 글에서 중점적으로 다룰 것이다. 대부분의 프로그래머들은 사용자의 브라우저가 이것을 디스플레이 하도록 요청 받으면 대부분의 프로그래머들은 자신들의 마크업에 어떤 일이 발생하는지 정확히 고려하지 않기 때문이다.



  • 브라우저가 단순히 HTML에 있는 텍스트를 읽고 디스플레이 하는가?

  • CSS가 외부 파일에 있을 경우, CSS는 어떤가?

  • 외부 파일에 있는 JavaScript는 어떤가?

  • 브라우저는 이러한 아이템들을 어떻게 핸들하며 이벤트 핸들러, 기능, 스타일들을 텍스트 마크업으로 어떻게 매핑하는가?


이 모든 질문들에 대한 답은 Document Object Model이다. 이제 본격적으로 DOM을 논해보자.


웹 프로그래머와 마크업


프로그래머의 작업이 끝날 때 웹 브라우저가 시작된다. 다시 말해서, HTML 파일을 웹 서버 상의 디렉토리에 얹어 놓으면 보통 이것을 "완료된 것 "으로 정리해 놓고 절대로 다시는 생각하지 않는다! 깨끗하고, 구성이 잘된 페이지를 작성할 때도 이것은 너무 멋진 목표이다. 여러분의 마크업이 브라우저를 통해 다양한 버전의 CSS와 JavaScript로 디스플레이 하기를 원하는데 이것도 잘못은 아니다.


문제는 이러한 접근 방식이 브라우저에서 실제로 무슨 일이 일어나는지에 대해 프로그래머가 알 수 있는 범위를 제한한다는 점이다. 더욱이 클라이언트 측 JavaScript를 사용하여 웹 페이지를 동적으로 업데이트, 변경, 재구현 할 수 있는 기능 까지 제한한다. 이러한 한계를 제거하고 더 나아가 웹 사이트에서 더 나은 인터랙션과 생산성을 도모할 수 있다.


프로그래머가 하는 일


웹 프로그래머로서 여러분은 텍스트 에디터와 IDE를 시작하고 HTML, CSS, 심지어 JavaScript를 입력하기 시작한다. 태그, 셀렉터, 애트리뷰트를 사이트가 올바르게 보일 수 있도록 하는 작은 태스크라고 생각하기 쉽다. 하지만 그러한 관점을 좀더 확장 할 필요가 있다. 여러분이 콘텐트를 구성하고 있다는 것을 깨달아야 한다. 걱정하지 말라. 마크업의 가치에 대해 일장 연설을 늘어놓으려는 것은 아니다. 웹 페이지의 진정한 가치를 깨닫는 방법 내지는 형이상학적인 무엇인가를 설명하려는 것도 아니다. 여러분이 이해해야 할 것은 웹 개발 시 여러분의 역할이 정확히 무엇인가를 이해해야 한다.


페이지를 보이게 해야 하는 시점에 와서 여러분은 제안만 할 수 있을 뿐이다. 여러분이 CSS 스타일시트를 제공하면 사용자는 여러분의 스타일을 무시할 수 있다. 폰트 사이즈를 제공하면 사용자의 브라우저는 그러한 사이즈를 변경할 수 있고 모니터에 맞게 스케일링 할 수 있다. 폰트와 컬러도 사용자의 모니터에 맞게 선택할 수 있다. 페이지를 스타일링 할 때 최선을 다하는 것도 중요하지만 이는 웹 페이지에 큰 영향을 주지 못한다.


여러분이 완벽히 제어하는 것은 웹 페이지의 구조이다. 여러분의 마크업은 변경할 수 없고 사용자는 이것을 망칠 수 없다. 브라우저는 웹 서버에서 이것을 가져와서 디스플레이 한다. (여러분의 구미 보다 사용자의 구미에 따라 스타일로) 하지만 이 페이지의 구성은-이 단어가 그 문단 내에 있든 다른 div에 있든-전적으로 여러분에 달려있다. 페이지를 실제로 변경할 때(이것은 대부분의 Ajax 애플리케이션들이 집중하는 것이다.) 이것은 여러분이 운영하는 페이지의 구조이다. 텍스트의 조각의 색상을 변경하는 것이 좋지만 텍스트나 전체 섹션을 기존 페이지에 추가하는 것이 훨씬 더 좋다. 사용자가 그 섹션을 어떻게 스타일링 하던지 간에 페이지 그 자체의 구성을 가지고 작업한다.


마크업이 수행하는 일


마크업이 구성에 관한 것이라는 것을 깨달으면 이것을 달리 볼 수 있다. h1이 텍스트를 크고, 검고, 두껍게 만든다고 생각하는 대신 h1을 헤딩으로서 생각하라. 사용자가 어떻게 보는가, 그리고 사용자가 여러분의 CSS를 사용하는 자신들의 것을 사용하든 두 개를 결합하여 사용하든 이것은 두 번째 문제이다. 대신 마크업은 이 정도의 구성을 제공하는 것이라는 것을 깨달아라. P는 텍스트가 단락(paragraph)이라는 것을 나타내고, img는 이미지를, div는 페이지를 섹션으로 나눈다.


스타일과 작동(이벤트 핸들러와 JavaScript)가 fact 뒤에 이 구성에 적용된다는 것도 명확해 진다. 마크업은 적소에서 작동되거나 스타일링 되어야 한다. 따라서 HTML에 대한 외부 파일에 CSS를 갖는 것처럼 마크업의 구성도 스타일, 포맷팅, 작동과 분리된다. 엘리먼트의 스타일 또는 텍스트 조각을 JavaScript로부터 확실히 변화시킬 수 있고 마크업이 레이아웃 한 구성을 실제로 바꿀 수 있다는 것은 더 흥미 있는 사실이다.


마크업이 페이지에 구성 또는 프레임웍만 제공한다는 것을 마음에 새긴다면 본격적인 게임에 돌입해보자. 브라우저가 모든 텍스트 구성을 가지고, 이를 변경, 추가, 삭제 가능한 객체로 바꾸는 방법을 보자.



마크업에 대해 더 알아야 할 것들


플레인 텍스트 편집: 옳은가, 그른가?


플레인 텍스트 파일은 마크업을 저장하는 데는 이상적이지만 그 마크 업을 편집하는 데는 그렇지 못하다. Macromedia DreamWeaver 또는 Microsoft ® FrontPage ® 같은 IDE를 사용하는 것이 바람직하다. 이러한 환경은 종종 웹 페이지를 구현할 때 도움이 되는 지름길과 도움말을 제공한다. 특히 CSS와 JavaScript를 사용할 때 그렇다. 많은 사람들은 여전히 Notepad나 vi를 선호한다. (고백하건데 나도 그 중 하나이다.) 이것 역시 좋은 선택이다. 두 경우 모두 마지막 결과는 마크업으로 가득 찬 텍스트 파일이다.



네트워크를 통한 텍스트: 좋은 것


이미 언급했듯이 텍스트는 HTML이나 CSS 같은 문서에 있어 훌륭한 미디어이다. 이것은 네트워크를 통해 수백, 수천 번 이동한다. 브라우저가 텍스트를 나타내는데 어려움을 겪는다면 텍스트를 시각적 페이지와 그래픽 페이지로 변환한다는 의미이다. 브라우저가 웹 서버에서 페이지를 실제로 가져오는 방법과는 관련이 없다. 이 경우 텍스트는 여전히 최상의 옵션이다.



텍스트 마크업의 장점


웹 브라우저를 논하기 전에 왜 플레인 텍스트가 HTML을 저장하기에 최상의 선택인지를 생각해 봐야 한다. (마크업에 대해 더 알아야 할 것들 참조) 찬반을 논하기 전에, 페이지가 보여질 때 마다 HTML이 네트워크를 통해 웹 브라우저로 보내진다는 것을 생각해 보라. (캐싱 같은 문제는 차후에 논하기로 한다.) 텍스트와 함께 전달하는 것 보다 효율적인 방법은 없다. 바이너리 객체, 그래픽으로 구현된 페이지, 재구성된 마크업 청크 등, 이 모든 것들은 플레인 텍스트 파일 보다 네트워크를 통해 전송할 때 더 어려운 것들이다.


브라우저를 이러한 방정식에 대입해 보자. 오늘날의 브라우저에서는 사용자가 텍스트의 크기길 변경하고, 이미지를 스케일링 하고 CSS나 JavaScript를 다운로드 할 수 있다. 이 모든 것은 페이지의 온갖 종류의 그래픽 표현을 브라우저로 보내는 전조가 된다. 대신 브라우저는 미가공 HTML을 필요로 한다. 왜냐하면 이것은 태스크를 핸들하기 위해 서버를 믿기 보다 어떤 프로세싱이든 브라우저에 있는 페이지에 적용할 수 있기 때문이다. 같은 맥락에서, CSS와 JavaScript를 분리하고, 이들을 HTML 마크업에서 분리할 때에는 분리하기 쉬운 포맷이 필요하다.


HTML 4.01, XHTML 1.0/ 1.1 같은 새로운 표준들이 콘텐트(페이지의 데이터)를 표현과 스타일(보통 CSS에 의해 적용됨)에서 분리하겠다는 약속을 기억하는가? 프로그래머들이 CSS에서 HTML을 분리하려면 브라우저를 실행하여 페이지에서 몇몇 구현들을 가져오고 그러한 표준의 많은 장점들을 없앤다. 브라우저에서 이렇게 다른 부분들을 계속 분리시키면 브라우저는 서버에서 HTML을 가져올 때 최상의 유연성을 보인다.


웹 브라우저 분석


지금까지 여러분이 읽은 모든 것은 웹 개발 프로세스에서의 여러분의 역할에 대한 리뷰에 불과하다. 하지만 웹 브라우저가 무엇을 수행하는지를 논해야 하는 시점에서 유능한 많은 웹 디자이너와 개발자들은 보이지 않는 곳에서 실제로 어떤 일이 발생하는지 종종 깨닫지 못한다. 이 섹션에서는 바로 그 부분을 설명하도록 하겠다. 걱정하지 말라. 코드도 함께 등장할 것이다. 잠시 코딩하고 싶은 조바심을 접어두라. 웹 브라우저가 정확히 어떤 일을 수행하는지를 이해하는 것이 정확한 코딩 작업의 필수이기 때문이다.


텍스트 마크업의 단점


텍스트 마크업이 디자이너나 페이지 생성자에게 엄청난 이득을 주듯이 브라우저에는 비교적 큰 단점을 갖고 있다. 특히 브라우저는 텍스트 마크업을 사용자에게 시각적으로 직접 나타내기가 매우 힘들다. (마크업에 대해 더 알아야 할 것들 참조) 다음과 같은 브라우저 태스크를 생각해 보라.



  • CSS 스타일?외부 파일에 있는 다중 스타일시트?을 HTML 문서의 엘리먼트 유형, 클래스, 아이디, 위치에 기반하여 마크업에 적용한다.

  • JavaScript 코드에 기반한 스타일과 포맷팅?외부 파일에도 있음?을 HTML 문서의 다른 부분들에 적용한다.

  • JavaScript 코드에 기반하여 폼 필드의 값을 변경한다.

  • JavaScript 코드에 기반하여 이미지 롤오버와 이미지 스와핑 같은 시각 효과를 지원한다.


복잡함은 이러한 태스크들을 코딩 하는데 있는 것이 아니다. 이러한 일들을 하기는 정말 쉽다. 복잡함은 브라우저가 실제로 요청된 액션을 수행하는 데서 온다. 마크업이 텍스트로 저장되면 center-text 클래스에서 텍스트를 센터링 해야 한다. (text-align: center) 이것을 어떻게 할 것인가?



  • 텍스트에 인라인 스타일링을 추가하는가?

  • 브라우저의 HTML 텍스트에 스타일링을 적용하고 어떤 것이 센터링 되는지, 어떤 것이 센터링 되지 않는지를 지켜보는가?

  • 스타일링 되지 않은 HTML을 적용한 다음 팩트 다음에 포맷을 적용하는가?


이 같은 매우 어려운 질문들 때문에 몇몇 사람들이 오늘날 브라우저를 코딩을 한다.


확실히, 플레인 텍스트는 브라우저를 위해 HTML을 저장하는 최상의 방법은 아니다. 텍스트가 페이지의 마크업을 가져오는 좋은 솔루션이었지만 말이다. 이 외에도 JavaScript가 페이지의 구조를 변경하는 기능은 트릭이 조금 있다. 브라우저가 수정된 구조를 디스크에 재작성 해야 하는가? 문서의 현재 어떤 단계에 있는지를 어떻게 파악할 수 있는가?


확실히 텍스트는 답이 못 된다. 수정하기도 어렵고, 스타일과 작동을 추가하기에는 불편하고, 궁극적으로 오늘날 웹 페이지의 동적인 특징과 거리가 멀다.


트리 뷰로 이동하기


이 문제에 대한 답, 오늘날의 웹 브라우저에 맞는 답은 트리 구조를 사용하여 HTML을 나타내는 것이다. 텍스트 마크업으로 구현된 단순하고 지루한 HTML 페이지 대신 Listing 1을 보자.



Listing 1. 텍스트 마크업의 간단한 HTML 페이지


<html>
<head>
<title>Trees, trees, everywhere</title>
</head>
<body>
<h1>Trees, trees, everywhere</h1>
<p>Welcome to a <em>really</em> boring page.</p>
<div>
Come again soon.
<img src="come-again.gif" />
</div>
</body>
</html>


그림 1. 이 브라우저는 이것을 트리 구조로 변환한다.




그림 1. Listing 1의 트리 뷰

The tree view of Listing 1


이 글을 위해 단순함을 유지했다. DOM과 XML 전문가는 공백이 문서에 있는 텍스트가 구현되고 웹 브라우저의 트리 구조에서 깨지는 방법에 영향을 줄 수 있다는 것을 알 것이다. 공백의 효과에 대해 알고 있다면 정말 대단하다. 그렇지 않다면 공부하면 된다. 걱정 말라. 이것이 문제가 될 때 필요한 것이 무엇인지를 깨닫게 될 것이다.


실제 트리 백그라운드 외에 여기에서 알아야 할 첫 번째 것은 트리에 있는 모든 것이 가장 바깥쪽에서 시작되고 HTML의 엘리먼트(html)를 포함하고 있다는 것이다. 이는 트리 메타포에서 루트(root) 엘리먼트라고 불린다. 이것이 트리의 바닥에 있지만 트리를 분석할 때면 언제나 이것부터 시작한다. 완전히 거꾸로 뒤집어보면 도움이 될 것이다.


루트에서부터 마크업의 다양한 조각들 간 관계를 보여주는 라인의 흐름을 따라가 보라. headbody 엘리먼트는 html 루트 엘리먼트의 자식들이다. titlehead의 자식이고 "Trees, trees, everywhere" 텍스트는 title의 자식이다. 전체 트리는 브라우저가 그림 1과 비슷한 구조가 될 때까지 이와 같이 구성된다.


몇 가지 추가 용어


트리 메타포를 이해하기 위해서 headbodyhtml의 브랜치(branch)라고도 일컬어진다. 이들은 자신들의 자식이 있기 때문에 브랜치이다. 트리의 말단에 다다르면 "Trees, trees, everywhere"와 "really" 같은 텍스트로 가게 된다. 이들은 자식들이 없기 때문에 잎(leave)으로 일컬어진다. 이 용어들을 다 기억할 필요는 없고 특정 용어가 무엇을 의미하는지를 파악하려면 나무의 구조를 머리속에 그려보면 된다.


객체의 가치


기본적인 용어들을 익혔으니 엘리먼트 이름과 텍스트가 들어있는 작은 직사각형에 집중해 보자.(그림 1) 각 직사각형들은 객체이다. 여기에서 브라우저는 텍스트와 관련된 문제들을 해결한다. 객체를 사용하여 HTML 문서의 조각들을 나타냄으로서 구성을 변경하고, 스타일을 적용하며, JavaScript를 문서에 액세스 시키기가 매우 쉬워진다.


객체 유형과 속성


모든 가능한 유형의 마크업은 고유의 객체 유형을 갖는다. 예를 들어, HTML에 있는 엘리먼트는 Element 객체 유형에 의해 구현된다. 문서에 있는 텍스트는 Text 유형에 의해 구현된다. 애트리뷰트는 Attribute 유형에 의해 표현된다.


따라서 웹 브라우저는 객체 모델을 사용하여 문서를 표현하고?정적 텍스트를 다룰 필요가 없음?객체 유형에 따라 즉각 구분할 수 있다. HTML 문서는 파싱되고 그림 1의 객체들로 바뀐다. 그런 다음 대괄호 같은 이스케이프 시퀀스로 바뀐다. 이는 브라우저의 작업을 훨씬 쉽게 만든다. 적어도 인풋 HTML을 파싱한 후에도 말이다. 어떤 것이 엘리먼트이고 어떤 것이 애트리뷰트인지 파악하고 객체 유형을 어떻게 다룰지를 결정하는 작동은 간단하다.


객체들을 사용함으로서 웹 브라우저는 그러한 객체들의 속성들을 변경할 수 있다. 예를 들어, 각 엘리먼트 객체는 하나의 부모와 자식 리스트를 갖고 있다. 새로운 자식 엘리먼트나 텍스트를 추가하는 것은 새로운 자식을 엘리먼트의 자식 리스트에 추가하는 문제에 지나지 않는다. 이러한 객체들은 또한 style 속성을 갖고 있어서 엘리먼트의 스타일이나 텍스트 조각을 쉽게 변경할 수 있다. 예를 들어, 다음과 같이 JavaScript를 사용하여 div의 높이를 수정할 수 있다.



someDiv.style.height = "300px";


다시 말해서, 웹 브라우저는 이와 같이 객체 속성들을 사용하여 트리의 모양과 구조를 쉽게 변경한다. 이것을 복잡한 것과 비교해 보라. 속성과 구조가 변할 때 마다 브라우저는 정적 파일을 재작성 하고, 재 파싱 하고 이를 스크린에 다시 디스플레이 해야 한다. 이 모든 것이 객체로도 가능해진다.


이 시점에서 HTML 문서에 대해 알아보고 이를 트리로 그려보자. 평범한 요청은 아닌 것 같지만 이들을 다룰 수 있으려면 이러한 트리 구조에 익숙해져야 한다. 보통의 요청은 아닌 것 같지만 이 트리 구조에 익숙해져야 한다. 이들을 조작할 수 있으려면 말이다.


이 프로세스에서 몇 가지 이상한 점들을 발견하게 된다. 다음과 같은 상황을 생각해 보자.



  • 애트리뷰트에는 어떤 일이 발생하는가?


  • emb 같은 엘리먼트로 나뉜 텍스트는 어떻게 되는가?

  • 정확하게 구축되지 않은 HTML은 어떻게 되는가? (닫기 p 태그가 소실 되는 경우)


일단 이러한 유형의 문제에 익숙해지면 다음 섹션을 이해하기가 더 쉬울 것이다.


엄격함을 유지한다.


내가 언급했던 것을 직접 해본다면 마크업의 트리 뷰에 잠재적 문제 몇 가지들을 발견할 것이다. (직접 하지 않을 것이라면 내 말을 믿어라.) 사실, Listing 1그림 1에서 여러 가지를 발견할 것이다. p 엘리먼트가 나뉘어지는 방법부터 시작해서 말이다. 전형적인 웹 개발자에게 p 엘리먼트의 텍스트 콘텐트가 무엇인지를 묻는다면 일반적으로 "Welcome to a really boring Web page"라고 답할 것이다. 이것을 그림 1과 비교하면 이러한 대답이 논리적이긴 하지만 전혀 맞지 않다는 것을 알게 될 것이다.



p 엘리먼트는 세 개의 다른 자식 객체들을 갖고 있고, 이 중 어떤 것도 전체 "Welcome to a really boring Web page" 텍스트를 포함하고 있지 않다. "Welcome to a "와 " boring Web page" 같은 텍스트의 일부를 볼 수는 있어도 이것이 전체 문장은 아니다. 이를 이해하려면 마크업의 모든 것이 어떤 유형의 객체로 바뀌어야 한다는 것을 기억하라.


더욱이 순서도 문제가 된다. 정확한 마크업이지만 여러분의 HTML에서 제공된 순서와 다르다면 사용자가 웹 브라우저에 어떻게 대응할지를 상상할 수 있겠는가? 문서를 구성했던 방식이 아닐 때에도 제목과 헤딩 사이에 끼게 될 것이다. 브라우저는 엘리먼트와 텍스트의 순서를 보존해야 한다.


이 경우, p 엘리먼트는 세 개의 구별된 부분들을 갖는다.




  • em 엘리먼트 앞에 오는 텍스트


  • em 엘리먼트


  • em 엘리먼트 뒤에 오는 텍스트


이 순서를 섞는다면 텍스트의 잘못된 부분에 강조를 적용한 것이다. 이 모든 것을 바로잡으려면 p 엘리먼트는 Listing 1의 HTML에 나타났던 순서 대로 세 개의 객체 자식들을 가져야 한다. 더욱이 강조된 텍스트인 "really"는 p의 자식 엘리먼트가 아니다. 이것은 p의 자식인 em의 자식이다.


이 개념을 이해하는 것은 매우 중요하다. "really" 텍스트가 나머지 p 엘리먼트의 텍스트와 함께 디스플레이 되더라도 이것은 여전히 em 엘리먼트의 직접적인 자식이다. 이것은 나머지 p와는 다른 포맷팅을 가질 수 있고 나머지 텍스트와 개별적으로 움직일 수 있다.


이를 유념하면서 Listing 23의 HTML을 다이어그램으로 그리면서 텍스트에 정확한 부모를 유지하도록 한다. 정확한 부모로 유지시킨다.



Listing 2. 약간의 트릭이 들어간 엘리먼트 중첩이 있는 마크업


<html>
<head>
<title>This is a little tricky</title>
</head>
<body>
<h1>Pay <u>close</u> attention, OK?</h1>
<div>
<p>This p really isn't <em>necessary</em>, but it makes the
<span id="bold-text">structure <i>and</i> the organization</span>
of the page easier to keep up with.</p>
</div>
</body>
</html>



Listing 3. 보다 트릭이 심한 엘리먼트의 중첩


<html>
<head>
<title>Trickier nesting, still</title>
</head>
<body>
<div id="main-body">
<div id="contents">
<table>
<tr><th>Steps</th><th>Process</th></tr>
<tr><td>1</td><td>Figure out the <em>root element</em>.</td></tr>
<tr><td>2</td><td>Deal with the <span id="code">head</span> first,
as it's usually easy.</td></tr>
<tr><td>3</td><td>Work through the <span id="code">body</span>.
Just <em>take your time</em>.</td></tr>
</table>
</div>
<div id="closing">
This link is <em>not</em> active, but if it were, the answers
to this <a href="answers.html"><img src="exercise.gif" /></a> would
be there. But <em>do the exercise anyway!</em>
</div>
</div>
</body>
</html>


이러한 관행에 대한 해답은 이 글 끝부분의 GIF 파일인 tricky-solution.gif (그림 2)trickier-solution.gif (그림 3)에서 찾을 수 있다. 스스로 알아내기 전에 몰래 보지 않기를 바란다. 엄격한 규칙이 트리를 구성하는데 어떻게 적용되는지를 이해하면 도움이 될 것이다. HTML과 트리 구조를 마스터 한다면 정말로 도움이 될 것이다.


애트리뷰트


애트리뷰트를 어떻게 다루어야 하는지를 파악할 때 문제가 생긴 적이 있는가? 앞서 언급했지만 애트리뷰트는 고유의 객체 유형을 갖고 있지만 애트리뷰트는 엘리먼트의 자식이 아니다. 중첩 엘리먼트와 텍스트는 같은 레벨의 애트리뷰트가 아니고 Listing 23에 대한 답에는 애트리뷰트가 나타나지 않는다는 것을 알 수 있다.


사실 애트리뷰트는 브라우저가 사용하는 객체 모델에 저장되지만 이들은 특별한 경우이다. 각 엘리먼트는 여기에 사용되는 애트리뷰트 리스트를 갖고 있고 자식 객체의 리스트에서 분리된다. 따라서 div 엘리먼트는 "id" 애트리뷰트와 또 다른 이름 " class "를 포함하고 있는 리스트를 갖게 된다.


엘리먼트용 애트리뷰트가 유일한 이름을 갖고 있어야 한다는 것을 기억하라. 다시 말해서, 하나의 엘리먼트가 두 개의 "id" 또는 두 개의 "class"애트리뷰트를 가질 수 없다. 보존하고 액세스 할 리스트를 매우 쉽게 만든다. 다음 글에서 보겠지만 getAttribute("id") 같은 메소드를 호출하여 애트리뷰트 값을 이름 별로 얻을 수 있다. 애트리뷰트를 추가하고 기존 애트리뷰트의 값을 비슷한 메소드 호출을 설정(재설정)할 수 있다.


애트리뷰트의 독자성은 리스트를 자식 객체들의 리스트와 구별시킨다. p 엘리먼트는 그 안에 여러 em 엘리먼트를 갖고 있기 때문에 자식 객체의 리스트에는 중복 아이템이 포함될 수 있다. 자식 리스트와 애트리뷰트 리스트는 비슷하게 작동하지만 하나는 중복을 포함할 수 있고(객체의 자식) 하나는 그럴 수 없다는 것이다. (엘리먼트 객체의 애트리뷰트) 마지막으로 엘리먼트만이 애트리뷰트를 가질 수 있기 때문에 텍스트 객체는 여기에 첨부될 리스트가 없다.


어지러운 HTML


더 진행하기에 앞서 브라우저가 마크업을 트리 구현으로 변환하는 방법, 브라우저가 엉성한 폼의 마크업을 어떻게 다루는지를 볼 필요가 있다. 구성이 잘되었다(Well-formed)는 용어는 XML에서 광범위하게 사용되고 두 가지 기본적인 의미가 있다.



  • 모든 오프닝 태그는 여기에 매칭되는 클로징 태그를 갖고 있다. 따라서 모든 <p></p><div></div>와 매칭된다.

  • 가장 안쪽의 오프닝 태그는 가장 안쪽의 클로징 태그와 매칭된다. 그 다음 안쪽의 오프닝 태그는 그 다음 안쪽의 클로징 태그와 매칭되는 식이다. 따라서 <b><i>bold and italics</b></i>는 옳지 않다. 가장 안쪽의 오프닝 태그인 <i><b>와는 맞지 않기 때문이다. 이를 잘 구성하려면 오프닝 태그 순서를 바꾸거나 클로징 태그 순서를 바꾼다. (둘 다 바꾼다면 똑 같은 문제가 생긴다.)


이 두 가지 규칙들을 자세히 연구해 보자. 두 규칙 모두 문서의 간단한 구성을 늘릴 뿐만 아니라 모호함을 제거한다. Bolding이 먼저 적용되고 그 다음에 italics를 적용해야 하는가? 아니면 그 반대인가? 순서와 다의성이 큰 문제인 것처럼 보이지만 CSS에서는 이 규칙들이 다른 규칙들을 무시할 수 있도록 한다. 따라서 b 엘리먼트 안에 있는 텍스트의 폰트가 i 엘리먼트 내의 폰트와 다르다면 포맷팅이 적용되는 순서는 매우 중요하다. 따라서 HTML 페이지의 좋은 구성이 중요한 것이다.


잘 구성되지 않은 문서를 브라우저가 받는 경우 할 수 있는 최선을 다한다. 결과 트리 구조는 가장 좋은 경우는 원래 페이지 작성자가 의도했던 것과 비슷한 것이고 최악의 경우 완전히 다른 것이다. 브라우저에 페이지를 로딩하고 기대했던 것과 완전히 다른 일이 발생한다면 구조에 대해 다시 생각해 봐야 한다. 물론 픽스는 간단하다. 문서가 잘 구성되었는지를 확인하는 것이다. 표준화된 HTML을 작성하는 방법을 모르겠다면 참고자료를 참조하라.


DOM


지금 까지, 브라우저가 웹 페이지를 객체 구현으로 변환하는 것에 대해 배웠다. 아마도 여러분은 객체 구현이 DOM 트리라고 생각해왔을 것이다. DOM은 문서 객체 모델(Document Object Model)을 의미하고 World Wide Web Consortium (W3C)에서 사용할 수 있는 스팩이다.(참고자료)


DOM은 브라우저가 마크업을 나타낼 수 있도록 하는 객체의 유형과 속성들을 정의한다. (다음 글에서는 JavaScript와 Ajax 코드에서 DOM을 사용하는 방법을 설명하겠다.)


문서 객체


무엇보다도 객체 모델에 액세스 해야 한다. 이것은 매우 쉽다. 웹 페이지에서 실행되는 JavaScript 코드 조각에 있는 빌트인 document 변수를 사용하려면 다음과 같이 코드를 작성한다.



var domTree = document;


물론 이 코드는 그 자체로는 쓸모가 없지만 모든 웹 브라우저가 document 객체를 JavaScript 코드에 사용할 수 있도록 하고 객체는 완벽한 마크업 트리를 나타낸다.(그림 1)


모든 것이 노드이다!


확실히, document 객체는 중요하지만 단지 시작에 불과하다. 더 진행하기 전에 또 다른 용어인 노드(node) 개념을 익혀야 한다. 마크업의 각 부분이 객체에 의해 구현되지만 이는 하나의 객체(특정 유형의 객체)인 DOM 노드에 불과하다는 것을 이미 알 것이다. 보다 특별한 유형인 텍스트, 엘리먼트, 애트리뷰트는 이러한 기본적인 노드 유형에서 확장된다. 따라서 여러분은 텍스트 노드, 엘리먼트 노드, 애트리뷰트 노드를 갖고 있는 것이다.


JavaScript로 프로그래밍을 했다면 DOM 코드를 사용하는 방법도 알 것이다. 이 Ajax 시리즈를 충실히 이행했다면 여러분도 DOM 코드를 사용한 것이다. 예를 들어, var number = document.getElementById("phone").value; 라인은 DOM을 사용하여 특정 엘리먼트를 찾아 그 엘리먼트의 값(이 경우 폼 필드)을 가져온다. 따라서 여러분이 인식 못했더라도 여러분은 document를 JavaScript 코드에 타이핑 할 때마다 DOM을 사용한 것이었다.


여러분이 배웠던 용어를 정비하기 위해 DOM 트리는 객체의 트리지만 보다 구체적으로는 노드 객체들의 트리이다. Ajax 애플리케이션 또는 JavaScript에서 그러한 노드들과 작업하여 엘리먼트와 이것의 콘텐트를 지우고 특정 텍스트 조각을 강조하고 새로운 이미지 엘리먼트를 추가하는 등 특별한 효과를 만들 수 있다. 이 모든 것은 클라이언트 측(웹 브라우저에서 실행되는 코드)에서 발생하기 때문에 이러한 효과는 서버와 통신 없이 즉시 발생한다. 결국 보다 반응성 있는 애플리케이션이 될 것이다.


대부분의 프로그래밍 언어에서 각 노드 유형에 맞는 실제 객체 이름들을 배우고 속성들을 배우고 유형과 캐스팅을 파악해야 한다. 하지만 이중 어떤 것도 JavaScript에서는 필요하지 않다. 변수를 만들어서 여기에 원하는 객체를 할당한다.



var domTree = document;
var phoneNumberElement = document.getElementById("phone");
var phoneNumber = phoneNumberElement.value;


변수를 만들고 여기에 정확한 유형을 핸들하는 유형과 JavaScript는 없다. 결과적으로 JavaScript에서 DOM을 사용하기가 매우 쉬워진다. (다음 글에서는 XML과 관련한 DOM에 초점을 맞춰 설명하겠다.)


결론


지금 여기에서 설명한 것이 DOM의 전부는 아니다. 사실 이 글은 DOM의 개요서에 지나지 않는다. 오늘 설명한 것 이상의 것이 DOM에는 있다.


다음 글에서는 JavaScript에서 DOM을 사용하여 웹 페이지를 만들고 HTML을 수정하고 사용자 인터랙션을 높이는 방법을 설명하겠다. DOM을 주제로 다시 한면 설명하겠다. Ajax 애플리케이션의 중요한 부분인 DOM에 익숙해지기 바란다.


바로 지금 DOM을 더 깊이 연구할 수 있다. DOM 트리로 옮기는 방법, 엘리먼트와 텍스트의 값을 얻는 방법, 노드 리스트를 통해 반복하는 방법 등을 자세히 설명하겠다.


무엇보다도 트리 구조에 대해 생각해 보고 HTML을 통해서 웹 브라우저가 HTML을 마크업의 트리 뷰로 어떻게 전환하는지에 대해 생각해 보고 다음 글에 임하기 바란다. 또한, DOM 트리의 구성을 생각해 보고 이 글에 설명된 특별한 경우를 생각해 보라. 애트리뷰트, 그 안에 엘리먼트와 혼합된 텍스트, 텍스트 콘텐트를 갖고 있지 않은 엘리먼트(img 엘리먼트) 등을 생각해 보라.


이러한 개념을 확실히 이해하고 JavaScript와 DOM의 신택스를 배운다면 도움이 될 것이다.


여기 Listing 23에 대한 답을 제시하겠다. 샘플 코드도 포함되어 있다.




그림 2. Listing 2에 대한 답

The answer to Listing 2




Figure 3. The answer to Listing 3

The answer to Listing 3

by 미소 | 2007/12/30 23:15 | AJAX 기본 | 트랙백(3)

3. Ajax 마스터하기, Part 3: Ajax의 고급 요청 및 응답

난이도 : 초급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 6 월 12 일
2006 년 6 월 12 일 수정

많은 웹 개발자들에게 간단한 요청과 응답을 받는 것은, 사실 필요로 하는 전부이기도 합니다. Ajax를 마스터하고자 하는 개발자들에게는 HTTP 상태 코드, 준비 상태, XMLHttpRequest 객체에 대한 완벽한 이해가 필요합니다. 이 글에서는 다양한 상태 코드들을 보여주고 브라우저가 이를 핸들링하는 방법을 설명합니다.



지난 글에서는 , XMLHttpRequest 객체에 대해 구체적으로 소개했다. 이것은 서버측 애플리케이션이나 스크립트에 대한 요청을 핸들하고, 서버측 컴포넌트에서 리턴 데이터를 처리하는 Ajax 애플리케이션의 주요 특징이다. 모든 Ajax 애플리케이션은 XMLHttpRequest 객체를 사용하기 때문에 Ajax 애플리케이션의 작동은 여기에 얼마나 익숙해지냐에 달려있다.


이번에는 지난 글에서 다루었던 기초를 넘어서 요청 객체의 세 가지 핵심 부분들에 대해 자세히 설명하겠다.



  • HTTP 준비 상태

  • HTTP 상태 코드

  • 요청 유형들


이들 각각은 요청이라는 배관의 일부로 간주된다. 결국, 작은 상세가 이러한 주제들에 대해 기록된다. 하지만 Ajax 프로그래밍을 염두하고 있다면 준비 상태, 상태 코드, 요청에 익숙해 져야 한다. 애플리케이션에서 무엇인가 잘못되고 있다면 준비 상태, HEAD 요청을 하는 방법, 또는 400 상태 코드가 의미하는 것이 무엇인지를 이해하면 5분의 디버깅으로 끝낼 수 있거나 5시간 동안 좌절과 혼돈 속에서 방황할 수 있다.



XMLHttpRequest 또는 XMLHttp: 또 다른 이름의 장미

Microsoft™와 Internet Explorer는 Mozilla, Opera, Safari, 비 Microsoft 계열 브라우저에서 사용되는 XMLHttpRequest 객체 대신 XMLHttp 라는 객체를 사용한다. 단순하게 하기 위해서 이 두 가지 객체 모두 XMLHttpRequest로 칭하기로 한다.
웹을 검색하다 보면 이런 경우가 비일비재 하고 마이크로소프트도 Internet Explorer 7.0의 요청 객체의 이름으로 XMLHttpRequest를 사용하고 있다. (Part 2 참조)



HTTP 준비 상태 먼저 보도록 하자.


HTTP 준비 상태


지난 글에서 XMLHttpRequest 객체는 readyState 라는 속성을 갖고 있다고 설명했다. 이 속성은 서버가 요청을 완료하고 콜백 함수가 그 서버에서 온 데이터를 사용하여 웹 폼이나 페이지를 업데이트 하도록 한다. Listing 1은 이것에 대한 예제이다.(참고자료)



Listing 1. 콜백 함수에서 서버의 응답 처리하기


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);
}
}


이것은 전형적인 준비 상태의 사용법이다. "4"라는 숫자에서 짐작하듯 여러 가지 다른 준비 상태들이 있다.(참고자료)




  • 0: (open()을 호출하기 전에는) 요청이 초기화 되지 않는다.


  • 1: (send()를 호출하기 전에는) 요청은 설정은 되지만 보내지지 않는다.


  • 2: 요청이 보내지고 처리 중에 있다. (이 시점에서 응답에서 콘텐트 헤더를 얻을 수 있다.)


  • 3: 요청이 처리 중에 있다. 부분적인 데이터를 응답에서 사용할 수 있지만 서버는 이 응답으로는 종료되지 않는다.


  • 4: 응답이 완료된다. 서버의 응답을 받고 이를 사용한다.


Ajax 프로그래밍의 기초 이상으로 넘어가고 싶다면 이러한 상태 뿐만 아니라 이들이 언제 발생하고 어떻게 사용하는지에 대해 알아야 한다. 우선, 가장 중요한 것은 어떤 요청 상태가 될 것인지를 배워야 한다. 이는 별로 기분 좋은 일이 아니고 몇 가지 특별한 경우가 포함되어 있다.


숨어있는 준비 상태



readyState 0 (readyState == 0)으로 표시되는 첫 번째 준비 상태는 초기화 되지 않은 요청을 나타낸다. 요청 객체에 대해 open()을 호출하면 속성은 1로 설정된다. 대부분 요청을 초기화 하면서 open()을 호출하기 때문에 readyState == 0을 보는 일은 드물다. 더욱이 초기화 되지 않은 준비 상태는 실제 애플리케이션에서는 쓸모 없다.



Listing 2를 보면 0으로 설정된 준비 상태가 되는 방법을 알 수 있다.



Listing 2. 준비 상태 0


function getSalesData() {
// Create a request object
createRequest();
alert("Ready state is: " + request.readyState);

// Setup (initialize) the request
var url = "/boards/servlet/UpdateBoardSales";
request.open("GET", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}


이 간단한 예제에서 getSalesData()는 웹 페이지가 요청을 시작하기 위해 호출하는 함수이다. (예를 들어, 버튼이 클릭 될 때.) open()이 호출되기 전에 준비 상태를 체크 해야 한다. 그림 1은 이 애플리케이션을 실행한 결과이다.




그림 1. 준비 상태 0

A ready state of 0



0이 4와 같을 때

다중 JavaScript 함수들이 같은 요청 객체를 사용하는 경우, 그 요청 객체가 사용되고 있지 않다는 것을 확인하기 위해 준비 상태 0을 확인하면 문제가 많아질 수 있다. readyState == 4는 완료된 요청을 나타내기 때문에, 4로 설정된 준비 상태인 채로 사용되지 않은 요청 객체를 종종 보게 된다. abort()이라고 하는 요청 객체를 리셋하는 함수가 있지만 이는 여기에 사용하는 것이 아니다. 다중 함수들을 사용해야 한다면 다중 함수에 객체를 공유하는 것 보다 각 함수용 요청 객체를 생성 및 사용하는 것이 낫다.



분명히 이것은 좋지 않다. open()이 호출되지 않았다는 것을 확인해야 한다. 실제 Ajax 프로그래밍에서 이러한 준비 상태의 유일한 사용은 다중 함수들에 같은 XMLHttpRequest 객체를 사용하여 다중 요청을 만드는 경우이다. 그러한 상황에서, 여러분은 요청 객체가 새로운 요청을 만들기 전에 초기화 되지 않은 상태(readyState == 0)에 있다는 것을 확인해야 한다. 이로서 또 다른 함수가 동시에 객체를 사용하는 것을 방지할 수 있다.


진행중인 요청의 준비 상태 보기


0 준비 상태 외에 요청 객체는 전형적인 요청 응답에서 또 다른 준비 상태를 경험하게 된다. 그리고 마지막으로는 준비 상태 4로 끝난다. 이 때는 대부분의 콜백 함수에서 if (request.readyState == 4)가 된다. 서버가 완료되고 웹 페이지를 업데이트 하거나 서버에서 받은 데이터를 기반으로 액션을 취하는 시기이다.


프로세스를 실제로 보는 것은 간단하다. 준비 상태가 4 라면 콜백에서 단순히 코드를 실행시키는 것 대신 콜백이 호출될 때 마다 준비 상태를 출력한다.(Listing 3)



Listing 3. 준비 상태 점검


function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState);
}


이것이 어떻게 실행되는지 확실히 모르겠다면 웹 페이지에서 호출할 함수를 만들고 서버측 컴포넌트로 요청을 보내도록 한다.(Listing 2) 요청을 설정할 때 콜백 함수를 updatePage()로 설정한다. 요청 객체의 onreadystatechange 속성을 updatePage()로 설정한다.


이 코드는 onreadystatechange가 정확히 무엇을 의미하는지 잘 보여주고 있다. 요청의 준비 상태가 변할 때 마다 updatePage()가 호출되고 경고를 받는다. 그림 2는 호출되는 함수의 샘플이다. 이 경우 준비 상태는 1이다.




그림 2. 준비 상태 1

A ready state of 1


코드를 직접 실행해 보라. 웹 페이지에 넣고 이벤트 핸들러를 활성화 한다. (버튼을 누르거나, 요청을 실행하기 위해 설정하는 모든 메소드를 사용하라.) 콜백 함수는 여러 번 실행될 것이다. 요청의 준비 상태가 변할 때 마다 각 준비 상태에 대한 경고를 보게 된다. 이는 각 단계를 통해 요청을 따라가는 최상의 방법이다.


브라우저 차이


이 프로세스에 대해 기본적인 개념이 쌓였다면 여러 가지 다양한 브라우저에서 웹 페이지로 액세스 해보라. 준비 상태가 처리되는 방식에 차이가 있을 것이다. 예를 들어, Firefox 1.5에서, 준비 상태는 다음과 같다.



  • 1

  • 2

  • 3

  • 4


요청의 각 단계들이 다 나타나기 때문에 놀랍지도 않다. 하지만 Safari를 사용하여 같은 애플리케이션에 액세스 하면 재미있는 것을 발견하게 된다. 다음은 Safari 2.0.1에서 보게 되는 상태이다.



  • 2

  • 3

  • 4


Safari는 첫 번째 준비 상태를 배제하고 그 이유에 대해서는 자세히 나와있지 않다. 바로 이것이 Safari 방식이다. 또한 중요한 포인트이기도 하다. 서버에서 데이터를 사용하기 전에 요청의 준비 상태가 4라는 것을 확인하는 것은 좋은 생각인 반면 일시적인 준비 상태에 의존하는 코드를 작성하는 것은 다른 브라우저 마다 다른 결과를 얻을 수 있는 확실한 방법이다.


예를 들어, Opera 8.5를 사용할 때 상황은 더 악화된다.



  • 3

  • 4


Internet Explorer는 다음과 같은 상태로 반응한다.



  • 1

  • 2

  • 3

  • 4


요청과 관련하여 문제가 있다면 문제의 원인을 찾을 수 있는 첫 번째 장소이다. 요청의 준비 상태를 보여주는 경고를 추가하여 상황이 정상적으로 돌아가는지를 확인할 수 있다. Internet Explorer와 Firefox 모두 테스트 하면 네 개의 모든 준비 상태를 얻을 수 있고 각 요청 단계를 검사할 수 있다.


이제는 응답 쪽을 살펴보도록 하자.


응답 데이터


요청 동안에 다양한 준비 상태가 발생할 수 있다는 것을 이해했다면 XMLHttpRequest객체의 또 다른 중요한 부분에 대해 살펴보도록 하자. 바로 responseText 속성이다. 이것은 서버에서 데이터를 얻을 때 사용되는 속성이다. 서버가 요청 처리를 완료하면 그 요청에 응답하는데 필요한 데이터를 요청의 responseText에 둔다. 그런 다음 콜백 함수가 그 데이터를 사용한다.(Listing 1Listing 4 참조)



Listing 4. 서버에서 응답 사용하기


function updatePage() {
if (request.readyState == 4) {
var newTotal = request.responseText;
var totalSoldEl = document.getElementById("total-sold");
var netProfitEl = document.getElementById("net-profit");
replaceText(totalSoldEl, newTotal);

/* Figure out the new net profit */
var boardCostEl = document.getElementById("board-cost");
var boardCost = getText(boardCostEl);
var manCostEl = document.getElementById("man-cost");
var manCost = getText(manCostEl);
var profitPerBoard = boardCost - manCost;
var netProfit = profitPerBoard * newTotal;

/* Update the net profit on the sales form */
netProfit = Math.round(netProfit * 100) / 100;
replaceText(netProfitEl, netProfit);
}



Listing 1은 매우 간단하다. Listing 4는 좀 더 복잡하다. 시작하려면 준비 상태를 검사하고 responseText 속성에서 값을 얻어야 한다.


요청하는 동안 응답 텍스트 보기


준비 상태와 마찬가지로 responseText 속성의 값은 요청의 수명 주기에 걸쳐 변화한다. Listing 5의 코드를 사용하여 요청의 응답 텍스트를 테스트한다. 준비 상태도 마찬가지로 테스트 한다.



Listing 5. responseText 속성 테스트 하기


function updatePage() {
// Output the current ready state
alert("updatePage() called with ready state of " + request.readyState +
" and a response text of '" + request.responseText + "'");
}


브라우저에서 웹 애플리케이션을 열고 요청을 활성화 한다. 이 코드를 최대한 활용하려면 Firefox나 Internet Explorer를 사용한다. 이 두 개의 브라우저는 요청 동안 모든 준비 상태들을 보고하기 때문이다. 준비 상태 2에서 responseText 속성은 정의되지 않는다.(그림 3) JavaScript 콘솔이 열려있었다면 에러가 생겼을 것이다.




그림 3. 준비 상태 2의 응답 텍스트

Response text with a ready state of 2


준비 상태 3에서, 서버는 responseText 속성에 값을 배치한다.(그림 4)




그림 4. 준비 상태 3의 응답 텍스트

Response text with a ready state of 3


준비 상태 3의 응답은 스크립트 마다, 서버 마다, 브라우저 마다 다르다. 애플리케이션을 디버깅 하는데 매우 유용하다.


안전한 데이터 얻기


모든 문서와 스팩들에서는 준비 상태가 4가 되어야지만 데이터를 안전하게 사용할 수 있다고 나와있다. 나를 믿으라. 준비 상태가 3일 때에도 responseText 속성에서 데이터를 얻을 수 있다. 하지만 여러분의 애플리케이션에서 이것에 의존하는 것은 좋지 않은 생각이다. 준비 상태 3에서 완전한 데이터에 의존하는 코드를 작성하는 것은 데이터가 불완전하다는 증거이다.


준비 상태가 3일 때 사용자에게 피드백을 제공하는 것이 좋은 생각이다. alert() 같은 함수를 사용하는 것은 좋지 않다. Ajax를 사용하고 사용자와 경고 다이얼로그 박스를 차단시키는 것은 좋지 않지만 준비 상태가 변할 때 마다 폼이나 페이지에 대한 필드를 업데이트 할 수 있다. 예를 들어, 프로그레스 인디케이터의 넓이를 준비 상태 1에 25 퍼센트, 준비 상태 2에 50 퍼센트, 준비 상태 3에 75 퍼센트, 준비 상태 4에 100 퍼센트를 설정한다.


물론 알다시피, 이 방식은 좋기는 하지만, 브라우저에 의존적이다. Opera에서는 첫 번째 두 개의 준비 상태를 결코 얻지 못하고 Safari는 처음 1 상태를 누락시킨다.


이제 상태 코드에 대해 알아보자.


HTTP 상태 코드


Ajax 프로그래밍 기술에서 준비 상태와 서버의 응답 외에도, Ajax 애플리케이션에 또 다른 고급 레벨을 추가할 수 있다. 바로 HTTP 상태 코드이다. 이 코드들은 Ajax에서는 새우울 것이 없다. 웹에 있는 한 언제나 존재하는 것들이다. 웹 브라우저를 통해 이들을 보았을 것이다.




  • 401: Unauthorized


  • 403: Forbidden


  • 404: Not Found


이 외에도 더 있다.(참고자료) Ajax 애플리케이션에 또 다른 제어 및 응답 레이어를 추가하려면 요청과 반응에 상태 코드를 검사해야 한다.


200: Everything is OK


많은 Ajax 애플리케이션에서 준비 상태를 점검하고 서버 응답으로 온 데이터로 작업하는 콜백 함수를 볼 수 있다.(Listing 6)



Listing 6. 상태 코드를 무시하는 콜백 함수


function updatePage() {
if (request.readyState == 4) {
var response = request.responseText.split("|");
document.getElementById("order").value = response[0];
document.getElementById("address").innerHTML =
response[1].replace(/\n/g, "<br />");
}
}


이것은 근시안적이고 에러를 많이 만드는 Ajax 프로그래밍 방식이다. 스크립트가 인증을 필요로 하는데 요청이 유효 증명을 제공하지 않으면 서버는 403 또는 401 같은 에러를 리턴한다. 하지만 서버가 요청에 응답하기 때문에 준비 상태는 4로 설정될 것이다. 결과적으로 사용자는 유효 데이터를 얻지 못하고 JavaScript가 존재하지 않는 서버 데이터를 사용하려고 할 때 에러를 얻게 된다.


서버가 요청을 완료하고 "Everything is OK" 상태 코드를 리턴했다는 것을 확인하는 것은 간단한 일이다. 이 코드는 "200"이고 XMLHttpRequest 객체의 status 속성을 통해서 보고된다. 서버가 요청으로 끝나고 OK 상태를 리포트 했다는 것을 확인하려면 추가 체크를 콜백 함수에 추가한다.(Listing 7)



Listing 7. 유효 상태 코드 추가


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);

}
}


코드에 몇 줄을 추가하는 것으로 무엇이 잘못되었는지를 알 수 있고 사용자는 아무런 설명이 없는 데이터 데신 유용한 에러 메시지들을 받을 수 있다.


리다이렉션과 재 라우팅


에러에 대해 이야기 하기 전에 Ajax를 사용할 때 걱정하지 않아도 될 부분에 대해 말해두겠다. 바로 리다이렉션이다. HTTP 상태 코드에서, 이것은 300 대의 상태 코드이다.




  • 301: Moved permanently


  • 302: Found (요청이 또 다른 URL/URI로 리다이렉션 된다.)


  • 305: Use Proxy (요청은 프록시를 사용하여 요청 받은 리소스에 액세스 해야 한다.)


Ajax 프로그래머가 리다이렉션에 대해 염려 할 필요가 없는 이유가 두 가지 있다.



  • Ajax 애플리케이션들은 특정 서버측 스크립트, 서블릿, 애플리케이션을 위해 작성된다. 그 컴포넌트를 없애거나 다른 곳으로 이동하기 위함이다. 리소스는 변경되었다는 것을 (이미 이동했기 때문에)알고, 요청에서 URL을 변경하고 이러한 종류의 결과를 절대 만나지 않게 된다.

  • 보다 관련성 있는 이유가 있다. Ajax 애플리케이션과 요청들은 샌드박스화 되어있다. Ajax 요청을 만드는 웹 페이지를 공급하는 도메인은 그러한 요청에 응답해야 하는 도메인이다. 따라서 ebay.com에서 공급 받은 웹 페이지는 Ajax 스타일의 요청을 amazon.com에서 실행되는 스크립트에 할 수 없다. ibm.com 상의 Ajax 애플리케이션은 netbeans.org에서 실행되는 서블릿으로 요청할 수 없다.


결국, 요청은 보안 에러를 만들지 않고서는 또 따른 서버로 리다이렉션 될 수 없다. 그러한 경우에, 상태 코드를 전혀 얻을 수 없다. 디버그 콘솔에 JavaScript 에러를 갖게 된다. 따라서 많은 상태 코드에 대해 생각하는 동안 리다이렉션 코드 정도는 무시할 수 있는 것이다.



엣지 케이스와 하드 케이스

이 부분에서, 신참 프로그래머들은 이러한 혼란에 대해 궁금할 것이다. Ajax 요청의 5 퍼센트 정도는 2와 3 정도의 준비 상태와 403 같은 상태 코드로 작동해야 한다. (사실, 1 퍼센트 미만이다.) 이러한 케이스는 중요하고, 엣지 케이스(edge cases)라고 일컬어진다. 이상한 조건들이 부합되는 특수한 상황인 것이다. 일상적인 것은 아니지만 사용자를 곤란에 처하게 한다.


일반적인 사용자들은 애플리케이션이 정확히 작동하는지 매번 잊지만 그렇지 않을 때는 분명히 기억한다. 엣지 케이스와 하드 케이스를 핸들 할 수 있다면 사이트 사용자들을 만족시킬 수 있을 것이다.



에러


일단, 상태 코드 200을 관리했고 300 계열의 상태 코드는 대충 무시하면 다양한 유형의 에러들을 나타내는 400 계열의 코드만 남게 된다. Listing 7을 보면 에러가 처리되는 동안 사용자에게 출력되는 매우 일반적인 에러 메시지라는 것을 알게 된다. 이것은 올바른 방향으로 가는 단계지만 사용자와 프로그래머에게는 매우 쓸모없는 메시지이다.


우선 소실된 페이지에 대한 지원을 추가한다. 이는 제품 시스템에서는 실제로 발생하지는 않지만 스크립트를 이동시키는 테스트나 정확하지 않은 URL을 입력할 때 자주 일어나는 일이다. 404 에러를 보고하면 혼란스러워 하는 사용자와 프로그래머에게 더 많은 도움말을 제공할 것이다. 예를 들어, 서버 상의 스크립트가 제거되거나 Listing 7에서 그 코드를 사용하면 다음과 같은 에러가 생긴다.(그림 5)




그림 5. 일반적인 에러 핸들링

Generic error handling


사용자는 문제가 무엇인지 잘 모른다. 인증에 관련된 것인지, 소실된 스크립트 인지, 사용자 에러인지 알 수 없다. 몇 가지 간단한 코드 추가로 이 에러는 더욱 구체화 된다. Listing 8을 보면 소실된 스크립트와 인증 에러까지 구체적인 메시지와 함께 처리된다.



Listing 8. 유효 상태 코드 점검


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 if (request.status == 404) {
alert ("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");
} else
alert("status is " + request.status);
}
}


이는 오히려 더 간단하지만 추가 정보 까지 제공한다. 그림 6그림 5와 같은 에러를 보여주지만 이번에는 에러 핸들링 코드가 더 나은 그림을 제공하고 있다.




그림 6. 구체적인 에러 핸들링

Specific error handling


여러분의 애플리케이션에서 인증 때문에 오류가 발생했을 때 사용자 이름과 패스워드를 지우고 에러 메시지를 스크린에 추가하는 것을 고려할 수도 있다. 이와 비슷한 방식이 소실된 스크립트나 다른 400 유형의 에러들을 핸들하는데 사용될 수 있다. 여러분이 어떤 선택을 하든 서버에서 리턴 된 상태 코드를 핸들하는 것으로 시작한다.


추가 요청 유형



XMLHttpRequest 객체를 제어하고 싶다면 HEAD 요청을 레파토리에 추가하라. 이전 두 개의 기사에서 GET 요청을 하는 방법을 설명했다. 앞으로는 POST 요청을 사용하여 서버로 데이터를 보내는 것을 설명하도록 하겠다. 향상된 에러 핸들링과 정보 수집을 위해 HEAD 요청에 대해 배워야 한다.


요청하기


HEAD 요청은 실제로 매우 간단하다. 첫 번째 매개변수로서 "GET" 또는 "POST" 대신 "HEAD"로 open() 메소드를 호출한다.(Listing 9)



Listing 9. HEAD 요청


function getSalesData() {
createRequest();
var url = "/boards/servlet/UpdateBoardSales";
request.open("HEAD", url, true);
request.onreadystatechange = updatePage;
request.send(null);
}


이와 같이 HEAD 요청을 하면 서버는 GET이나 POST 요청 때 처럼 실제 응답을 리턴하지 않는다. 대신, 서버는 응답에 있는 콘텐트가 마지막으로 수정된 시간이 포함된 리소스의 헤더를 리턴한다. 게다가 몇 가지 재미있는 정보도 추가한다. 이들을 사용하여 서버가 리소스를 처리 및 리턴하기 전에 리소스에 대해 알 수 있다.


이와 같은 요청으로 할 수 있는 가장 쉬운 일은 모든 응답 헤더들을 나누는 것이다. 이로서 HEAD 요청을 통해 무엇이 가능한지를 알 수 있다. Listing 10은 HEAD 요청에서 모든 응답 헤더를 출력하는 콜백 함수이다.



Listing 10. HEAD 요청에서 모든 응답 헤더 프린트 하기


function updatePage() {
if (request.readyState == 4) {
alert(request.getAllResponseHeaders());
}
}



그림 7에서 서버에 HEAD 요청을 한 간단한 Ajax 애플리케이션에서 온 응답 헤더를 볼 수 있다.




그림 7. HEAD 요청에서 온 응답 헤더

Response headers from a HEAD request


이러한 헤더들을 개별적으로 사용하여 Ajax 애플리케이션에서 추가 정보나 기능을 제공할 수 있다.


URL 검사


URL이 존재하지 않을 때 404 에러를 검사하는 방법을 이미 보았다. 이것이 일반적인 문제라면, 특정 스크립트나 서블릿이 잠시 동안 오프라인에 있었다면, GET 또는 POST 요청을 하기 전에 URL을 검사해 보는 것이 좋다. HEAD 요청을 하고 콜백 함수에서 404 에러를 검사한다. Listing 11은 샘플 콜백을 보여준다.



Listing 11. URL이 존재하는지 여부 검사


function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
alert("URL exists");
} else if (request.status == 404) {
alert("URL does not exist.");
} else {
alert("Status is: " + request.status);
}
}
}


솔직히 말하면 이것의 가치는 별로 없다. 서버는 요청에 응답해야 하고 응답을 분석하여 응답 헤더에 파퓰레이트 하기 때문에 여러분은 프로세싱 시간을 저축할 수 없다. 게다가 요청을 하고 HEAD 요청을 사용하여 URL이 존재하는지 보는 것에도 많은 시간이 걸린다. Listing 7에서 처럼 에러를 핸들링 하기 보다 GET이나 POST를 사용하여 요청하기 때문이다. 무엇을 사용할 수 있는지를 정확히 아는 데는 가끔 유용하다.


유용한 HEAD 요청


HEAD 요청이 유용한 한 가지 부분은 콘텐트 길이나 콘텐트 유형을 검사할 때이다. 요청을 처리하기 위해 많은 양의 데이터를 보낼 것인지, 서버가 HTML, 텍스트, XML 대신 바이너리 데이터를 리턴해야 할지를 결정할 수 있다. (이 세 가지 모두 바이너리 데이터 보다 JavaScript에서 처리하는 것이 더 쉽다.)


이 경우, 적절한 헤더 이름을 사용하고 이를 XMLHttpRequest 객체의 getResponseHeader() 메소드로 보낸다. 따라서 응답의 길이를 알려면 request.getResponseHeader("Content-Length");를 호출한다. 콘텐트 유형을 알려면 request.getResponseHeader("Content-Type");를 사용한다.


많은 애플리케이션에서 HEAD 요청을 하면 어떤 기능도 추가하지 않고 요청의 속도를 늦출 수 있다. (HEAD 요청을 실행하여 응답에 대한 데이터를 얻고 후속 GET 또는 POST 요청을 통해 응답을 실제로 받는다.) 하지만 스크립트나 서버측 컴포넌트에 대해 확실하지 않은 경우 HEAD 요청으로 기본적인 데이터를 받을 수 있다.


결론



Ajax와 웹 프로그래머에게 이 글은 다소 어려울 것이다. HEAD 요청을 하는 것의 가치는 무엇인가? JavaScript에서 리다이렉션 상태 코드를 핸들해야 하는 때는 언제인가? 이 모두 좋은 질문이다. 간단한 애플리케이션의 경우, 이 모든 것은 가치가 별로 없다.


하지만 웹이 단순한 애플리케이션만 수용하는 것은 아니다. 사용자는 점점 고급화 되고 고객들도 강력한 에러 리포팅을 원한다. 관리자 역시 애플리케이션이 조금만 느려져도 해고를 당하게 된다.


간단한 애플리케이션을 넘어 XMLHttpRequest에 대한 이해를 높여야 할 때이다.



  • 다양한 준비 상태를 이해하고 이들이 브라우저 마다 어떻게 다른지를 이해하면 애플리케이션을 빠르게 디버깅 할 수 있다. 준비 상태에 기반하여 창조적인 기능을 만들고 요청자의 상태에 대해 사용자와 고객에게 보고할 수 있다.

  • 상태 코드를 핸들했다면 스크립트 에러, 예기치 못한 응답들, 엣지 케이스들을 다룰 수 있다. 결국, 애플리케이션은 언제나 잘 작동될 것이다.

  • 여기에 더하여 HEAD 요청을 만들고, URL의 존재를 검사하고 파일이 언제 수정되었는지를 파악하고 사용자가 유효 페이지를 얻었는지를 확인할 수 있다면 언제나 최신의 정보와 강력한 기능으로 사용자들을 만족시킬 수 있을 것이다.


이들은 모두 Ajax의 강점이지만 극히 일부분이다. Ajax를 사용하여 애플리케이션이 에러와 문제들을 부드럽게 해결할 수 있는 강력한 토대를 구현한다면 사용자는 여러분의 사이트를 방문할 것이다. 다음 글에서는 보다 더 재미있고 흥미 있는 주제들을 나누도록 하겠다.


by 미소 | 2007/12/30 23:04 | AJAX 기본 | 트랙백(2)

2. Ajax 마스터하기, Part 2: JavaScript와 Ajax를 이용한 비동기식 요청

난이도 : 중급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 6 월 05 일
2006 년 6 월 05 일 수정

대부분의 웹 애플리케이션들은 서버에서 전체 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>

여기서 다음의 각 단계를 반드시 이해한다.

  1. request라는 새 변수를 생성한 다음 이를 거짓으로 설정한다. XMLHttpRequest 객체가 아직 생성되지 않은 상태에서 거짓 값으로 설정한다.
  2. try/catch 블록에서 추가로 다음과 같은 작업을 한다.
    1. XMLHttpRequest 객체를 시험한 다음 생성한다.
    2. 1번 과정이 실패한 경우(catch (failed)), request가 여전히 거짓으로 설정되어 있는지 확인한다.
  3. request가 여전히 거짓으로 설정되어 있는지 확인한다. (에러가 없는 경우, 거짓으로 설정되지 않는다.)
  4. 에러가 발생한 경우(request가 거짓인 경우), JavaScript 경고를 사용해 문제가 발생했다는 사실을 사용자에게 알린다.

이런 작업은 상당히 간단하다. 실제로 대부분의 JavaScript 및 웹 개발자들은 객체를 읽고 작성하는 것보다는 이해하는 게 더 빠르다. 이제, XMLHttpRequest 객체를 생성하는 일부 에러-증명 코드가 생성되어 에러 여부를 알게 된다.

Microsoft로 처리하기

적어도 인터넷 상에서 이 코드를 적용하기 전까지는 이와 같은 작업이 무난하다. 이 코드를 적용하면 그림 1과 같이 에러가 나오게 된다.


그림 1. 에러 보고 인터넷
Internet Explorer reporting an error
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>

사실 브라우저에 대한 지원기능이 손실되기 쉽다. 따라서 다음과 같이 단계별로 하길 권한다.

  1. request 명의 새 변수를 생성한 다음 이를 거짓으로 설정한다. XMLHttpRequest 객체가 아직 생성되지 않았다는 전제 하에 거짓으로 설정한다.
  2. try/catch 블록에서 추가로 다음과 같은 작업을 한다.
    1. XMLHttpRequest 객체를 시험한 다음 생성한다.
    2. 1번 과정이 실패한 경우 (catch (trymicrosoft)):
      1. 새로운 Microsoft 버전 (Msxml2.XMLHTTP)을 이용해 Microsoft 호환성 객체를 시험한 다음 생성한다.
      2. 전 과정이 실패한 경우(catch (othermicrosoft)), 이전 Microsoft 버전(Microsoft.XMLHTTP)을 이용해 Microsoft 호환성 객체를 시험한 다음 생성한다.
    3. 그래도 실패한 경우(catch (failed))에는, request가 여전히 거짓으로 설정되어 있는지 확인한다.
  3. request가 여전히 거짓으로 설정되어 있는지 다시 확인한다. (에러가 나지 않는 경우 거짓으로 설정되지 않는다.)
  4. 그래도 문제가 발생한 경우(request가 거짓인 경우), JavaScript 경고를 사용해 사용자에게 문제가 발생했다는 것을 알린다.

코드를 변화시키고 Internet Explorer 상에서 다시 한 번 시도해 보면 에러 메시지 없이 생성한 형태를 보게 된다. 본인의 경우엔 그와 같은 시도가 그림 2와 같은 것으로 나타난다.


그림 2. 정상적으로 작동하는 Internet Explorer
Internet Explorer working normally

동적/정적

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 코드
Ajax code popping up an alert

브라우저에 ‘따라 웹 형식에서 경고 팝업을 중지할 때까지 경고가 두 번, 세 번, 심지어는 네 번까지 울린다. 그러면 무슨 일이 벌어지고 있는 것인가? 요청/응답 사이클의 중요 구성요소인 HTTP 준비상태에 대해 고려하지 않았다.

HTTP 준비 상태

초기에 필자는 서버에서 요청이 종료되면 XMLHttpRequestonreadystatechange 속성에서 호출되는 메소드를 탐지한다고 가르쳤다. 사실, 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 형식
The Break Neck form after retrieving customer data

이 글을 마치기 전에 XMLHttpRequest 객체의 중요한 속성 중 하나인 responseXML 속성에 대해 언급한다. 이 속성은 서버가 XML과의 응답을 선택한 경우, XML 응답을 포함한다. (상상이 되는가?) XML 응답 처리는 평범한 텍스트 처리과정과 상당히 다르며, 문장분석 및 문서 객체 모델(DOM)을 포함한다. 다음 글에서는 XML에 대해 다루게 된다. responseXML 은 공통적으로 responseText과 관련된 논의에서 나오기 때문에 언급할 가치가 있는 것이다. 많은 단순 Ajax 애플리케이션의 경우, responseText만 있으면 된다. 하지만 Ajax 애플리케이션을 통해 XML을 처리하는 방법에 대해서도 곧 배우게 된다.





맺음말

XMLHttpRequest 객체에 대해서는 인제 좀 지루하게 들릴지도 모른다. 필자는 단일 객체, 특히 간단한 객체에 대한 전반적인 글을 거의 읽지 못했다. 하지만 Ajax를 사용하고 작성하는 각 페이지 및 애플리케이션에서 계속 XMLHttpRequest 객체를 사용하게 된다. 아직도 XMLHttpRequest 객체에 대해 언급되지 않은 것들이 많은 건 사실이다. 다음 글에서는 요청에서 GETPOST를 사용하고 서버로부터의 응답 및 요청의 컨텐트 헤더를 설정하고 읽어들이는 방법을 배운다. 그러면 요청을 코드화하고 심지어는 요청/응답 모델에서 XML을 다루는 방법을 배우게 될 것이다.

좀 더 상세하게 나가면 일반적으로 사용하는 Ajax 툴킷에 관해서도 알게 된다. 이 툴킷은 본 글에서 논의된 상세사항의 대부분을 실지로 요약한 것이다. 한편 툴킷을 손쉽게 이용하는 경우, 낮은 레벨의 상세사항을 코드화하는 이유에 대해 궁금해할 수도 있다. 사실은 애플리케이션 상에서 발생하는 현상을 이해하지 못하는 경우, 애플리케이션에서 일어나는 에러를 이해하는 게 어려워진다.

따라서, 이와 같은 사항을 간과하거나 지나치면 안 된다. 가변성 툴킷에서 에러가 발생할 경우, 머리를 끄적이면서 e-메일을 보내지 않아도 된다. 직접 XMLHttpRequest 사용법을 이해하면 가장 이상한 문제를 디버그하고 수정하는 것도 쉬워진다. 툴킷에 집중하면서 모든 문제를 해결하지 않는 한 툴킷은 그런대로 괜찮다.

따라서, XMLHttpRequest 객체에 대해 친숙해져라. 사실, 툴킷을 사용하는 Ajax 코드를 실행할 경우 XMLHttpRequest 객체 및 속성, 메소드를 사용해Ajax 코드를 재작성한다. 상당히 좋은 연습이 될 것이며 현재 이 객체에서 벌어지는 현상에 대해 더 잘 이해하게 될 것이다.

다음 글에서는 XMLHttpRequest에 대해 좀 더 자세하게 들어간다. 이 객체에서 어려운 속성(responseXML등의), POST 요청 사용법 및 몇 가지 다른 포맷에서의 데이터를 전송하는 방법을 조사할 것이다. 한 달 동안 코드화 작업을 시작해 코드를 다시 점검한다.

by 미소 | 2007/12/30 20:17 | AJAX 기본 | 트랙백

1. Ajax 마스터하기, Part 1: Ajax 소개

난이도 : 초급

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

2006 년 5 월 29 일
2006 년 5 월 29 일 수정

HTML, JavaScript™, DHTML, DOM으로 구성된 Ajax는 볼품없는 웹 인터페이스를 인터랙티브 Ajax 애플리케이션으로 변형하는 획기적인 방식이다. Ajax 전문가인 필자는 이러한 기술들이 어떻게 작용하는지 전체적인 개요를 비롯하여 세부사항 까지 설명한다. 또한 XMLHttpRequest 객체 같은 Ajax의 중심적인 개념들을 소개한다.

5년 전, XML에 대해 무지했다면 아무도 얘기할 상대가 없는 미운 오리 새끼 신세가 되었을지도 모르겠다. Ruby 프로그램이 주목을 받았던 8개월 전, Ruby 프로그램 언어 기능에 대해 알지 못했던 프로그래머들은 냉수기 관련 산업세계에서 환영 받지 못했다. 그런 것처럼, 최신 기술단계로 입문하고자 한다면 Ajax에 대해 알아야 한다.

하지만 Ajax는 일시적으로 유행하는 툴이 아니다. 웹 사이트를 구축하는 강력한 방식이며 완전히 새로운 언어를 배우는 것보다는 그다지 어렵지 않다.

Ajax에 관해 자세히 들어가기 전에 잠시 Ajax의 기능에 대해 알아보자. 오늘날 애플리케이션을 작성할 시 두 가지 애플리케이션이 있다.

  • 데스크톱 애플리케이션
  • 웹 애플리케이션

두 애플리케이션은 다 친숙한 것들이다. 일반적으로 데스크톱 애플리케이션은 CD상에 배치된 다음 (또는 웹 사이트에서 다운로드) 컴퓨터에 완전 설치된다. 이 애플리케이션은 인터넷을 이용해 업데이트를 다운로드하기도 하지만 애플리케이션 실행 코드는 데스크톱 상에 상주해 있다. 전혀 새로운 것이 아닌 웹 애플리케이션은 웹 서버 상에서 실행되며 웹 브라우저 상에서 접속된다.

하지만 두 애플리케이션에 대한 코드 실행 위치보다 애플리케이션 작동방식 및 애플리케이션과 사용자와의 상호작용방식이 중요하다. 일반적으로 데스크톱 애플리케이션은 상당히 빠르고 (컴퓨터 상에서 실행되고 인터넷 상에서 대기 중인 상태가 안 나온다.), 대형 사용자 인터페이스(일반적으로 운영체제와 상호작용)를 갖추며 상당히 동적이다. 거의 대기시간 없이 메뉴 및 하위 메뉴를 클릭, 지시, 입력하고 풀업한다.

반면 웹 애플리케이션은 가장 최신 것이며 데스크톱에서는 전혀 얻을 수 없는 서비스를 제공한다.(Amazon.com 및 eBay를 생각해 볼 것.) 하지만 웹 애플리케이션 기능으로 인해 서버 응답 대기, 스크린 재생 대기, Request 컴백 및 새 페이지 생성에 관한 대기 기능 등이 부수된다.

분명 지나친 단순화 과정임에는 틀림없지만 기본 개념은 얻게 된다. 이미 눈치를 챘겠지만 Ajax는 데스크톱 애플리케이션 및 항상 업데이트 되는 웹 애플리케이션의 기능 및 상호작용 간의 차이를 줄여주는 역할을 한다. 여러분은 마치 데스크톱 애플리케이션에서 찾은 것처럼 동적 사용자 인터페이스 및 가상 제어기능을 사용한다. 하지만 웹 애플리케이션 상에서 데스크톱 애플리케이션을 이용할 수 있다. 그러면 대기 중인 것이 무엇인가? Ajax 및 볼품없는 웹 인터페이스가 응답 Ajax 애플리케이션으로 변환되는 과정에 대해 살펴보기로 하자.

그러면 대기 중인 것이 무엇인가? Ajax 및 볼품없는 웹 인터페이스가 응답 Ajax 애플리케이션으로 변환되는 과정에 대해 살펴보기로 하자.

오래된 기술, 새로운 기법

Ajax에 관해 살펴보면 Ajax는 실지로 많은 기술들이 응집되어 있다. Ajax의 기본을 마치고 넘어가려면 몇 가지 다른 기술들(필자는 첫 번째 이 시리즈에서 각각의 기술에 관해 설명할 것이다.)을 면밀히 살펴보아야 한다. 하지만 이들 기술 가운데 어느 정도 알고 있는 것이 많은 건 다행이다. 더군다나 각각의 기술 대부분은 Java/Ruby같은 프로그래밍 언어만큼 어려운 게 아니라서 배우기 쉽다.

Ajax 정의

Ajax는 비동기 JavaScript 및 XML의 약어이다.(DHTML도 마찬가지다.) Adaptive Path사의 Jesse James Garrett이 이 약어를 만들어냈으며(참고자료), Jesse에 따르면 이 약어는 두문자어는 아니라고 한다.

Ajax 애플리케이션에 포함된 기본기술은 다음과 같다.

  • 웹 양식을 구축하고 애플리케이션 완료 때까지 사용되는 필드를 식별하는 데 HTML을 사용한다.
  • 자바 스크립트 코드는 Ajax 애플리케이션을 실행하는 중심 코드며 서버 애플리케이션과의 커뮤니케이션을 용이하게 한다.
  • DHTML(동적 HTML)은 웹 양식을 동적으로 업데이트 한다. div, span및 기타 동적 HTML 요소를 사용해 HTML을 마크업 한다.
  • 서버에서 복귀된 HTML 및 (때로) XML 구조를 다루는 데 있어 DOM, 즉 문서 객체 모델(Document Object Model)을 사용한다.

이 기술들에 대해 간략히 요약하고 각 기술의 기능에 대해 좀 더 알아보기로 하는데 각 기술에 관한 자세한 사항은 차후 글에서 다룰 것이다. 우선은 Ajax의 구성요소 및 기술에 대해 친숙해 지는 데 초점을 맞추기로 한다. 자바 스크립트에 익숙할수록 Ajax에 담긴 기술에 관한 일반적인 지식 단계에서 각 기술에 관한 자세한 지식으로 넘어가는 게 더 쉬워진다.(또한 이로 인해 웹 애플리케이션 개발에 관한 문이 열리게 된다.)

XMLHttpRequest 객체

알고자 하는 객체 중 첫 번째는 아마도 가장 생소한 것이 아닌가 싶다. 그 객체는 일명 XMLHttpRequest인데 자바 스크립트 객체의 일종이며 Listing 1에 나와 있는 것처럼 단순하게 생성된다.


Listing 1. 새로운 XMLHttpRequest 객체 생성


<script language="javascript" type="text/javascript">
var xmlHttp = new XMLHttpRequest();
</script>

필자는 다음 글에서 이 객체에 대해 더 논의할 것이다. 하지만 지금 상태에서는 모든 서버 커뮤니케이션을 다루는 객체라는 사실만 알아둔다. 다음 사항으로 가기 전에 잠깐 생각해 보면 자바 스크립트 객체는 XMLHttpRequest를 통해 서버에 전달하는 자바 스크립트 기술의 일종이다. 이 객체는 애플리케이션 흐름이 정상적이지 않으며 Ajax 기술의 많은 부분을 차지하고 있다.

정상적인 웹 애플리케이션에서 사용자는 양식 필드를 기입하며 제출 버튼을 클릭한다. 그러면 전 양식을 서버에 보내며 서버는 처리과정을 통해 양식을 스크립트(일반적으로 PHP, 자바 또는 CGI 과정/이와 유사한 과정)에 전송한다. 스크립트를 실행할 때 스트립트를 통해 완전히 새로운 페이지가 전송된다. 그 페이지는 데이터가 작성된 새로운 양식의 HTML/확인 페이지 또는 원 양식에 기입된 데이터에 근거해 선택된 옵션이 포함된 페이지일 수 있다. 물론, 서버 상의 스크립트/프로그램이 처리되면서 새로운 양식을 다시 보내는 동안 사용자는 대기해야 한다. 서버로부터 데이터를 다시 받을 때까지는 스크린 상에 아무 것도 없게 되며 결국 대화성은 낮게 된다. 사용자는 즉각적으로 응답을 받지 못하며 데스크톱 애플리케이션 상에서 작업하는 기분이 들지 않게 된다.

Ajax는 근본적으로 자바 스크립트 기술 및 웹 양식 및 서버 간의 XMLHttpRequest 객체를 결합한다. 사용자가 웹 양식을 기입할 때 데이터는 직접 서버 스크립트에 전송되지 않고 자바 스크립트 코드에 전달된다. 대신 자바 스크립트 코드는 양식 데이터를 포착해 Request를 서버에 전송한다. 이 과정이 일어나는 동안, 사용자 스크린 상의 양식은 순식간에 나타나거나 깜빡이거나 사라지거나 정지하지 않는다. 즉 자바 스크립트 코드는 몰래 Request를 전송하며 사용자는 Request가 만들어졌는지도 알지 못한다. 게다가 Request를 비동기적으로 전송하기 때문에 더 좋은 상황이 된다. 이는 자바 스크립트에서 서버 응답을 그냥 대기하지 않는다는 것을 의미한다. 따라서, 사용자는 데이터를 계속 기입하고 화면이동하고 애플리케이션을 사용한다.

그런 다음 서버는 자바 스크립트 코드(웹 양식에 대해 아직도 대기 중임)에 데이터를 다시 전송한다. 자바 스크립트 코드에서는 데이터와의 상호기능을 결정하며 연속적으로 양식 필드를 업데이트 하면서 애플리케이션에 즉각적인 응답을 준다. 결국 사용자는 양식을 제출/재생하는 작업 없이 새로운 데이터를 얻게 된다. 자바 스크립트 코드는 데이터를 얻고 계산도 수행하며 또 다른 Request를 전송하며 이런 모든 과정은 사용자 개입 없이도 된다! 이것이 바로 XMLHttpRequest 객체의 장점이다. XMLHttpRequest 객체는 서버와 같이 커뮤니케이션을 주고받고 사용자는 그 과정에서 벌어지는 과정을 알지 못한다. 이로 인해 데스크톱 애플리케이션과 마찬가지로 동적, 상호 반응적인 고도의 양방향 경험을 얻게 되지만 그 속에 인터넷의 모든 장점이 담겨 있다.

자바 스크립트에 대한 부가사항

일단 XMLHttpRequest에 대해 다루게 되면 나머지 자바 스크립트 코드는 상당히 평범한 것들이다. 사실 다음과 같은 기본적인 작업에 자바 스크립트 코드를 이용한다.

  • 양식 데이터 얻기: 자바 스크립트 코드로 HTML 양식에서 데이터를 꺼내 이를 서버에 전송하는 작업이 간단해진다.
  • 양식 상의 값 변환: 필드 값 설정에서 연속적인 이미지 교체작업에 이르는 양식 업데이트 작업 또한 간단하다.
  • HTML 및 XML 구문분석: 자바 스크립트 코드를 이용해 DOM(다음 섹션 참조)을 처리하고 서버에서 다시 전송하는 HTML 양식 및 임의의 XML 데이터에 관한 구조를 다루게 된다.

첫 번째 두 항목에 대해서 여러분은 Listing 2에 나온 대로 getElementById()에 익숙해지려 할 것이다.


Listing 2. 자바 스크립트 코드에서의 필드 값 포착 및 설정
				
// Get the value of the "phone" field and stuff it in a variable called phone
var phone = document.getElementById("phone").value;
// Set some values on a form using an array called response
document.getElementById("order").value = response[0];
document.getElementById("address").value = response[1];

Ajax 애플리케이션에서 특별히 획기적인 사항은 없고 상기 사항 정도면 충분하다. 이에 대해 상당히 복잡한 건 없다는 사실을 깨달아야 한다. 일단 XMLHttpRequest만 정복하면 Ajax 애플리케이션에서 나머지는 대부분 Listing 2에 나온 바와 같이 상당히 독창적인 HTML과 결합된 단순 자바 스크립트 코드다. 그런 다음 가끔 약간의 DOM 작업이 발생하게 된다. 이에 관해 살펴 보자.

DOM으로 종료하기

DOM, 즉 문서 객체 모델이라는 것이 있는데 이는 아주 중요하다. DOM에 대해 듣는 것은 그다지 어렵지 않다고 하는 사람들이 있다. HTML 디자이너에 의해서는 종종 사용되지 않으며 하이-엔드 프로그래밍 작업으로 들어가지 않는 한은 JavaScript 코더에서 흔치 않은 것이 바로 DOM이다. 종종 과중-업무 Java 및 C/C++ 프로그램 상에서 DOM을 종종 많이 활용하게 된다. 사실은 DOM이 배우기 어려운 특성 때문에 명성이 자자해 그 프로그램 상에서 종종 사용하는 것이 아닌가 싶다.

다행히도 JavaScript 기술에 있어 DOM을 활용하는 일은 쉽고 대부분 직관적이다. 이 시점에서 필자는 DOM 사용법에 관해 보여 주고 적어도 이에 대한 몇 가지 코드 예를 제시하려 하지만 이 글의 의도와는 벗어나는 것 같다. DOM에 관해 대략적으로 다루는 것 없이도 Ajax에 대해 깊이 다룰 수 있다. 필자는 차후의 글에서 다시 DOM에 관해 다루려 한다. 하지만 지금 상황에서는 언급하지 않으려 한다. JavaScript 코드와 서버 사이에 XML을 이리저리 전송하고 HTML 양식을 변화시킬 때 DOM에 대해 자세히 다루게 될 것이다. 지금은 DOM없이 효과적인 Ajax 애플리케이션을 작동하는 게 쉬우므로DOM은 논외로 한다.





Request 객체 얻기

Ajax 애플리케이션에 관한 기본적 개념에 대해 배웠으면 몇 가지 특수사항에 대해 살펴 보자. XMLHttpRequest 객체는 Ajax 애플리케이션에서 중요하므로, 아마도 많은 이들에게는 생소한 것일 수도 있다. 거기서 필자는 논의를 시작한다. Listing 1에서 보다시피, XMLHttpRequest 객체를 생성, 사용하는 것은 상당히 쉬워야 한다. 잠깐만 기다려 보시라.

수년 동안 브라우저에 관한 논란은 끊이지 않았고 동일한 브라우저로는 아무 것도 얻을 수 없다는 사실을 기억하는가? 믿건 말건, 소규모 브라우저에서도 이와 같은 논쟁은 끊이지 않고 있다. 더군다나 놀라운 사실은 XMLHttpRequest가 이 논란의 희생양 중 하나라는 것이다. 따라서 XMLHttpRequest 객체를 작동시키기 위해선 몇 가지 다른 작업을 해야 한다. 단계별로 설명하겠다.

Microsoft 브라우저 다루기

Microsoft 브라우저, Internet Explorer는 XML을 다룰 시 MSXML 구문분석계를 사용한다.(참고자료) Internet Explorer 상에서 다뤄야 할 Ajax 애플리케이션을 작성할 시 독특한 방식으로 XMLHttpRequest 객체를 작성해야 한다.

하지만 그렇게 간단한 작업은 아니다. Internet Explorer에 설치된 JavaScript 기술 버전에 따라 MSXML 버전도 변하게 되며 실지로 2개의 버전이 있다. 따라서 두 경우를 다루는 코드를 작성해야 한다. Microsoft 브라우저 상에서 XMLHttpRequest 객체를 생성하는 데 필요한 코드에 관해선 Listing 3을 보라.


Listing 3. Microsoft 브라우저 상에서 XMLHttpRequest 객체 생성
				
var xmlHttp = false;
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP")
;} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}

모든 작업이 정확히 맞아떨어지는 것은 아니다. 하지만 그래도 상관없다. 이 시리즈가 끝나기 전에 JavaScript 프로그래밍, 에러 취급 및 조건부 번역 및 기타 사항에 관해 자세히 다루게 될 것이다. 지금 현 상태에서는 두 가지 중심 라인만 다루고자 한다.

xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");

and

xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");.

간단히 말해서, 이 코드로 MSXML의 한 버전을 이용해 XMLHttpRequest 객체 생성을 기한다. 하지만 객체가 생성되지 않는 경우 다른 버전을 사용해 XMLHttpRequest 객체를 생성한다. 두 코드 다 작동되지 않는 경우 xmlHttp 변수는 거짓으로 설정되고 작동되지 않는 것이 있다는 것을 코드에 알려 준다. 그럴 경우, 비-Microsoft 브라우저가 있을 가능성이 있다. 따라서 객체 생성을 위해선 다른 코드를 사용해야 한다.

Mozilla 및 Microsoft 브라우저 다루기

인터넷 브라우저를 선택하지 않거나 비-Microsoft 브라우저를 작성할 경우 다른 코드가 필요하다. 사실, 이 라인은 Listing 1에서 봤던 단순 코드라인이다.

var xmlHttp = new XMLHttpRequest object;.

이 단순한 라인으로 Mozilla, Firefox, Safari, Opera 및 임의의 양식/형태에서 Ajax애플리케이션을 지원하는 기타 비-Microsoft 브라우저에서 XMLHttpRequest 객체를 생성한다.

지원기능 통합

여기서 모든 브라우저를 지원하는 것이 중요하다. Internet Explorer/비-Microsoft 브라우저에서만 작동되는 애플리케이션을 작성하는 사람이 어디 있겠는가? 또한 더 심한 경우, 애플리케이션을 두 번 작성하고자 하는가? 물론 아니라고 믿는다. 따라서 코드에선 Internet Explorer 및 비-Microsoft 브라우저를 지원하는 기능이 포함되어야 한다. Listing 4에서는 다중-브라우저 방식으로 작동하는 코드에 대해 나와 있다.


Listing 4. 다중 브라우저 방식으로 XMLHttpRequest 객체 생성하기


/* Create a new XMLHttpRequest object to talk to the Web server */
var xmlHttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
xmlHttp = false;
}
}
@end @*/

if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
xmlHttp = new XMLHttpRequest();
}

지금 현재로선, 주석 및 @cc_on와 같은 어려운 태그를 무시한다. 이들은 다음 글에서 깊이 다룰 JavaScript 컴파일러 명령으로 오로지 XMLHttpRequest 객체 상에만 초점이 맞추어져 있다. 이 코드에 관한 핵심은 세 가지 과정으로 요약된다.

  1. 변수 xmlHttp를 생성해 앞으로 생성할 XMLHttpRequest 객체를 참조한다.
  2. Microsoft 브라우저에서의 객체를 시도, 생성한다.
    • Msxml2.XMLHTTP 객체를 사용해 XMLHttpRequest 객체를 시도, 생성한다.
    • 과정이 실패할 경우, Microsoft.XMLHTTP 객체를 사용해 XMLHttpRequest 객체를 시도, 생성한다.
  3. xmlHttp가 아직도 설정되지 않은 경우 비-Microsoft 방식으로 XMLHttpRequest 객체를 생성한다.

위 과정 끝 단계 시 사용자가 실행하는 브라우저 종류에 관계없이 xmlHttp의 경우 유효한 XMLHttpRequest 객체를 인용한다.

보안

보안이란 무엇인가? 오늘날 브라우저는 사용자들에게 보안 레벨을 올리고 JavaScript 기술을 생성하며 브라우저 옵션을 해제하는 기능을 제공한다. 이 경우 코드가 작동되지 않는 경우도 있을 수 있다. 그 때 발생하는 문제를 적절하게 다뤄야 한다. 이에 관한 내용은 적어도 기사 한 분량을 차지할 정도라 나중에 다루기로 하겠다.(긴 시리즈가 될 것 같다, 그렇지 않은가? 하지만 걱정 마시라. 과정을 다 배우고 나면 이와 관련된 모든 사항을 숙달할 테니까.) 현재로선 강력하지만 완전하지 않은 코드를 작성하는 중이다. 이 코드는 Ajax 애플리케이션을 관리하는 데 좋다.





Ajax 세계에서의 Request/Response

인제 Ajax 애플리케이션에 대해 이해하고 XMLHttpRequest 객체 및 객체 생성에 관한 기본적인 개념을 얻는다. 자세히 읽다 보면 Ajax 애플리케이션은 웹 애플리케이션에 제출되는 HTML 양식보단 서버 상의 임의의 웹 애플리케이션에 대화하는 JavaScript 기술이라는 사실을 알게 된다.

그러면 빠진 부분은 어떤 것인가? 실질적인 XMLHttpRequest 객체 사용법이다. 이 객체는 작성하는 각각의 Ajax 애플리케이션에서 일정 형태로 사용하는 중요 코드라 Ajax 애플리케이션이 포함된 기본 Request/응답 모델 모양을 통해 객체 사용법을 빨리 익힌다.

Request 만들기

새로운 XMLHttpRequest 객체가 있는 경우 이를 시험해 보자. 먼저 웹 페이지에서 호출하는 JavaScript 방법이 필요하다.(사용자가 텍스트에서 입력하거나 메뉴에서 옵션을 선택할 시와 같음.) 그 다음, 거의 모든 Ajax 애플리케이션에서의 동일한 기본 아웃라인을 따른다.

  1. 웹 양식으로부터 필요한 모든 데이터 얻기
  2. 연결할 URL 구축
  3. 서버 연결
  4. 서버 실행 종료 시 서버 실행 기능 설정
  5. Request 전송

Listing 5는 위의 순서대로 5단계를 진행하는 Ajax 방법의 예에 관해 나와 있다.


Listing 5. Ajax가 포함된 Request 만들기

function callServer() {
// Get the city and state from the web form
var city = document.getElementById("city").value;
var state = document.getElementById("state").value;
// Only go on if there are values for both fields
if ((city == null) || (city == "")) return;
if ((state == null) || (state == "")) return;

// Build the URL to connect to
var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);

// Open a connection to the server
xmlHttp.open("GET", url, true);

// Setup a function for the server to run when it's done
xmlHttp.onreadystatechange = updatePage;

// Send the request
xmlHttp.send(null);
}

Ajax 코드에 관한 많은 것이 명백하다. Ajax 코드의 첫번째 비트는 몇 가지 양식 필드 값을 포착하는 기본 JavaScript 코드를 사용한다. 그런 다음 이 코드에서는 연결 최종 목적지로 PHP 스크립트를 설정한다.

PHP 스크립트의 URL을 지정한 다음(양식에서 나온) 단순한 GET 매개변수를 이용해 이 URL에 도시 및 국가를 추가한다. 그 다음 연결하면 먼저 XMLHttpRequest 객체가 작동되는 것을 보게 된다. 연결방법은 연결 URL 뿐만 아니라, GET 매개변수에도 나와 있다. 최종 매개변수를 true로 설정한 경우, 이 매개변수에선 비동기식 연결(Ajax를 만든다.)을 요구한다. false로 설정한 경우엔 Request를 만들 시 서버 상에서 Ajax에서의 JavaScript 코드가 대기하고 응답을 받을 때 코드가 지속된다. 사용자는 최종 매개변수를 true로 설정하면서 서버에서 배경에 있는 Request를 처리하는 동안 사용자는 웹 양식(심지어는 기타 JavaScript 방식)을 여전히 사용한다.

한편 xmlHttp(이것은 XMLHttpRequest 객체의 인스턴스라는 사실을 기억하라.)의 onreadystatechange 속성으로 서버 실행이 종료될 시(5분/5시간 내에 종료될 수 있음) 서버 기능을 명령한다. 이 코드는 서버 상에서 대기하지 않기 때문에 서버가 기능을 인식해 서버에 응답할 수 있도록 하는 게 필요하다. 이 경우 서버에서 Request를 처리하면서 종료 시 이른바 updatePage()라 불리는 특수 방법을 트리거한다.

최종적으로 send() 코드를 0(null) 값으로 호출한다. 데이터를 추가해 이를 서버에 전송하므로 Request에는 추가해서 보낼 게 없다. 이렇게 되면 Request를 발송하고 서버는 서버에 요구된 기능을 실행한다.

이 코드에서 나오는 것이 없는 경우, 코드가 상당히 간단하다는 것을 명심하라. 이 코드는 Ajax 애플리케이션의 비동기적 특성을 제외하고는 상당히 단순하다. 이 코드를 통해 복잡한 HTTP Request/응답 코드보다는 근사한 애플리케이션 및 인터페이스에 완전 초점을 맞추도록 한다는 사실을 여러분은 높게 평가할 것이다.

Listing 5의 코드는 코드를 얻는 방법만큼이나 쉽다. 데이터는 단순 텍스트이고 Request URL의 일부로 포함된다. GET 매개변수는 더 복잡한 POST대신 Request를 전송한다. 여기에 덧붙일 XML/컨텐츠 헤더가 없고 Request 본체에 전송할 데이터도 없다. 이게 바로 Ajax 유토피아다.

그렇다고 미리 겁먹지 마라. 시리즈가 계속될수록 문제는 더 복잡해진다. 그 때는 POST Request를 전송하는 방법, Request 헤더 및 컨텐츠 형식을 설정하는 방법, 메시지에 XML을 설정하는 방법 및 Request에 보안기능을 추가하는 방법을 배우게 되는데 배우는 목록만 해도 상당히 길다! 지금은 이런 어려운 주제에 대해 신경 쓰지 말자! 그냥 기본만 충실하게 익히면 Ajax 전체 툴을 구축하게 된다.

응답 취급과정

이제 서버 응답을 실지로 취급해야 한다. 이 시점에서는 정말로 두 가지 사항만 알면 된다.

  • xmlHttp.readyState 속성이 4와 같을 때까지는 어떤 작업도 해선 안 된다.
  • 서버는 xmlHttp.responseText 속성에 응답한다.

2가지 항목 중 첫번째 항목인 준비 상태에 관해선 다음 글에서 대부분 다룰 것이다. 그 때는 HTTP Request 단계에 대해 알고 싶은 것 이상으로 배우게 된다. 지금 현재로선, xmlHttp.responseText 속성 값 4를 단순 점검하는 경우, 작업이 계속 진행된다.(다음 글에서 기대할 만한 사항이 나오게 된다.) 서버 응답을 얻기 위해 xmlHttp.readyState 속성을 사용하는 과정인 두 번째 항목은 쉽다. Listing 6Listing 5에서 전송된 값에 근거해 서버에서 호출하는 방법에 관한 예를 보여준다.


Listing 6. 서버 응답 취급하기


function updatePage() {
if (xmlHttp.readyState == 4) {
var response = xmlHttp.responseText;
document.getElementById("zipCode").value = response;
}
}

다시 보면, xmlHttp.readyState 코드는 그리 어렵거나 복잡하지 않다. 이 코드는 서버에서 해당 준비 상태로의 호출을 대기하고 서버에서 다시 복귀되는 값(이 경우, 사용자 기입 도시 및 국가에 대한 ZIP 코드)을 사용해 또 다른 형태의 양식 필드를 설정한다. 그 결과, zipCode 필드는 ZIP 코드와 함께 갑자기 나타난다. 하지만 사용자는 버튼을 클릭해서는 안 된다! 그게 바로 이전에 말했던 데스크톱 애플리케이션이다. Ajax 코드에는 응답성, 동적 상태 외의 더 많은 것이 있다. 독자들은 zipCode가 정상 텍스트 필드라는 것을 눈치챘을지도 모른다.

일단 서버에서 zipCode를 복귀시키고 updatePage() 방식으로 도시/국가 ZIP 코드와 함께 zipCode 필드 값을 설정하는 경우 사용자는 값을 무효로 한다. 값을 무효로 하는 데는 두 가지 이유가 있다. 예에서 나오는 상황을 단순화시키고, 때로는 사용자가 서버에서 명령하는 것을 무효로 하기 위해서다. 이 두 가지를 명심하라. 좋은 사용자-인터페이스 설계를 위해 중요하다.





웹 양식 다루기

그러면 이 글에서 다룰 게 남아 있는가? 그다지 많지 않다. 양식에 기입할 정보를 포착해 이를 서버에 전송하고 응답에 관해 취급할 또 다른 JavaScript 방법을 제공하면서 심지어는 다시 응답될 때 필드 값을 설정하기까지 하는 JavaScript 방법을 다룬다. 여기서는 첫번째 JavaScript 방법을 호출해 전 과정을 시작하기만 하면 된다. 분명 HTTL 양식에 버튼을 추가하지만 2001년 버전과 거의 동일하다고 생각되지 않는가? Listing 7과 같이 JavaScript 기술을 활용한다.


Listing 7. Ajax 프로세스 시작



<form>
<p>City: <input type="text" name="city" id="city" size="25"
onChange="callServer();" /></p>
<p>State: <input type="text" name="state" id="state" size="25"
onChange="callServer();" /></p>
<p>Zip Code: <input type="text" name="zipCode" id="zipCode" size="5" /></p>
</form>

이런 단면이 루틴 코드의 한 단면 이상을 보여준다고 생각된다면 맞는 말이다. – 그렇다! 사용자가 도시/국가 필드에 관한 새로운 값을 입력할 경우 callServer() 방식을 전송한 다음 Ajax 애플리케이션이 시작된다. 이제 여러 상황을 다룰 만하다고 느껴지기 시작하는가? 좋다! 바로 그거다!





맺음말

이 시점에서 적어도 리소스 란에서 Ajax 애플리케이션에 관해 깊숙이 알려고 하는 경우, 첫번째Ajax 애플리케이션을 작성할 준비가 되어 있지 않을 게다. 하지만 이런 애플리케이션이 작동하는 기본 개념 및 XMLHttpRequest 객체의 기본 개념을 이해하기 시작한 경우 이 객체, JavaScript-서버 간 대화 취급방식, HTML 양식 취급 및 심지어 DOM 관리 방식까지 모든 것을 배워야 한다.

지금 현재로선, Ajax 애플리케이션이 얼마나 강력한 툴인지 생각하는 데 시간을 보낸다. 버튼만 클릭할 뿐만 아니라 필드에 입력하고 콤보 상자에서 옵션을 선택하고 심지어는 마우스를 스크린 주위에 끄는 경우 응답하는 웹 형식을 상상해 본다. 비동기식의 정확한 의미 및 Request 상에서 응답하기 위해 서버 상에서 실행하지만 대기하지 않는 JavaScript 코드에 관해 생각해 본다. 여러분이 부딪치는 문제의 종류는 어떤 것인가? 어떤 영역의 문제에 주의를 기울일 것인가? 프로그래밍에 이 새로운 접근방식을 설명하기 위해 양식 설계를 변환하는 방법은 어떤 것인가?

이런 문제에 관해 실지로 생각할 시간을 보낸다면 잘라 붙이는 코드를 가지고 이를 잘 이해하지 못하는 애플리케이션에 포함시키는 것보다는 훨씬 더 낫다. 다음 글에서는 이와 같은 개념을 실제 작업에 응용해 본 작업에서처럼 애플리케이션을 만들어야 하는 코드에 관한 자세한 정보를 제공하기로 한다. 그 때까지 Ajax 애플리케이션의 가능성을 마음껏 즐겨라.

by 미소 | 2007/12/30 19:44 | AJAX 기본

◀ 이전 페이지다음 페이지 ▶