본문 바로가기
Web Programing!/JAVA / JSP

[JSP] 자바가 바라보는 xml

by 어설픈봉봉이 2011. 10. 15.
반응형
SMALL



자바가 바라보는 XML


XML과 관련해 자바에서 필요한 자료는 대부분 이미 나와있다 해도 과언이 아니다. 다만 ‘언제 어떤 도구를 사용할 것인가’를 일목요연하게 정리한 자료를 좀처럼 찾기 어렵다는 것이 흠이라 할 수 있다. 따라서 자바 환경에서 간단한 XML 프로그램을 만들어 보고, DTD 등의 스키마에 맞춰 검증하는 등 XML 문서를 처리하는 기본적인 도구를 살펴보자.

글쓴이 김도형


필자가 현재 수행하는 프로젝트에 XML을 도입하기 시작한 것은 지난해 9월경부터다. 처음에는 개발중인 응용 프로그램에서 읽을 수 있도록 자바 컴포넌트에 관련된 메타 정보(metadata)를 기술하기 위해 XML을 사용했는데, IBM의 XML4J 파서(parser)를 이용해도 별 무리가 없었다. 하지만 올해로 접어들어 XML의 적용 범위가 넓어지고 그 형태가 복잡해짐에 따라 XML을 읽기 위한 가장 기본적인 도구인 DOM(Document Object Model) 파서 혹은 SAX (Sim ple API for XML) 파서만을 갖고는 팀 내의 개발자들이 유지보수가 용이한 XML 처리 코드를 작성하는 것이 어렵다는 것을 알게 됐다. 이로 인해개발하고자 하는 모듈의 특성에 따라 어떤 도구를 사용할 지에 대한 명확한 지침과 빈번히 사용되는 처리 유형에 적합한 도구를 만들어 구축할 필요성이 부각됐기 때문이다.

자바의 경우 XML과 관련해 필요한 자료는 이미 대부분 나와있다 해도 과언이 아니다. 다만 어떤 도구가 현재 나와 있고, 이들을 어떻게 사용해야 하는가에 대한 방침을 일목요연하게 정리한 자료가 좀처럼 없다는 것이 흠이다. 따라서 이 글에서는 XML을 간단히 만들어 보고, DTD(Docu ment Type Definition) 등의 스키마에 맞춰 검증하는 등 XML 문서를 처리하기 위한 기본적인 도구를 소개할 것이다. 덧붙여 언제 어떤 도구를 어떻게 사용해야 하는지를 살펴보고, 기존 라이브러리를 보완할 수 있는 라이브러리를 몇 개 만들어 볼 것이다.

이 글은 각 API나 XML 처리 도구의 사용법을 설명하기보다는 어떤 수단이 있고, 각각의 장단점은 무엇인지를 설명하기 위한 것임을 명심하기 바란다. 따라서 XML 자체나 API의 구체적인 사용법은 이미 어느 정도 알고 있다는 전제 아래 얘기를 풀어나갈 것이다.


XML에 어떤 도구를 사용할까

자세한 설명에 들어가기 앞서 여기서 사용할 도구를 간단히 살펴보면 다음과 같다.

◆ Xerces-J 1.2.0 : 자바로 쓰여진 대표적인 XML 파서. 원래는 IBM의 XML4J를 기반으로 아파치 프로젝트의 일부로 시작됐으나, 현재는 IBM이 Xerces-J의 코드를 기반으로 테스트한 후 XML4J를 배포하는 상황이다. SAX2, DOM2, XML 스키마 등 최근 완결됐거나 진행중인 웹 컨소시엄(W3C)의 표준은 물론 XML 문서를 생성하는 도구를 함께 지원한다(참고 자료 8).

◆ JDOM 1.0 베타 5 : 언어 독립적으로 작성된 W3C의 DOM(IDL로 정의된 인터페이스)을 대신해 자바 프로그래머가 사용하기 쉬운 형태의 객체 모델을 대신 제공하는 자바 패키지. 어떤 자바 파서와도 함께 사용할 수 있으나 반드시 Xerces-J를 함께 사용해야 한다(참고 자료 6).

◆ 오라클 클래스 생성기(Class Generator) : DTD로부터 XML 구조를 표현하는 자바 클래스를 위한 코드를 자동으로 생성해주는 시스템. 이를 이용하면 DOM보다는 해당 DTD의 구조에 가까운 자바 객체를 사용해 그 DTD를 따르는 XML 파일을 메모리에 객체로 구성, DTD와의 일치 여부 확인 및 출력이 가능하다. 현재 개발중인 XML 데이터 바인딩(data binding)과 비슷한 시도라고 볼 수 있지만 아직은 출력 전용이다.


XML 관련 자바 API의 표준화

C++나 다른 언어에 비해 자바가 갖는 장점 중 하나는 API의 표준화가 빠르다는 것이다. 이는 JCP(Java Community Process)가 효과를 발휘하고 있기 때문이다. 현재 XML과 관련해 다음과 같은 몇몇 표준 API가 개발됐는데, 가까운 장래에 XML 관련 API는 J2SE(JABA 2 Standard Edition) 혹은 J2EE(Java 2 Enterprise Edition)에 포함될 것으로 보인다.

◆ XML 처리 API(Java API for XML Processing) : 현재 버전 1.0이 나와 있으며, XML 문서의 파싱을 위한 표준 API를 정의하고 있다. XML 네임스페이스(namespace) 1.0, DOM 레벨 1, 그리고 SAX 1.0을 지원한다. 다음 버전인 1.1은 추가로 DOM 레벨 2와 SAX 2.0을 지원하고 단순히 파싱 이외에 최근 많이 사용되는 XML 변환 표준인 XSLT 1.0을 지원할 예정이다. 이 API는 JAXP라고 지칭하는 경우가 많은데, 이때 마지막 ‘P’는 1.0에서는 ‘Parsing’이었으나 1.1에서는 XSLT에 대한 지원이 들어가면서 ‘Processing’으로 바뀌었다.

◆ XML 데이터 바인딩 : DTD와 XML 스키마같은 XML 문서의 구조를 표현하는 스키마로부터 그에 맞는 자바 객체를 자동으로 생성하는 기술. 해당 스키마를 따르는 XML 문서를 파싱해, 문서 구조를 나타내는 자바 객체가 스키마에 맞는지 검증하거나 거꾸로 자바 객체로부터 XML 문서를 생성한다(참고 자료 3, 4).

◆ XML 메시징 API : ebXML 등 XML 메시지의 교환을 위한 표준 API(참고 자료 5).

API가 표준화되면 응용 프로그램이 파서 등 특정 XML 처리 도구에 묶이지 않고, 이러한 표준 API를 매개로 다양한 XML 처리 도구는 서로 연동할 수 있는 기반을 마련하게 된다. 하지만 아직 XML 관련 처리 도구가 XML 파서와 함께 제공되거나 XML 파서의 내부 정보에 의존하는 경우가 많아 응용 프로그램을 작성할 때 많은 부분을 특정 모듈에 맞춰 작성하는 것이 현실이다. 이런 문제점은 앞으로 점차 해결될 것으로 보이나, 현재로서는 가능한 한 표준 API를 사용하는 것이 최선이라고 하겠다.


DOM과 SAX의 세계로의초대

XML 문서는 기본적으로 컴퓨터로 처리하기 위한 문서이다. 또 흔히 자바에서 메타 정보를 저장하기 위해 사용하는 프로퍼티 파일(pro perties file, java.util.Properties를 사용해 읽고 생성)과는 달리 구조적인 정보를 표현할 수 있는 문서 형식이다. 단순히 비트 혹은 문자들의 연속인 XML 문서가 컴퓨터에 의해 처리되기 위해서는 그 구조를 복원, 프로그램에 의해 처리 가능한 데이터 구조나 구조화된 정보의 흐름 등으로 변환하는 것이 필요하다. 이런 일을 해 주는 것이 XML 파서이고, 파서가 처리한 XML 문서를 응용 프로그램에 전달하는 수단 중 표준화된 것이 바로 DOM과 SAX이다.

DOM은 HTML에서 자바스크립트를 사용해 본 사람들이라면 상당히 친숙한 개념이다. XML 문서는 요소(element), 속성(attribu te), 텍스트 등으로 구성된 나무 구조의 계층화한 정보로 볼 수 있다. 따라서 이들을 각각 자바 객체에 대응시키면 XML 문서를 나무 구조의 자바 객체로 표현할 수 있다(실제는 XML 문서의 한 부분에서 다른 부분을 지칭할 수 있으므로 그래프라고 볼 수 있겠다). 이와 같은 XML 문서를 나타내는 객체들의 인터페이스를 표준으로 정해 놓은 것이 바로 DOM이다. 일반적인 DOM 파서는 바로 XML 문서로부터 DOM 구조를 생성하는 역할을 한다.

반면 SAX는 XML 문서를 앞에서 뒤로 읽어가면서 어떤 요소, 속성 등이 나타났는지 알려주는 구조 API이다. 즉, 문서의 구조를 일련의 사건(이벤트) 흐름으로 표현한다고 볼 수 있다. 이런 이벤트들은 일종의 정보 덩어리로 볼 때, SAX 파서는 XML 문서을 정보 덩어리의 흐름으로 바꾸는 도구이다.

일반적으로 DOM과 SAX를 파싱 단계에서 필요한 파서와의 인터페이스 정도로 생각하는 사람이 있는 듯 하다. 하지만 앞서 장황하게 설명한 것처럼 XML 문서를 컴퓨터 내에서 표현하는 대표적인 방법으로, 응용 프로그램의 다양한 모듈 간에 XML 문서를 주고받는 표준적인 방법이라고 보는 것이 옳다. 실제 많은 툴이 단순히 XML 파일 혹은 스트림을 입출력 형식으로 지원하는 것 외에 DOM이나 SAX DocumentHandler를 추가로 지원하는 경우가 많은 것은 단적인 예라고 하겠다. 이 둘은 그 성격이 판이하게 다르므로 어떤 경우에 어떤 것을 사용할 것인가를 명확히 파악해 두는 것이 매우 중요하다.


DOM이야? SAX야?

파싱 후 문서를 처리하는 면에서의 DOM과 SAX의 주요 차이점은 DOM이 몇 번이고 원하는 부분을 추가 및 수정할 수 있는 수단인데 비해, SAX는 문서를 처음에서 끝까지 순차적으로 처리한다는 것이다. 우선 DOM뿐 아니라 일반적으로 문서의 구조가 메모리에 있을 때 유리한 점은 다음과 같다.

문서의 일부를 두 번 이상 읽어야 할 때
문서를 빈번히 수정해야 할 때
문서 구조 처리가 특별히 중요할 때
즉, 문서의 일부분을 정렬하거나 GUI를 통해 사용자가 빈번히 수정해야 할 때가 바로 이런 경우에 해당한다. 하지만 이런 경우에는 XML 파일로부터 반드시 DOM 객체를 생성할 필요가 없다. XML 데이터 바인딩으로부터 생성된 객체를 사용하거나 또는 SAX 파서를 통해 문서를 읽으면서 그 내용을 다른 자바 객체의 나무 구조로 변환한 경우에는 SAX가 오히려 DOM보다 유리하다.

이런 맥락에서 이 글에서는 DOM 객체의 대용으로 JDOM을 소개하고, SAX를 이용해 특정 구조를 갖는 XML 파일를 자바 객체 구조로 쉽게 변환할 수 있는 도구를 만들어 본다. 참고로 실제 DOM 파서들은 대부분 SAX 파서를 내부적으로 사용해 DOM 객체를 만들어 낸다. 이런 점을 고려하면 DOM은 다음의 경우에 적합하다.

응용 프로그램이 다양한 종류의 XML 문서를 다루는 경우
(특정한 문서 구조를 가정할 수 없는 경우)
XML 문서의 구조가 충실히 보존돼야 하는 경우
(XML 편집기나 XPath 등 XML과 연관된 다른 표준들을 처리하는 처리기를 만드는 경우)
별도의 자바 객체들을 정의하기에는 배보다 배꼽이 커지는 경우
다음은 DOM이 갖고 있는 단점을 정리한 것이다.

메모리 사용이 많다 : DOM은 문서 전체를 메모리에 올려둔다. 따라서 문서가 어느 이상 클 경우 메모리 사용량이 지나치게 커질 수 있다. 또 특정 XML 문서의 구조와 무관한 일반적인 구조이므로 실제 처리되지 않는 공백 등도 메모리에 할당한다.
속도가 느리다 : DOM 구조를 빈번히 이용하는 경우면 상관없다. 그러나 DOM 구조를 한번 처리하거나 다른 자바 객체의 나무 구조로 변환하고 버리는 경우에는 DOM 객체의 생성 비용을 정당화할 수 없다.
DOM은 IDL로 정의돼 있다. 따라서 이를 자바로 매핑(mapping)한 현재 DOM API는 자바 프로그래머 입장에서는 매우 부자연스럽고 사용이 불편하다. 또 특정 형식의 문서를 처리할 경우 DOM은 지나치게 일반적이므로 문서의 특성을 충분히 반영하지 못한다.
DOM에 비해 SAX는 일반적으로 다음과 같은 경우에 적합하다.

XML 문서를 순차적으로 일괄 처리하는 경우
상대적으로 XML 문서가 간단하고 그 구조 자체가 주요 관심사가 아닌 경우
결국 SAX를 사용하는 것은 DOM과 같은 메모리, 속도와 관련된 문제점을 해결하기 위함이다. 하지만 SAX는 이와 달리 DOM 생성 작업이 단순하지 않다. 실제로도 DOM 파서는 SAX 파서를 이용하되, DOM 생성 부분을 추가하고 있는 경우가 대부분이다. SAX는 그야말로 단순하게 어떤 요소를 읽었는지 등의 정보를 줄 뿐, 현재 이 요소가 어떤 요소의 일부인가 등의 문맥 정보를 자동으로 유지해 주지 않는다. 이는 사용자가 문서의 구조로부터 정보를 추출하기 위해 보다 많은 일을 해야함을 의미한다.

결국 SAX를 사용하는데 있어 성패는 SAX의 단점을 보완할 만한 도구를 적절히 만들 수 있느냐에 달렸다고 해도 과언이 아니다. 따라서 여기서는 그런 종류의 도구를 하나 만들어 볼 것이다. 참고 자료 10은 이런 맥락에서 SAX를 사용하는 방법에 대한 좋은 지침서이다.


DOM을 이용한 XML 문서 처리

DOM 파서를 사용해 DOM 객체를 메모리에 생성시킨 이후 하는 일은 결국 이 DOM 객체의 나무 구조를 따라가며 필요한 정보를 얻거나 필요한 출력을 만드는 것이라 볼 수 있다. 다음과 같은 간단한 XML 파일을 생각해 보자.

<?xml version=”1.0” ?>
<student-file>
<student>
    <name>A</name>
    <id>1</id>
</student>
<student>
    <name>B</name>
    <id>2</id>
</student>
</student-file>

<리스트 1>은 이를 파싱해 내용을 원하는 형식으로 화면에 출력하기 위한 프로그램이다. 표준인 JAXP(Java API for XML Proce ssing)를 사용했으므로 실상 JAXP를 제공하는 아무 파서에서나 실행된다.

<리스트 1>처럼 흔히 DOM을 사용한 XML 처리 부분은 루프(loo p)와 조건 분기가 빽빽하게 들어있어 읽기가 힘든 코드가 되는 것이 보통이다. 지금은 문서의 구조가 단순하지만 문서의 구조 자체가 복잡하다고 생각하면 들여쓰기(indent)가 끔찍할 수준에 이를 것이다.

주로 DOM으로 하는 일이 특정 순서에 의해 XML 각 부분을 처리하고 지나가는 것인데 비해, DOM API 자체는 DOM 구조를 특정 순서로 방문하기 위한 쉬운 방법을 제공하지 않는다. 이런 맥락에서 현재 아직 최종 버전이 나오지 않은 DOM 레벨 2에서는 필요한 노드만 추출해 DOM 객체의 나무 구조를 쉽게 방문하기 위한 API를 추가했다. 여기서는 DOM과 같이 나무 구조로 된 데이터 구조를 훑어가기 위해 자주 사용되는 디자인 패턴인 방문자(visitor) 패턴을 사용한 일반적인 라이브러리를 구현해 보도록 하겠다. DOM 구조의 특정 부분을 골라내는(filtering) 부분은 DOM 레벨 2의 모델을 빌려 왔고, 방문자 구조는 일반적인 모델과는 다소 다르게 DOM 객체들 자체에는 아무런 변화를 주지 않고 구현하는 방법을 사용했다(방문자 패턴에 대해서는 참고 자료 12 참고).

다음은 주요 객체에 대한 설명이다. 소스코드는 지면관계상 ‘이달의 디스켓’을 참고하기 바란다.

◆ Visitor : 인터페이스로서 특정 종류(no detype)의 특정 이름(nodename)을 가진 노드를 방문했을 때 불리는 두 가지의 메쏘드를 정의하는 객체. voidvi sitPre(Node)와 void visitPost (Node)가 그 예로, 전자는 자식 노드를 방문하기 전에 그리고 후자는 자식 노드를 모두 방문한 후에 불린다. 관심있는 종류마다 사용자가 만들어줘야 한다. DefaultVisitor는 두 메쏘드를 아무 일도 하지 않도록 구현해 놓은 편의 객체이다. 두 메쏘드 중 하나만 구현하고자 할 때 사용한다.

◆ NodeFilter : DOM 객체의 구조를 따라갈 때, Visitor에게 넘겨줄 노드인가를 판단하는 객체. 사용자가 만들어줘야 한다. NodeFilter에는 int accept(Node) 메쏘드가 있는데, 여기서 돌려줄 수 있는 값은 세 가지이다. NodeFilter.ACCEPT는 Visitor에게 넘겨주고, NodeFilter.REJECT는 해당 노드와 그 자식 노드까지 모두 무시하며, NodeFilter.SKIP은 해당 노드만 무시하되 자식 노드들은 Visitor에게 넘기라는 뜻이다. NodeFilter.ACCEPT_ALL은 모든 노드를 받아들이는 NodeFilter 객체이다.

◆ DOMTraverser : DOM 구조를 깊이 우선(depth-first)으로 훑는 객체. 실제 NodeFilter로 노드를 시험하고 적절한 Visitor를 찾아 호출해 주는 역할을 담당.

사용법은 <리스트 2>를 보자. 이것은 <리스트 1>과 동일한 역할을 하는 객체이다. 각 Visitor 객체들이 요소별로 정의돼 있고, DOMT raverser가 이 객체에 정의된 메쏘드를 필요에 따라 불러준다.


SAX를 이용한 XML 문서 처리

SAX는 이름이 의미하는 대로 그야말로 단순함 그 자체이다. <리스트 3>은 Xer ces 파서를 사용한 예로, 표준인 JAXP를 쓰지 않았다. 이는 JAXP 1.0이 아직 SAX2를 지원하지 않기 때문이다. <리스트 1>의 DOM을 사용한 예제보다는 다소 정돈된 느낌을 준다.

SAX 파서는 문서를 훑어가면서 태그를 만났을 때 startEle ment(...)를 불러주는 식으로 정보를 주기는 하지만, 그 정보를 어떤 체계적인 형태로(이를 테면, DOM과 같은 나무구조) 저장해 주지는 않는다. 따라서 SAX를 사용해 프로그래밍하는 경우 가장 중요한 것은, 과연 특정 메쏘드를 불렀을 때 이 부분이 문서의 구조상 해당 부분을 유지하는 것이다. <리스트 3>은 XML 문서의 내용을 조금 다른 형식으로 표시하는 것뿐이므로 구조적인 정보는 별도로 유지할 필요가 없어 비교적 단순해 보인다.

‘현재 문서 구조에서 어느 부분을 처리하고 있는가’ 하는 문맥 정보는 일반적으로 SAX의 ContentHandler(SAX1이라면 Document Handler) 객체 내 필드 변수에 저장해 ContentHandler의 각 콜백(callback) 메쏘드 사이에서 공유한다. 경우에 따라서는 스택을 사용하는 것이 많은 도움을 주기도 한다. 흔히 전역 변수(global variable)를 지나치게 사용하면 프로그램의 각 부분이 얽혀 유지보수가 어려운 것처럼 공유 변수도 처리할 XML 문서가 복잡해지다 보면 결국 마찬가지 결과를 가져올 수 있다(참고 자료 10).

참고 자료 10의 두 번째 기사에서는 이런 문제를 쉽게 해결할 수 있는 수단으로 TagTracker와 SAXMapper라는 라이브러리를 제시하고 있다. 자바월드에서 내려받을 수 있는 소스코드에는 TagTracker .java와 SaxMapper.java, SaxMapperLog.java의 최종 버전이 빠져 있는데, 필자가 손을 봐서 ‘이달의 디스켓’에 넣어뒀다. 이 자료에는 예제가 충분히 나와 있으므로 여기서는 기본적인 소개와 간단한 예제만을 다루도록 한다.

TagTracker라는 객체는 일종의 콜백 객체로서 앞서 소개한 바 있는 Visitor 객체와 유사한 역할을 한다. 단, 여기서 메쏘드는 네 가지로 각각의 의미는 다음과 같다.

◆ onStart(String uri, String localName, String qName, Attributes attr) : TagTracker에 연관된 요소가 시작될 때 불린다. Visitor의 visitPre와 같은 기능을 수행.

◆ onEnd(String uri, String localName, String qNa me, CharArrayWriter contents) : TagTracker에 연관된 요소가 닫힐 때 불린다. Visitor의 visitPost와 같은 기능을 수행.

◆ onDeactivate() : 현재 TagTracker가 담당하는 요소의 자식 요소의 처리가 시작되기 전에 현재의 TagTracker에게 그 사실을 알려주기 위해 호출된다.

◆ onReactivate() : 현재 TagTracker가 담당하는 요소의 자식 요소의 처리가 완결되자마자 불린다.

특정 요소에 대응하도록 만들어진 TagTra cker는 마치 파일 경로명을 적어 주듯 상대 경로명과 함께 다른 TagTracker의 track 메쏘드를 사용해 등록시켜 준다. 이렇듯 나무구조 형태로 TagTracker의 등록을 마치면 SaxMapper가 스스로 XML 파일을 파싱해 가며, 필요한 TagTracker를 찾아 앞서 설명한 onXXX 메쏘드를 호출해 준다.

<school-file>
<student>
    <name>Student</name>
    <id>12</id>
</student>
<teacher>
    <name>FirstTeacher</name>
    <id>AA</id>
</teacher>
<student>
    <name>NextStudent</name>
    <id>13</id>
</student>
<teacher>
    <name>SecondTeacher</name>
    <id>1A</id>
</teacher>
</school-file>

앞의 소스는 맨 처음 소스를 약간 변형한 것으로서 학생뿐 아니라 이제는 선생님들에 대한 정보를 섞어 나열하고 있다. 여기서 선생님의 ID는 16진수, 학생들의 ID는 10진수로 표현돼 있다고 가정하자. 자, 이제 이 XML에 담긴 정보를 <리스트 4>와 같은 객체로 연관시키려고 한다. 이를 TagTracker를 사용해 작성하면 <리스트 5>와 같은 프로그램이 만들어진다.

우선 문서 요소에는 별 관심이 없으며 루트 TagTracker에 track (“school-file/student”, student)과 같은 식으로 더해 주고 있음을 알 수 있다. <리스트 4>에 대해서는 별도로 TagTracker를 등록한 적이 없으므로 아무런 처리 없이 넘어간다. SAX는 DOM 대신 독자적인 자바 객체를 생성하는데 상당히 유용한 도구라고 볼 수 있다.


표준 DOM의 대안, JDOM

비록 DOM이 많은 단점이 있다지만 프로그래머 입장에서는 메모리에 있는 데이터 구조를 다루는 방식이 이벤트 방식보다 자연스럽다. JDOM은 DOM의 장점을 살리는 동시에 자바 프로그래머에게 부자연스럽다는 DOM의 문제점을 자바2의 컬렉션 등을 사용해 해결한다. 따라서 보다 자바에 특화된 직관적인 형태의 인터페이스를 제공하는 XML 처리 도구이다(참고 자료 6). 또 일반적으로 저성능에 메모리를 많이 차지하는 DOM 파서에 비해 매우 적은 메모리를 사용하며, 속도도 SAX 파서를 사용하는 경우와 필적할 만큼 빠르다.

JDOM은 API가 작고 간단해 쉽게 사용할 수 있다(<리스트 6>). 이를 <리스트 1>과 비교해 보자. 우선은 java.util의 컬렉션 클래스를 그대로 사용하며, 상대적으로 일반적인 자바 객체를 다루는 것처럼 작업할 수 있음을 알 수 있다.

문제는 일반적으로 JDOM이 아닌 DOM 객체나 SAX 이벤트가 응용 프로그램 내에서 XML 문서를 전달하는 수단이라는 점인데, JD OM은 이를 위한 배려도 잊지 않고 있다. org.jdom.output 패키지 안에 있는 DOM Outputter나 SAXOutputter(아직 구현되지 않았음) 등을 이용하면 쉽게 DOM이나 SAX로 바꿀 수 있다. 또 SAX Builder 대신 DOMBuilder를 사용하면 DOM 구조를 직접 JDOM으로 바꿀 수도 있다.

참고로 이 글을 쓰는 현재 JDOM은 1.0 베타 5까지 나와 있다. 따라서 JDOM 사이트에는 미리 컴파일해 둔 바이너리는 물론 API 문서도 제대로 올라와 있지 않다. 불행히도 직접 라이브러리와 API 문서(javadoc으로 소스코드에서 생성)를 생성해야 한다.


오라클의 XML 클래스 생성기

오라클의 XML 클래스 생성기는 DTD로부터 DTD에 정의된 문서 구조를 반영하는 자바 클래스를 생성해 준다. 단, 앞서 언급한 바와 같이 이 도구는 절름발이이다. 왜냐하면 메모리에 자바 객체로 XML 문서를 표현하고 DTD에 맞는지 점검한 뒤 다시 XML 파일로 바꿀 수 있을 뿐, XML 파일로부터 거꾸로 생성된 객체들의 구조를 생성해 주지는 못하기 때문이다. 물론 이 기능도 유용하다고 할 수 있지만, XML 데이터 바인딩이 줄 편의를 생각하면 여러 가지로 부족한 면이 없지 않다. 이 글에서 클래스 생성기를 소개하는 주된 이유는 XML 문서의 구조를 반영한 클래스를 사용하는 것이 얼마나 편리한가를 보여주기 위해서다. 참고로 이 생성기는 오라클의 XML 파서에 의존한다. 즉, 클래스 생성기나 이것으로 생성된 코드를 사용하기 위해서는 반드시 오라클 파서가 설치돼 있어야 한다(클래스 생성기만 내려 받아도 그 안에 포함되어 있다). 이제 실제 예를 통해 살펴보도록 하자.

우선 가장 먼저 소개한 XML 소스코드에 대한 DTD가 필요한데, 그 내용은 다음과 같다.

<!ELEMENT student-file (student*)>
<!ELEMENT student (name, id)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT id (#PCDATA)>

다음으로 클래스 생성기를 실행해야 하지만 불행히도 javac와 같이 별도 명령으로 제공하는 것이 아니므로 직접 프로그램을 작성해야 한다. 여기서는 일을 간단히 하기 위해 클래스 생성기에 따라오는 예제를 이용하도록 한다. SampleMain.java가 그것으로, 클래스 생성기 zip 파일을 풀면 sample 디렉토리 아래 위치해 있다.

java SampleMain -root student-file student-file.dtd

앞의 명령에서 “-root”는 외부 DTD인 경우에 루트 요소를 지정하기 위해 필요하다. 이렇게 생성된 파일들이 바로 Student_file.java, Student.java, Name.java, Id.java, Student_file_dtd.txt(이 파일은 XML 파일에 포함된 경우에 특히 중요하다. 내용은 DTD 정의와 거의 동일)이다. <리스트 7>은 이들 객체를 이용해 새로운 XML 파일을 만드는 예제이며, <리스트 8>은 이를 실행한 결과이다.

<리스트 7>에서 주의할 부분은 주석 처리된 내용으로, 이 부분을 다시 넣을 경우 validateContent()에서 예외가 발생한다. 이는 메모리에서 해당 객체들이 DTD에 합당한 문서를 구성하고 있는가를 점검하기 때문이다. XML 편집기 같은 소프트웨어는 사용자가 편집하는 내용이 그때그때 DTD에 만족하는지를 점검할 필요가 있을 경우에 상당히 유용하다.

참고로 Xerces의 org.apache.xerces.parsers.Revalidating DOMParser도 이런 용도에 사용할 수 있다. validate(Node)라는 메쏘드가 그 역할을 수행하는 메쏘드이다. 이것 역시 일반적인 XML 문서 편집기를 만들거나 다른 데이터로부터 XML을 만드는 경우, 실제 XML 파일을 출력해서 다시 파싱하지 않고도 DTD가 정하고 있는 구조적 조건을 만족하는지 확인할 수 있도록 해 준다.


도구 연마는 각자의 몫

지금까지 XML 문서를 처리하기 위한 기본적인 도구들을 비교해 살펴봤다. ‘언제 어떤 도구를 사용할 것인가’가 얼마나 응용 프로그램에 영향을 미칠 수 있는지 충분히 전달됐으리라 생각한다. 또 DOM의 경우에서와 같이 같은 도구라도 어떤 방식으로 사용하는가에 따라 응용 프로그램의 복잡도를 높이기도 낮추기도 한다는 것도 잘 알았으리라 믿는다. 결국 어떤 문제든지 흔히 발생하는 경우를 쉽게 해결할 수 있도록 도구를 만들어 두고, 프로그래밍 패턴을 정리해두는 것이 성공의 열쇠이다.

마지막으로 이 글에서 제시한 일부 도구들은 아직 충분히 다듬어지지 않았다. 이를 테면 네임스페이스를 활용하는 경우 다소 불편한 부분이 있을 지도 모르겠다. 날이 서지 않은 도구를 갈아 날이 서게 하는 것은 독자들의 몫이다.

글쓴이 연락처 : 김도형 dynaxis@4dl.com
정리 : 박은정 whoami@sbmedia.co.kr 


본기사는 월간 마이크로소프트웨어의 기사내용을 발췌한 내용입니다.

반응형