앞서 Part 1에서 '웹 페이지에 삽입하기 쉬운 날씨 뱃지 만들기'라는 문제를 정의했다. 날씨 뱃지는 미국립 기상청(United States National Weather Service, NWS)에서 가져온 정보에다 Ajax 기술을 적용해 만든다. NWS가 제공하는 자료는 XML 형식이며 15분마다 갱신된다.
이 연재 기사에서는 날씨 뱃지를 구현하는 방법 네 가지를 살펴본다. Part 1에서 설명했던 첫 번째 접근 방식은 아파치 웹 서버에 프록시 규칙을 설정하여 NWS XML 정보를 브라우저로 가져온다. 그런 다음, 자바스크립트 코드로 DOM에서 필요한 정보를 추출한 후 HTML로 변환하여 브라우저에 표시한다.
이번 기사에서는 두 번째와 세 번째 방법을 살펴본다. 두 방법 모두 XSLT를 사용한다는 공통점이 있다.
XSLT는 XML을 질의해 다른 형식으로 변환해주는 언어다. 정확히 우리 날씨 뱃지에 필요한 기능이라 하겠다. 날씨 뱃지에서는 XML 형식인 날씨 정보를 사용자에게 (혹은 브라우저에게) 좀 더 친근한 형식인 HTML로 바꿔야 하기 때문이다. 또한 NWS에서 가져온 정보는 날씨 뱃지에 필요한 정보 외에도 많은 정보가 담겨 있다. 따라서 날씨 뱃지에 필요한 정보만 추출해 HTML로 변환해야 한다. 즉 XSLT는 우리의 목적 두 가지(필요한 정보만 추출하기, 원하는 형식으로 변환하기)에 모두 부합하는 언어다.
구체적인 XSLT 개념은 기사 범위를 벗어나므로 다루지 않는다. XSLT를 깊이 있게 이해하고 싶다면 developerWorks 기사인 "What kind of language is XSLT?"(참고자료 참조)를 참고한다.
XSLT 언어는 문법 자체가 유효한 XML이라는 점에서 다른 컴퓨터 언어와 다르다. C, 자바(Java™), 펄, 파이썬 등과 같은 프로그래밍 언어에 익숙한 프로그래머라면 다소 혼란스러울지도 모르겠다.
날짜 뱃지를 만드는 두 번째 접근 방식과 세 번째 접근 방식 모두가 동일한 XSLT를 사용하므로, 우선 XSLT로 XML을 변환하는 방법부터 설명하겠다. 그런 다음, 나중에 각 접근 방식을 살펴보면서 XSLT 변환 코드가 어디에 어떻게 사용되는지 살펴본다.
가장 먼저, NWS가 제공하는 XML 정보 형식을 다시 한 번 살펴보자. Listing 1은 NWS XML 정보를 축약한 버전이다.
Listing 1. 예제 NWS XML 정보 파일 KNGU.xml(축약)
<?xml version="1.0" encoding="ISO-8859-1"?>
<current_observation version="1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://www.weather.gov/data/current_obs/current_observation.xsd">
<credit>NOAA's National Weather Service</credit>
<credit_URL>http://weather.gov/</credit_URL>
<image>
<url>http://weather.gov/images/xml_logo.gif</url>
<title>NOAA's National Weather Service</title>
<link>http://weather.gov</link>
</image>
<suggested_pickup>15 minutes after the hour</suggested_pickup>
<suggested_pickup_period>60</suggested_pickup_period>
<location>Norfolk, Naval Air Station, VA</location>
<station_id>KNGU</station_id>
<latitude>36.94</latitude>
<longitude>-76.28</longitude>
<observation_time>
Last Updated on Jan 7, 2:53 pm EST
</observation_time>
<observation_time_rfc822>
Mon, 7 Jan 2008 14:53:00 -0500 EST
</observation_time_rfc822>
<weather>Fair</weather>
<temperature_string>74 F (23 C)</temperature_string>
<temp_f>74</temp_f>
<temp_c>23</temp_c>
<relative_humidity>34</relative_humidity>
<wind_string>From the Southwest at 9 Gusting to 18 MPH</wind_string>
<wind_dir>Southwest</wind_dir>
<wind_degrees>240</wind_degrees>
<visibility_mi>10.00</visibility_mi>
<icon_url_base>
http://weather.gov/weather/images/fcicons/
</icon_url_base>
<icon_url_name>
skc.jpg
</icon_url_name>
<disclaimer_url>http://weather.gov/disclaimer.html</disclaimer_url>
<copyright_url>http://weather.gov/disclaimer.html</copyright_url>
<privacy_policy_url>http://weather.gov/notice.html</privacy_policy_url>
</current_observation>
|
Listing 1에서 우리는 굵은 글씨로 표시된 정보에만 관심이 있다. 그래서 우리 XSLT 프로그램은 먼저 굵은 글씨에 해당하는 요소만 추출한다. 그런 다음, 추출한 정보를 (브라우저에 표시하기 좋도록) HTML로 변환한다.
Listing 2는 이와 같은 두 가지 목적을 수행하는 XSLT 프로그램이다.
Listing 2. weather2html.xsl, 날씨 정보 XSLT 프로그램
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/current_observation">
<center>
<b><xsl:value-of select="location" /></b><br/>
<xsl:value-of select="weather" /><br/>
<xsl:variable name="icon_url_base" select="icon_url_base"/>
<xsl:variable name="icon_url_name" select="icon_url_name"/>
<img border='0' src='{$icon_url_base}{$icon_url_name}'/><br/>
<xsl:value-of select="temperature_string" /><br/>
Wind: <xsl:value-of select="wind_string" /><br/>
Humidity: <xsl:value-of select="relative_humidity" />%<br/>
Visibility: <xsl:value-of select="visibility_mi" /> miles<br/>
<br/><span style='font-size: 0.8em; font-weight: bold;'>
<xsl:value-of select="observation_time" /></span><br/>
</center>
</xsl:template>
</xsl:stylesheet>
|
가장 먼저 다음 행에 주목한다.
<xsl:output method="html" /> |
출력 형식이 HTML이라는 사실을 XSLT 프로세서에 알려주는 행이다. 별도로 지정하지 않으면 기본적으로 XML 형식이 출력된다.
위 XSLT 프로그램은 마치 HTML과 XML을 혼합한 듯하다. 사실 실제로도 그렇다.
<xsl:
로 시작하는 XML 요소는 XSLT 문이다. 나머지는 그대로 출력된다
예를 들어 다음 행을 보자.
<b><xsl:value-of select="location" /></b><br/> |
xsl:value-of 요소는 'XML 입력 파일에서 location이라는 요소를 찾아 값을 추출한 후 xsl:value-of 태그 대신 넣으라'고 XSLT 프로세서에 지시한다. 결과는 다음과 같다.
<b>Norfolk, Naval Air Station, VA</b><br/> |
펄이나 루비 같은 프로그래밍 언어처럼 XSLT도 언어 해석기를 사용한다. XSLT 언어 해석기를 흔히 XSLT 프로세서라고 부른다. 하지만 XSLT는 범용 프로그래밍 언어가 아니다. XSLT는 XML 자료 파일을 하나만 변환할 목적으로 사용한다. 그래서 대다수 XSLT 프로세서는 입력 파일을 두 개, 즉 XSLT 프로그램과 변환할 XML 파일을 받는다.
많은 리눅스(Linux®) 배포판이 xsltproc이라는 명령행 XSLT 프로세서를 제공한다. 유형이 비슷한 여느 도구와 마찬가지로, xsltproc 역시 XSLT 스크립트를 디버깅하고 다듬을 때 아주 유용한 도구다.
xsltproc은 매개변수 두 개를 받는다. 하나는 XSLT 프로그램이고 다른 하나는 XSLT 프로그램이 변환할 XML 파일이다. 앞서 우리는 NWS로부터 Norfolk Naval Air Station(버지니아 주) 날씨 정보를 가져와 KNGU.xml 파일에 저장했다(여기서 KNGU는 1부에서 설명한 스테이션 ID다. 스테이션 ID는 네 자로 이루어진 고유 ID다). xsltproc으로 Listing 2에 나온 우리가 만든 XSLT 프로그램을 돌려본다.
xsltproc weather2html.xsl KNGU.xml |
XSLT 프로세서는 weather2html.xsl에서 기술하는 변환을 KNGU.xml에 적용한 후 결과를 표준 출력에다 쓴다. 결과는 Listing 3과 같다.
Listing 3. XSLT 프로세서가 KNGU.xml을 처리한 결과
<center>
<b>Norfolk, Naval Air Station, VA</b><br>
Fair<br>
<img border="0"
src="http://weather.gov/weather/images/fcicons/skc.jpg"><br>
57 F (14 C)<br>
Wind: From the West at 7 MPH<br>
Humidity: 58%<br>
Visibility: 7.00 miles<br>
<br>
<span style="font-size: 0.8em; font-weight: bold;">
Last Updated on Oct 12, 7:53 am EDT
</span>
<br>
</center>
|
이제 위에서 설명한 XSLT 방법을 날씨 뱃지 문제에 적용해 보자. 접근 방식 2에서는 1) 서버쪽 스크립트가 NWS 서버로부터 날씨 정보를 가져온 후, 2) XSLT를 사용하여 XML을 HTML로 변환한 다음, 3) HTML 코드를 브라우저로 전송한다. 그러면 브라우저는 서버가 보내온 HTML 코드를 DIV 태그에 삽입한다.
그림 1은 접근 방식 2가 사용하는 자료 파이프라인이다. 자료는 NWS 서버에서 우리 서버로 이동하고, 우리 서버에 있는 서버쪽 스크립트가 XML 정보를 HTML로 변환한다. 파이프라인은 브라우저에서 끝나는데, 브라우저는 서버로부터 HTML 코드를 받아 웹 페이지에 삽입한다.
그림 1. 접근 방식 2가 따르는 자료 파이프라인
따라서 접근 방식 2는 서버쪽에서 XSLT 변환을 수행할 프로그램이 필요하다. Listing 4에서 보듯이, 나는 펄로 프로그램을 구현했다. 하지만 편의에 따라 다른 언어를 사용해도 무방하다. 서버쪽 스크립트는 브라우저에서 XMLHttpRequest를 호출하면 실행된다. Ajax 자바스크립트 코드는 서버쪽 스크립트에 매개변수 하나를 보낸다. 이 때 전송하는 매개변수는 NWS 서버에서 날씨 XML 정보를 가져올 때 필요한 NWS 스테이션 ID다.
Listing 4. weather_xml2html.cgi 펄 스크립트
#!/usr/bin/perl
use strict;
my $DATA_DIR = "/var/www/html/xml_weather/data";
my $XSL_FILE = "$DATA_DIR/weather2html.xsl";
my $XSLTPROC = "/usr/bin/xsltproc";
my $HTTP_BIN = "/usr/bin/wget -q -O - ";
my $URL_FORMAT = "http://www.nws.noaa.gov/data/current_obs/%s.xml";
# NOAA 위치 키 획득:
my $location = $ENV{QUERY_STRING};
# 위치 키를 확인하는 최소 점검 루틴:
if ($location !~ /^[\d\w]{4}$/) {
print "Content-type: text/html\n\n";
print "Unknown location ($location).\n";
exit 1;
}
# URL 생성:
my $url = sprintf ($URL_FORMAT, $location);
# 명령 생성:
my $cmd = "$HTTP_BIN '$url' 2> /dev/null | $XSLTPROC $XSL_FILE - ";
print "Content-type: text/html\n\n";
open (my $IPIPE, "$cmd |");
while (<$IPIPE>) {
print $_;
}
close $IPIPE;
|
그러면 아파치 웹 서버가 Listing 4 스크립트를 실행한다. 스크립트가 사용할 네 자리 스테이션 ID는 아피치 환경변수 QUERY_STRING에 들어 있다.
위 펄 스크립트는 외부 명령 두 개를 사용한다. 하나는 xsltproc이고, 다른 하나는 wget이다. xsltproc은 앞서 설명한 명령행 XSLT 프로세서다. wget은 자유 소프트웨어 재단에서 배포하는 자유 소프트웨어로, 대다수 리눅스 배포판에 미리 설치되어 있다. wget은 다른 웹 사이트에서 웹 페이지나 웹 자원을 가져온다. 위 펄 스크립트는 wget 유틸리티를 사용하여 NWS 서버로부터 XML 파일을 가져온다.
위 펄 스크립트는 (리눅스 명령행에서 실행하면) 다음과 같은 명령 파이프라인을 구성한다.
/usr/bin/wget -q -O - http://www.nws.noaa.gov/data/current_obs/KNGU.xml \ | /usr/bin/xsltproc /var/www/html/xml_weather/data/weather2html.xsl - |
펄 스크립트는 위 명령 결과를 읽어들여 표준 출력으로 보낸다. 앞서 언급했듯이, 서버쪽 스크립트는 브라우저 자바 스크립트 코드가 XMLHttpRequest를 사용할 때 실행된다. 따라서 서버쪽 스크립트 출력 결과는 응답으로 브라우저에 보내진다.
접근 방식 1과 달리, 접근 방식 2는 아파치 프록시 규칙이 필요하지 않다. weather_xml2html.cgi 스크립트가 지능적인 프록시 역할도 수행하기 때문이다. 스크립트 자체가 우리 서버에 있으므로 Part 1에서 설명했던 '같은 도메인' 문제도 해결된다(참고자료 참조).
서버쪽 스크립트는 이미 형식이 갖춰진 HTML 코드(날씨 뱃지 정보)를 반환한다. 따라서 클라이언트쪽 날씨 뱃지 라이브러리가 할 일은 거의 없다. Listing 5는 접근 방식 2에 필요한 자바스크립트 코드다.
Listing 5.
weather_badge_intel_proxy.js에서 weather_badge() 함수
function weather_badge (nws_id, div_name) {
var ajax = new Ajax
("/cgi-bin/xml_weather/weather_xml2html.cgi?",
nws_id,
"GET",
function (req) {
var div = document.getElementById (div_name);
div.innerHTML = req.responseText;
}
);
ajax.request ();
}
|
여기서 weather_badge() 함수는 (적절한 스테이션 ID를 주고서) 서버쪽 스크립트를 호출한 후 서버가 반환한 HTML 코드를 (innerHTML 속성을 사용하여) 목표 DIV 태그에 삽입하면 그만이다.
이런 접근 방식은 상당한 책임을 웹 서버에 전가한다. 웹 서버에 접근하는 사용자 수가 적거나 서버에 메모리나 자원이 충분하다면 괜찮은 해결책이다.
마찬가지로, 사용자가 구닥다리 데스크톱에서 브라우저를 돌릴 때도 괜찮은 해결책이다. 이 접근 방식에서는 브라우저가 할 일이 거의 없다. 서버로 요청을 보내 서버더러 골치아픈 변환을 처리하라고 명령하고 기다리기만 하면 끝이다.
세 번째 접근 방식 역시 XSLT를 사용한다. 그러나 이번에는 XSLT 변환이 브라우저쪽에서 일어난다. Listing 2에서 보았던 weather2html.xsl XSLT 프로그램을 여기서도 사용한다. 그림 2는 접근 방식 3이 사용하는 자료 파이프라인이다.
그림 2. 접근 방식 3이 따르는 자료 파이프라인
마이크로소프트 윈도우 익스플로러(Microsoft® Windows® Internet Explorer®), 파이어폭스, 오페라 등 주요 브라우저는 모두 나름대로 XSLT 처리를 지원한다. 파이어폭스와 오페라는 XSLTProcessor 객체를 제공한다. 인터넷 익스플로러는 문서 모델을 확장하여 XSLT 처리를 지원한다.
Listing 6은 접근 방식 3에서 사용하는 weather_badge() 함수다. 접근 방식 1에서와 마찬가지로, 브라우저가 NWS 서버에서 날씨 정보를 가져오려면 아파치 웹 프록시 규칙을 설정해야 한다. (Ajax 프로그램은 원래 웹 페이지를 가져온 서버에서만 자료를 가져올 수 있다는 사실에 주의한다. 자세한 내용은 Part 1을 참조한다.)
Listing 6.
weather_badge_cs_xslt.js에서 weather_badge() 함수
function weather_badge (nws_id, div_name) {
// 서버에서 XML 파일을 가져온다.
var ajax = new Ajax ("/nws_currobs/" + nws_id + ".xml", "", "GET", null);
ajax.setAsync (false);
ajax.request ();
var xml_doc = ajax.req.responseXML;
// 서버에서 XSLT를 가져온다.
ajax = new Ajax ("/xml_weather/data/weather2html.xsl", "", "GET", null);
ajax.setAsync (false);
ajax.request ();
var xsl_doc = ajax.req.responseXML;
var div = document.getElementById (div_name);
// 파이어폭스/모질라/오페라나 IE XSLT 지원이 가능한지
// 객체 감지 기법을 활용한다.
if (typeof XSLTProcessor != "undefined") {
var xsl_proc = new XSLTProcessor ();
xsl_proc.importStylesheet (xsl_doc);
var node = xsl_proc.transformToFragment (xml_doc, document);
div.innerHTML = "";
div.appendChild (node);
}
else if (typeof xml_doc.transformNode != "undefined") {
div.innerHTML = xml_doc.transformNode (xsl_doc);
}
else {
div.innerHTML = "XSLT not supported in browser.";
}
}
|
접근 방식 1에서는 NWS 서버로부터 XML 정보를 가져왔다. 접근 방식 3에서는 XML 정보만이 아니라 우리 서버에 있는 XSLT 파일도 가져와야 한다. Listing 6
weather_badge() 함수에서 전반부는 XML 파일과 XSLT 파일을 가져온다. 두 파일은 XMLHttpRequest 응답으로 가져오는데, xml_doc과 xsl_doc이라는 자바스크립트 문서 객체 형태로 반환된다.
weather_badge() 함수에서 후반부는 XML 객체에 XSLT 변환을 적용한다. 인터넷 익스플로러는 다른 브라우저와 다르므로, 먼저 현재 실행 중인 브라우저를 파익한다. 여기서는 객체 감지(Object Detection) 기법을 사용하여 브라우저를 판별한다. 구체적으로는, 자바스크립트 연산자인 typeof를 사용하여 변환에 필요한 객체가 있는지 확인한다.
XSLTProcessor가 정의되어 있다면 파이어폭스나 오페라 브라우저라는 뜻이다. 이 때는 새 XSLTProcessor 객체 인스턴스를 생성한 후 XSLT 스타일 시트를 객체로 읽어들인다. 그런 다음, XSLTProcessor 객체의 transformToFragment 메서드를 사용하여 XML 날씨 정보를 변환한다. 이 메서드는 HTML 텍스트가 아니라 문서 단편(document fragment)을 반환한다. 즉 앞서 접근 방식처럼 innerHTML을 사용하여 DIV 태그에 바로 뿌려넣지 못한다는 뜻이다. 해결책은 아주 간단하다. appendChild를 사용하여 웹 페이지 DOM 트리로 문서 단편을 삽입하면 된다.
XSLTProcessor는 정의되지 않았으나 transformNode 메서드가 정의되어 있다면 인터넷 익스플로러에서 자바스크립트 프로그램이 실행 중이라는 뜻이다. 이 때는 다음 코드 한 줄로 XSLT 변환을 수행하고 DIV 태그에 결과를 추가한다.
div.innerHTML = xml_doc.transformNode (xsl_doc); |
접근 방식 3은 접근 방식 1이나 접근 방식 2와 각각 비슷한 면이 있다. 아파치 웹 프록시를 사용하여 XML 정보를 브라우저로 가져온다는 점에서는 접근 방식 1과 비슷하다. XSLT로 XML 정보를 HTML로 변환한다는 점에서는 접근 방식 2와 비슷하다.
그러나 브라우저 내에서 XSLT 프로세서를 호출한다는 점에서 단순히 DOM 트리에서 정보를 추출했던 접근 방식 1보다 브라우저에 부담이 간다. 간단한 우리 예제에서는 사용자가 성능 저하를 느낄 가능성이 거의 없다. 하지만 XML이 크거나 XSLT 변환이 복잡하다면 브라우저가 예상보다 느려져 사용자에게 불편을 줄지도 모른다.
반면, 접근 방식 1은 XML 파일이 클 경우 DOM 트리도 그만큼 복잡해진다. 따라서 DOM 트리를 탐색하는 자바스크립트 코드도 그만큼 복잡해지므로 코드를 작성하고 관리하기가 어려워진다.
또한 사용자가 사용하는 브라우저와 컴퓨터도 고려해야 한다. 메모리가 충분하고 프로세서가 강력한 고가 워크스테이션인가? 성능이 떨어지는 구닥다리 시스템인가? 자바스크립트로 XSLT 변환을 수행하는 접근 방식 3이 적합한지는 사용자 환경에 달려 있다.
이 연재 마지막 기사인 Part 3에서는 네 번째 접근 방식을 살펴본다. 접근 방식 4는 이제까지 살펴본 세 가지 접근 방식과 확연히 다르다. 너무나 달라서 혹자는 Ajax가 아니라고 주장할지도 모르겠다. 마지막 접근 방식은 지금까지 소개한 세 가지 접근 방식이 겪었던 '같은 도메인' 문제를 우회하지만 전혀 새로운 문제를 야기한다. 또한 마지막 기사에서는 네 가지 접근 방식을 비교하고, 연재에서 다루지 않은 방식 몇 가지를 간략히 언급한다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| 이 연재의 예제 코드 | x-xmlajax.zip | 194KB | HTTP |
교육
-
Ajax에서 XML 처리하기, Part 1: 네 가지 방법(Mark Pruett, 한국 developerWorks, 2008년 5월): 어떤 프로그래밍 문제든 해결 방법은 여러 가지가 있다. 이 기사에서는 Ajax 날씨 뱃지를 만드는 네 가지 방법 중 DOM 트리를 사용하는 첫 번째 방법을 소개한다.
-
XML processing in Ajax, Part 3: JSON and avoiding proxies(Mark Pruett, developerWorks, 2007년 3월): 이 연재를 구성하는 마지막 기사다. 공용 웹 서비스와 JSON(JavaScript Object Notation), 동적 스크립트 태그를 사용하여 Ajax 날씨 뱃지를 만든다.
-
What kind of language is XSLT?(Michael Kay, developerWorks, 2001년 2월, 2005년 4월에 갱신): XSLT를 소개하는 우수한 기사다. 언어가 나온 배경, 언어가 제공하는 장점, 적절한 사용법을 설명한다.
-
Ajax 마스터하기, Part 1: Ajax 소개(Brett McLaughlin, 한국 developerWorks, 2006년 5월): 웹 사이트 구축이라는 실용적인 관점에서 Ajax를 소개하는 연재 기사다.
- IBM developerWorks에서 제공하는
Ajax 자원 센터: 다양한 Ajax 기술을 소개한다.
-
IBM XML 인증: XML과 관련 기술 분야에서 IBM 인증 개발자가 되는 방법을 소개한다.
-
XML 기술 라이브러리: developerWorks XML 영역에서는 각종 기사와 팁, 튜토리얼, 표준, IBM 레드 북 등 다양한 기술 자료를 제공한다.
-
developerWorks 기술 행사와 웹 캐스트: 최신 기술 동향을 파악하자.
- The 온라인 기술 서점: 다양한 기술 서적과 기술 자료를 제공한다.
제품 및 기술 얻기
-
IBM 평가판 소프트웨어: developerWorks에서 직접 내려 받아 다음 프로젝트에 활용해보자.
토론
-
XML 포럼: XML 관련 주제를 토론하는 포럼에 참여하자.
-
developerWorks XML zone: Share your thoughts: 기사에 관한 의견을 나누는 포럼이다. 중재는 XML 영역 편집자들이 맡는다. 언제든지 의견을 보내주기 바란다.
-
developerWorks 블로그: 블로그를 읽어보고 developerWorks 공동체에 참여하자.
Mark Pruett는 Dominion 사 시스템 아키텍트다. Mark Pruett는 오라일리 출판사를 통해 Ajax and Web Services와 Yahoo! Pipes를 저술했으며 Ajax Hacks의 기여 저자이기도 하다.