GWT에서는 Java 언어로 프로그래밍된 서버 측 서블릿에 쉽게 액세스할 수 있으며, 이때 데이터는 클라이언트와 서버 간에 백그라운드로 전송된다. 하지만 GWT에서는 서블릿과의 통신뿐만 아니라 모든 유형의 웹 서비스와도 데이터를 자유롭게 교환할 수 있다. 많은 경우에는(간단한 서비스의 경우) 일반 텍스트 형식으로 데이터가 전송되지만 데이터가 구조화되거나 복잡해지면(예: RSS) XML 형식이 일반 텍스트를 대체하게 될 것이다.
이 기사에서는 간단한 GWT 애플리케이션과 한 쌍의 PHP 웹 서비스를 사용하여 다양한 방법으로 XML 문서를 생성 및 사용하는 방법에 대해 설명한다. 이 기사는 튜토리얼이나 핸드북과 같은 세부적인 내용을 포함하고 있지는 않지만 XML을 통해 GWT와 PHP를 연결하는 작업을 빠르게 시작하는 데 필요한 도움을 제공하고 있다.
XML을 PHP와 GWT를 연결하는 다리로 사용하는 방법을 설명하기 위해 이 기사에서는 국가/지역/도시 데이터를 기반으로 하는 간단한 애플리케이션을 제공한다. Listing 1의 데이터베이스 생성 코드에는 다음과 같은 특징이 있다.
- Countries에는 고유 코드(예: 우루과이의 경우 UY)와 이름이 있다.
- Countries는 국가 내에서 고유 코드로 식별되고 이름을 가지고 있는 regions으로 구분된다.
- Regions에는 cities가 있으며, 각 도시는 (일반 ASCII) 이름, 악센트 부호가 있는 이름(외래 문자를 포함할 수 있음), 인구(알 수 없는 경우 0), 위도 및 경도를 가지고 있다. 도시 이름은 같은 국가의 여러 지역에서 나타날 수 있다.
Listing 1. 데이터베이스 생성 코드
CREATE DATABASE world
DEFAULT CHARACTER SET latin1
COLLATE latin1_general_ci;
USE world;
CREATE TABLE countries (
countryCode char(2) NOT NULL,
countryName varchar(50) NOT NULL,
PRIMARY KEY (countryCode)
KEY countryName (countryName)
);
CREATE TABLE regions (
countryCode char(2) NOT NULL,
regionCode char(2) NOT NULL,
regionName varchar(50) NOT NULL,
PRIMARY KEY (countryCode,regionCode),
KEY regionName (regionName)
);
CREATE TABLE cities (
countryCode char(2) NOT NULL,
cityName varchar(50) NOT NULL,
cityAccentedName varchar(50) NOT NULL,
regionCode char(2) NOT NULL,
population bigint(20) NOT NULL,
latitude float(10,7) NOT NULL,
longitude float(10,7) NOT NULL,
KEY `INDEX` (countryCode,regionCode,cityName),
KEY cityName (cityName),
KEY cityAccentedName (cityAccentedName)
);
|
이 기사에서는 하나의 양식과 한 쌍의 PHP 웹 서비스로 구성된 간단한 GWT 프로젝트를 만들었다. (전체 소스 코드는 다운로드에서 볼 수 있다.) 애플리케이션을 시작하면 그림 1과 같은 간단한 창이 표시된다.
그림 1. 비어 있는 양식
이 GWT 양식에 도시 이름의 일부를 입력하면 입력한 내용과 일치하는 모든 도시를 가져오는 PHP 서비스가 호출된다. 검색된 도시가 그리드로 표시되며, 인구, 위도 및 경도 필드를 편집할 수 있다. 편집된 데이터를 두 번째 PHP 웹 서비스로 다시 보내서 데이터베이스를 업데이트할 수 있다. 이러한 모든 데이터는 XML로 전송된다. 2009년은 Charles Darwin 탄생 200주년과 그의 저서인 The Origin of Species의 150주년이 되는 해이므로 여기에서도 이를 기념하여 DARWIN이 포함된 도시 이름을 검색해 보면 그림 2와 같은 결과가 표시된다.
그림 2. "Darwin"이 포함된 도시 이름 검색
참고적으로 이 기사에서 사용한 제품은 다음과 같다.
- GWT 버전 1.5.3
- PHP 버전 5.2.8
- MySQL® database server 버전 5.0.67
- Apache 버전 2.2.10(OpenSUSE® 버전 11.1 기반)
이 기사에서는 모든 소프트웨어를 기본 설정으로 설치했지만 GWT의 경우에는 GWT-PHP 연결을 테스트하기 위해 추가 구성 작업을 수행했다. 이러한 작업이 필요한 이유에 대한 설명은 사이드바에 있는 SOP 문제점에서 볼 수 있다. 내부 GWT 브라우저에서 SOP(Same-Origin Policy) 검사를 사용하지 않으려면 GWT 디렉토리에 있는 ./mozilla-1.7.12/greprefs/all.js 파일을 연 후 파일의 끝에 Listing 2의 내용을 추가한다.
Listing 2. 내부 GWT 브라우저의 구성 변경 사항
pref("capability.policy.default.XMLHttpRequest.abort", "allAccess");
pref("capability.policy.default.XMLHttpRequest.getAllResponseHeaders","allAccess");
pref("capability.policy.default.XMLHttpRequest.getResponseHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.open", "allAccess");
pref("capability.policy.default.XMLHttpRequest.send", "allAccess");
pref("capability.policy.default.XMLHttpRequest.setRequestHeader","allAccess");
pref("capability.policy.default.XMLHttpRequest.onreadystatechange","allAccess");
pref("capability.policy.default.XMLHttpRequest.readyState", "allAccess");
pref("capability.policy.default.XMLHttpRequest.responseText","allAccess");
pref("capability.policy.default.XMLHttpRequest.responseXML","allAccess");
pref("capability.policy.default.XMLHttpRequest.status", "allAccess");
pref("capability.policy.default.XMLHttpRequest.statusText", "allAccess");
|
이 변경 사항은 GWT를 업데이트할 때마다 다시 적용해야 한다. 또한 이 변경 사항을 적용하지 않은 코드는 호스트 모드에서 실패하지만 컴파일 모드에서 올바르게 실행되며, 변경 사항을 적용한 코드는 호스트 모드에서 실행되지만 컴파일 모드에서 실패하므로 주의를 기울여야 한다.
이 애플리케이션은 소수의 레이블, 텍스트 상자, 두 개의 명령 단추 및 결과 그리드로 구성된 간단한 양식을 정의한다. Get cities를 클릭하면 텍스트 상자에 입력한 내용과 일치하는 모든 도시가 포함된 XML 문서를 가져오기 위해 PHP 서비스가 호출된다. Listing 3에서는 PHP 서비스에서 GWT 애플리케이션으로 약간 축소된 형태의 전송된 샘플 XML을 보여 준다. 생성된 XML 코드는 여러 가지 XML 기능을 보여 주기 위한 것이기 때문에 실제 애플리케이션보다 훨씬 길다. 일반적으로 잘 설계된 XML 서비스는 태그 수가 작고 속성이 많으면서 문서 길이가 짧다.
Listing 3. "tokyo"를 검색하여 생성된 XML 문서
<?xml version="1.0" encoding="UTF-8"?>
<cities>
<city name="tokyo">
<country code="JP" name="Japan"/>
<region code="40" name="Tokyo"/>
<coords>
<lat>35.6850014</lat>
<lon>139.7513885</lon>
</coords>
<pop>31480498</pop>
</city>
<city name="tokyo">
<country code="PG" name="Papua New Guinea"/>
<region code="01" name="Central"/>
<coords>
<lat>-8.3999996</lat>
<lon>147.1499939</lon>
</coords>
</city>
<city name="tokyojitori">
<country code="KR" name="Korea, Republic of"/>
<region code="16" name="Cholla-namdo"/>
<coords>
<lat>34.2380562</lat>
<lon>125.9394455</lon>
</coords>
</city>
</cities>
|
이 기사에서 설명하는 PHP 서비스에는 두 가지 버전 즉, getcities1.php와 getcities2.php가 있으며 각 버전은 각기 다른 방법으로 XML을 생성한다.
XML을 생성하는 가장 간단한 방법은 해당 텍스트를 순서대로 인쇄하거나 문자열을 작성한
후 echo를 사용하여 출력하는 것이다. 올바르게 인식되도록 컨텐츠
유형을 text/xml로 설정해야 하며 XML 버전 및 데이터 인코딩을
지정하는 적절한 설명을 포함시켜야 한다. 부등호(< 및
>) 또는 앰퍼샌드(&) 문자가
포함되지 않도록 문자열을 이스케이프해야 하며 이를 수행하는 가장 쉬운 방법은 htmlspecialchars()
PHP 함수를 사용하는 것이다. Listing 4에서 볼 수 있듯이 코딩 작업은
쉽게 수행할 수 있다. 들여쓰기와 줄 바꿈은 읽기 쉬운 코드를 만드는 데 도움이 되기는
하지만 반드시 사용해야 하는 것은 아니다.
Listing 4. PHP 서비스에서 XML을 생성하는 가장 간단한 방법
...
header("Content-type: text/xml");
...
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
echo '<cities>'."\n";
...
while ($row= mysql_fetch_assoc($result)) {
echo ' <city name="'.htmlspecialchars($row['cityName']).'">'."\n";
echo ' <country code="'.$row['countryCode'].'" ';
echo 'name="'.htmlentities($row['countryName']).'"/>'."\n";
echo ' <region code="'.$row['regionCode'].'" ';
echo 'name="'.htmlentities($row['regionName']).'"/>'."\n";
echo ' <coords>'."\n";
echo ' <lat>'.$row['latitude'].'</lat>'."\n";
echo ' <lon>'.$row['longitude'].'</lon>'."\n";
echo ' </coords>'."\n";
if ($row['population']>0) {
echo ' <pop>'.$row['population'].'</pop>'."\n";
}
echo ' </city>'."\n";
}
echo '</cities>'."\n";
|
XML 코드를 생성하는 두 번째 방법(많이 길지는 않음)은 XMLWriter를
사용하는 것이다. (동료 클래스인 XMLReader를 사용하여 XML을 처리할
수 있다.) 이 방법을 사용하면 문자가 자동으로 이스케이프되기 때문에 따로 작업을 하지 않아도
된다. 이 방법을 사용하면 echo() 메소드를 사용할 때보다 길어
보이기는 하지만 코드를 더 쉽게 이해할 수 있다. 특히 php://output
프로토콜을 사용하면 echo 명령을 사용할 때와 같은 텍스트가 출력된다(Listing
5 참조).
Listing 5. 한 번에 한 요소씩 XML 문서를 생성할 수 있는 간단한 방법을 제공하는 XMLWriter
...
$writer= new XMLWriter();
$writer->openURI('php://output')
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement("cities");
while ($row= mysql_fetch_assoc($result)) {
$writer->startElement("city");
$writer->writeAttribute("name", $row['cityName']);
$writer->startElement("country");
$writer->writeAttribute("code", $row['countryCode']);
$writer->writeAttribute("name", $row['countryName']);
$writer->endElement();
$writer->startElement("region");
$writer->writeAttribute("code", $row['regionCode']);
$writer->writeAttribute("name", $row['regionName']);
$writer->endElement();
$writer->startElement("coords");
$writer->writeElement("lat", $row['latitude']);
$writer->writeElement("lon", $row['longitude']);
$writer->endElement();
if ($row['population']>0) {
$writer->writeElement("pop", $row['population']);
}
$writer->endElement(); // city
}
$writer->endElement(); // cities
...
|
PHP에서는 이러한 방법 외의 다른 여러 가지 방법으로도 XML을 생성할 수 있다. 예를 들어, SimpleXML을 사용할 수 있다. SimpleXML은 나중에 XML을 읽는 데 사용되며 XML 문서를 생성하는 기능도 제공한다. PEAR 프레임워크에도 XML을 쉽게 생성할 수 있는 여러 가지 클래스가 있다. (참고자료에서 자세한 정보에 대한 링크를 볼 수 있다.)
GWT는 XML을 읽고 쓸 수 있는 하나의 XMLParser(com.google.gwt.xml.client
패키지에 있음)를 제공한다. parse() 메소드를 사용하여 Document를
생성한 다음 getDocumentElement()를 사용하여 문서의 루트 요소를
가져오게 되면 XML을 탐색할 수 있는 준비가 완료된다.
여기에서 중요한 점은 removeWhitespace() 메소드를 사용하여
문서에 있는 공백을 제거해야 한다는 것이다. 브라우저 구문 분석기에서 탭 또는 줄 바꿈에
해당하는 빈 텍스트 노드가 생성되기도 하기 때문에 이러한 노드를 제거하지 않으면 프로세스에서
예기치 않은 추가 요소로 인해 논리의 오류가 발생할 수 있다(Listing 6
참조). 또 하나 중요한 점은 CDATA 섹션이 예상될 경우에는 브라우저에서 supportsCDATASection()
메소드를 허용하는지 여부를 확인해야 한다는 것이다. 이 메소드가 허용되지 않을 경우 이러한
섹션은 텍스트 노드를 생성한다. 이에 대한 자세한 정보는 GWT 문서(참고자료
참조)에서 볼 수 있다.
Listing 6. XML 읽기 및 생성 기능을 모두 제공하는 GWT의 XMLParser
protected void loadCities(final String xmlCities) {
...
final Document xmlDoc= XMLParser.parse(xmlCities);
final Element root= xmlDoc.getDocumentElement();
XMLParser.removeWhitespace(xmlDoc);
final NodeList cities= root.getElementsByTagName("city");
for (int i= 0; i < cities.getLength(); i++) {
final Element city= (Element)cities.item(i);
// show city.getAttributeNode("name").getValue()
final Element country= (Element)city.getElementsByTagName("country").item(0);
// show country.getAttributeNode("code").getValue()
// show country.getAttributeNode("name").getValue()
...
final Element population= (Element)city.getElementsByTagName("pop").item(0);
if (population != null) {
// show population.getFirstChild().getNodeValue()
}
final Element coords= (Element)city.getElementsByTagName("coords").item(0);
final Element lat= (Element)coords.getElementsByTagName("lat").item(0);
// show lat.getFirstChild().getNodeValue()
...
}
...
|
getElementsByTagName() 메소드를 사용하여 결과 배열을
차례대로 이동하면서 반복되는 요소(이 예제의 경우 city)를
가져올 수 있다. 또는 getFirstChild() 메소드와 함께
getNextSibling()을 사용하여 같은 레벨에 있는 나머지 요소를
가져올 수도 있다. 속성을 가져오려면 getAttributeNode() 메소드를
호출한 다음 getValue() 메소드를 호출해야 한다. CDATA 섹션,
주석 및 모든 가능한 XML 구성 요소를 처리하는 여러 가지 메소드가 있다.
이 GWT 애플리케이션에서는 인구, 위도 및 경도 필드를 편집한 후 도시 데이터를 서버로 보내서 데이터베이스를 업데이트할 수 있다. 각각의 XML 문자열을 생성하는 간단한 알고리즘과 특정 메소드를 사용하여 원하는 구조를 만드는 XMLParser 기반 알고리즘이 있다.
getCities1() 메소드에서는 간단한 알고리즘을 보여 준다. String
또는 StringBuffer 오브젝트를 사용하여 XML을 생성할 수 있다. 이
기사에서는 코드의 가독성을 높여 주는 전자를 사용했지만 성능 측면에서는 후자가 더 좋은 결과를
제공한다. 여기에서도 PHP 버전에서와 같은 문자 이스케이프 문제가 있으므로 Html.htmlspecialchars()
메소드를 사용하여 해결해야 한다(명확성을 위해 약간 수정된 Listing 7 참조).
Listing 7. 문자열을 사용하여 간단하게 각각의 XML 생성하기
protected String getCities1() {
String result= "";
result+= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
result+= "<cities>\n";
for (all rows in the grid) {
// get cityName, countryCode, regionCode, pop, lat, and lon, from the grid
result+= " <city name=\"" + Html.htmlspecialchars(cityName) + "\">\n";
result+= " <country code=\"" + countryCode + "\"/>\n";
result+= " <region code=\"" + regionCode + "\"/>\n";
if (!pop.equals("0") && !pop.isEmpty()) {
result+= " <pop>" + pop + "</pop>\n";
}
result+= " <coords>\n";
result+= " <lat>" + lat + "</lat>\n";
result+= " <lon>" + lon + "</lon>\n";
result+= " </coords>\n";
result+= "</city>\n";
}
result+= "</cities>\n";
return result;
}
|
XML을 간단하게 생성할 수 있는 나머지 알고리즘은 XMLParser 클래스의
createDocument() 메소드이다. PHP의 SimpleXML 함수를 사용할
때와 마찬가지로 먼저 빈 문서를 생성한 후 요소를 문서에 추가한다. 모든 종류의 노드를
생성하고 속성 값을 설정할 수 있다. 마지막으로 표준 Java toString()
메소드가 오브젝트의 표현을 생성하며, 이 경우 사용자는 초기 버전 및 인코딩 행만 추가하면
필요한 문자열을 얻을 수 있다(명확성을 위해 수정 및 요약된 Listing 8 참조).
Listing 8. PHP SimpleXML의 메소드와 비슷한 XMLParser의 XML 생성 메소드
protected String getCities2() {
Document xml= XMLParser.createDocument();
Element cities= xml.createElement("cities");
xml.appendChild(cities);
for (all rows in the grid) {
// get cityName, countryCode, regionCode, pop, lat, and lon, from the grid
Element city= xml.createElement("city");
city.setAttribute("name", cityName);
Element country= xml.createElement("country");
country.setAttribute("code", countryCode);
city.appendChild(country);
...
if (!pop.equals("0") && !pop.isEmpty()) {
Element popEl= xml.createElement("pop");
Text popText= xml.createTextNode(pop);
popEl.appendChild(popText);
city.appendChild(popEl);
}
Element coords= xml.createElement("coords");
Element lat= xml.createElement("lat");
Text latText= xml.createTextNode(lat);
lat.appendChild(latText);
coords.appendChild(lat);
...
city.appendChild(coords);
cities.appendChild(city);
}
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xml.toString();
}
|
PHP에서 XML을 처리하는 문제는 오래 전부터 제기되었으며 현재는 몇 가지 해결책이
사용되고 있지만 필자의 견해로는 명확성과 간결성 차원에서 SimpleXML과 견줄 수 있는
해결책을 보지 못했다. 기본적으로 사용자는 XML 문서를 simplexml_load_string()
메소드에 제공하여 PHP 오브젝트를 생성한 다음 표준 PHP 연산자를 사용하여 결과를 이동한다. 속성은
배열의 요소가 되고(예를 들어, Listing 9에서 국가 코드를 읽는 방법 참조)
요소는 배열로서 액세스하거나(children() 메소드 사용) 오브젝트의
속성으로서 직접 액세스할 수 있다.
Listing 9. PHP에서 가장 쉽게 XML을 처리할 수 있는 방법인 SimpleXML
$xml_str= $_POST["xmldata"];
$xml_obj= simplexml_load_string($xml_str);
...
foreach($xml_obj->children() as $city) {
$name= addslashes($city['name']);
$country= $city->country['code'];
$region= $city->region['code'];
$pop= $city->pop;
$lat= $city->coords->lat;
$lon= $city->coords->lon;
mysql_query("REPLACE INTO cities ".
"(cityName, countryCode, regionCode, population, latitude, longitude) VALUES (".
"'{$name}', '{$country}', '{$region}', '{$pop}', '{$lat}', '{$lon}')");
}
|
이외에도 PHP로 XML을 처리하는 여러 가지 방법이 있다. 참고자료 섹션에서 관련 링크를 볼 수 있다.
이 기사에서는 XML을 GWT와 PHP의 연결 고리 역할로 사용하는 간단하면서도 매우 중요한 여러 가지 방법에 대해 살펴보았다. 이 기사의 주요 논점은 GWT가 고유 RPC 메소드만 사용할 수 있는 제한된 환경에 머물지 않고 XML과의 공존이 가능한 환경이기에 XML 문서를 쉽게 생성 및 사용할 수 있다는 것이다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Java source code for this article | java_source_code.zip | 4KB | HTTP |
| PHP source code for this article | php_source_code.zip | 4KB | HTTP |
교육
- W3C XML 사이트에서 제공하는
최신 버전(5판)의 XML 표준 권장 사항을 포함한 여러 문서를
통해 XML에 대해 알아볼 수 있다.
- PHP에서 XML 처리: 이 기사에서 사용되는 SimpleXML,
XMLReader 및 XMLWriter를
포함한 여러 가지 방법에 대해 찾아볼 수 있다.
XMLParser클래스: GWT 문서를 살펴볼 수 있다.- JSON.org: XML의 대안으로서의
JSON에 대한 자세한 내용은 Java 언어 및 PHP를 포함한 대부분의 최신 프로그래밍 언어에 대한 리소스를 통해 확인할 수 있다.
- SOP(Same-Origin Policy):
SOP 문제점에 대한 최초의 해결책을 살펴보고 추가 고려 사항을 읽어볼 수 있다.
- IBM XML 인증: XML 및 관련 기술에 대한 IBM 인증 개발자가 되는 방법을 찾아볼 수 있다.
- XML Technical library: developerWorks XML 영역에서 다양한 기술 관련 기사와 팁, 튜토리얼, 표준 및 IBM Redbook을 볼 수 있다.
- developerWorks 기술 행사 및 웹 캐스트: 이들 세션에 참가하여 최신 기술에 대한 정보를 얻을 수 있다.
- 기술 서점: 다양한 기술 주제와 관련된 서적을 살펴볼 수 있다.
- developerWorks
팟캐스트: 소프트웨어 개발자의 흥미로운 인터뷰와 토론을 확인할 수 있다.
제품 및 기술 얻기
- GWT(Google Web Toolkit):
다운로드한 후 이 기사에 포함된 코드 샘플을 실행해 볼 수 있다.
- PEAR 프레임워크: 재사용 가능한 PHP 구성 요소를 살펴볼 수 있다.
- MaxMind의 무료 도시 테이블: 다운로드하여 국가 및 지역 데이터에 대한 링크를 찾아볼 수 있다.
- IBM 시험판 제품을
다운로드하거나 IBM SOA Sandbox의 온라인 시험판을 살펴보고
DB2®, Lotus®, Rational®, Tivoli® 및 WebSphere®의 애플리케이션 개발 도구 및
미들웨어 제품을 사용해 볼 수 있다.
토론
- XML 영역 토론 포럼: 여러 XML 관련 토론에 참여해 볼 수 있다.
- developerWorks 포럼 & 블로그:
이들 블로그를 읽어보고 developerWorks 커뮤니티에 참여해 볼 수 있다.
