때는 늦은 가을.. 무더위가 한풀 꺽일 무렵 담당업무의 까다로운 요구사항으로 고민에 빠져있던 저는 프로젝트PM님의 소개로 이 기술과 처음 만났습니다. 두려움 반 설렘 반으로 이 기술을 대면했던 순간과는 달리 이 기술을 쓰면 쓸수록 더욱 더 이 기술에 매료 되었습니다. 왜냐하면 이 기술을 씀과 동시에 매우 제한적이고, 유연하지 못했던 패키지 프로그램에 활력을 불어 넣었으니까요. 그래서 복잡 다양했던 요구사항을 멋지게 해결 할 수 있었습니다. 하지만 이 기술을 선택하기 까지 몇 가지 대안은 있었습니다. 패키지 내장함수사용과 전문 수정 등이 그것 이었는데, 먼저 패키지 내장 함수는 패키지에 종속되어있었기 때문에 해당 기능을 사용하기 위해서는 현재 프로젝트 상황에 맞게 커스터마이징 해야 하는 문제점이 있었고, 이를 위해서는 추가비용이 발생할 수 밖에 없었습니다. 그리고 두 번째 대안은 다양한 출력양식을 적용하고 있는 상황에서 전문 자체를 수정한다는 것은 개발 시기 상 맞지 않다는 문제점이 있었습니다. 이 때문에 역시 XPapth가 가장 유력한 대안이 될 수 있었습니다. 그래서 오늘은 그 기억을 되살려 이 기술의 정체를 재 조명 해 보도록 하겠습니다.

XPath의 정의와 특징

XPath(XML Path Language)는 W3C의 표준으로 XML(Extensible Markup Language)문서의 구조를 통해 경로(Path)위에 지정한 구문을 사용하여 항목을 배치하고 처리하는 방법을 기술하는 언어입니다. XML 표현보다 더 쉽고 약어로 되어 있으며, XSL변환(XSLT)과 XML지시자 언어(XPointer)에 쓰이는 언어로 XML 문서의 Node를 정의하기 위하여 경로식(Path Expression)을 사용하며, 수학 함수와 기타 확장 가능한 표현들이 있습니다.

XPath는 W3C의 권고 사항으로 견고한 표준이 확립되어 있고, 다양한 API와 함수, 경로 식(Path Expression)덕분에 유연성과 확장을 요하는 서버 통신이나 도큐먼트 출력, 기타 XML을 사용하는 여러 분야에 널리 사용되고 있습니다.

XPath의 데이터 모델

① XML 문서는 트리 구조의 Node들로 구성됩니다.
② XML 문서의 트리 구조는 단지 개념적 구조입니다.
③ XPath는 XML 문서를 트리 구조로 모델링 하여 처리합니다.
④ XML 문서의 계층구조, 즉 트리 구조에서 Node들을 식별, 선택, 조작하기 위해 XPath 표현 식을 사용합니다.

XPath Terminology

XPath를 이해하기 위해서는 XPath에서 사용되는 용어에 친숙해 질 필요가 있겠습니다. XML을 기반으로 구조를 파악하기 위한 XPath의 용어에 대해서 알아보겠습니다.

① Nodes: XPath에는 Element, Attribute, Text, Namespace, Processing-instruction, Comment, and Document 의 총 7가지의 Node들을 사용하고 있습니다. XML문서도 트리를 구성하는 Node로 취급합니다. Element트리 중 최고 상위Element를 Root Element라고 합니다.

② Atomic values: 부모와 자식이 없는 단일 Node들의 값을 말합니다.
예) <TITLE>XPath</TITLE>, <TYPE>A</TYPE>

③ Items: 단일 값들(Atomic values)과 Node들을 말합니다.
예) <TITLE>, A

④ Node들 간의 관계: XPath의 각 Node들은 다음의 관계를 가집니다.

  • Parent: 각각의 ElementNode와 속성은 하나의 부모를 가집니다.
  • Children: ElementNode는 0개 이상의 자식요소를 가집니다.
  • Siblings: 같은 부모를 가지는 Node들을 말합니다.
  • Ancestors: Node들의 부모 혹은 부모의 부모를 말합니다.
  • Descendants: Node들의 자식 혹은 자식의 자식을 말합니다.

XPath의 표현식(Expression)과 함수(Function)

① Expression: XPath는 XML에서 특정 Node나 Node들을 선택하고자 할 때 파일경로와 비슷한 경로 식(Path Expression)을 사용합니다. 파일경로와 마찬가지로 절대경로와 상대경로 또한 가집니다.
예) /:루트 Node, @:Node의 속성

보다 더 자세한 XPath표현식에 대한 자세한 정보를 원하시면 아래 사이트를 방문해 보세요. http://hyeonstorage.tistory.com/206

② Functions: XPath는 100여개 이상의 내장함수를 포함하고 있습니다. 이러한 함수들은 문자열, 숫자, 날짜와 시간비교, 시퀀스 생성, 논리값 등 매우 다양합니다.
예) fn:node-name(node): Node명 리턴, fn:abs(num): 절대값 리턴, fn:concat(string,string,...)

보다 더 자세한 XPath함수에 대한 자세한 정보를 원하시면 아래 사이트를 방문해 보세요. http://www.w3schools.com/xpath/xpath_functions.asp
http://hyeonstorage.tistory.com/209

XPath의 문법

① Node선택:

표현 설명
nodename Node명이"nodename"인 Node선택
/ 루트Node로 부터 선택
// 현재 Node로부터 문서상의 모든 Node를 조회
. 현재Node 선택
.. 현재Node의 부모Node 선택
@ 현재Node의 속성선택

② 술부(Predicates): ‘[….]’의 형태로 기술되며, 특정 값이나 조건에 해당 하는지 여부를 판별합니다.
예) //title[@lang]: 속성 lang을 갖는 모든 title엘리먼트를 선택.

③ 불특정 Node선택: wildcard를 이용하여 특정하지 않은 Node(들)를 선택 합니다.

표현 설명
* 매칭 되는 모든 ElementNode
@* 매칭 되는 모든 속성Node
Node() 현재 Node로부터 문서상의 모든 Node를 조회

④ 복수경로 선택: 복수의 경로식을 사용하여 Node(들)를 선택합니다.
예) //title | //price: 문서상의 모든 title ElementNode와 price ElementNode를 선택.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

⑤ 축(Axes): 현재Node와 관련된 Node 셋을 정의합니다.

축명 결과
ancestor 현재 Node의 모든 ancestor( parent, grandparent,..etc)Node들을 선택
ancestor-or-self ancestor + 현재Node
Node() 현재 Node로부터 문서상의 모든 Node를 조회
attribute 현재 Node의 모든 속성Node
child 현재 Node의 모든 자식Node
descendant 현재 Node의 모든 자손Node(child, grandchild..etc)
descendant-or-self descendant + 현재Node
following 현재 Node의 닫기 태그 이후의 문서상 모든 Node
following-sibling 현재 Node 이후의 모든 siblingNode
namespace 현재 Node의 모든 namespaceNode
parent 현재 Node의 부모Node
preceding 현재 Node가 나타나기 이전의 모든 Node (ancestor, namespace, 속성Node 제외)
preceding-sibling 현재 Node 이전의 모든 siblingNode
following-sibling 현재 Node 이후의 모든 siblingNode
self 현재 Node 자기자신

⑥ Location Path Expression:

Node의 위치를 명시적으로 표현하는 방법으로 ‘/’로 시작하는 절대경로와 ‘/’로 시작하지 않는 상대경로 표현법이 있습니다.

각 스텝은 현재 Node 셋의 Node들에 대하여 평가되고 다음의 세 부분으로 구성됩니다.

  • 축: 선택될 Node와 현재 Node와의 관계를 명시.
  • Node 테스트: 축과 Node의 구별을 위함.
  • 0개 이상의 술부: 선택된 Node를 정제하기 위함.
    예) child::*/child::price – 현재 Node의 모든 grandchildrenNode에서 priceNode를 선택

XPath의 적용사례

고객사 프로젝트 수행 시 증권과 약관을 출력하는 업무를 수행하였습니다. 출력물 출력 시 응답전문을 xml로 생성하여, 패키지 출력 프로그램을 이용하여 생성 된 xml전문을 분석하여 출력하였는데, 보험증권과 약관은 매우 다양한 정보를 포함하고 있어서 이를 포함하고 있는 xml을 분석하여 해당 정보를 알맞게 출력하기가 그리 쉬운 일은 아니었습니다. 하지만 이 복잡 다양한 분석 작업을 수행하기 위해서 XPath가 좋은 해결책이 되었습니다.

사용 예> 서버에서 생성된 xml전문을 분석하여 아래와 같은 출력물 포멧에 맞게 데이터를 추출하여 출력물을 완성합니다.

출력결과물

출력결과물 자, 이제부터 여러분도 같이 해보세요. 위와 같이 출력양식을 작성하기 위해서는 아래와 같은 xml전문이 필요합니다. 그리고 전문을 분석하기 위한 도구가 있어야 겠지요? 우리는 이미 XPath를 배웠고 또 알고 있습니다. 그럼 XPath를 작성할 스타일 쉬트가 필요합니다. 그것을 우리는 XSLT하고 합니다. 자세한 내용은 http://www.w3schools.com/xsl/default.asp를 참조 하세요. 아주 따라하기쉽게 자세히 설명이 되어있습니다. 그럼 먼저 xml 전문입니다.

<?xml version="1.0" encoding="UTF-8"?>  
<?xml-stylesheet type="text/xsl" href="quotation.xslt"?>  
<Contract quotationNo="00201400123456" issueDate="20140206">  
    <Contractor name="홍길동" address="서울 특별시 중구 새천로 1" age="49"/>

    <Object code="M1234" name="맛나공장" typeCode="2">
        <Coverage currency="USD" premium="200" code="C1111"/>
        <Coverage currency="USD" premium="250" code="C1112"/>
        <Coverage currency="USD" premium="300" code="C1113"/>
        <Location address="강원도 강릉시 삼척동"/>
    </Object>

    <Object code="M1235" name="번창해상가" typeCode="3">
        <Coverage currency="USD" premium="200" code="C1114"/>
        <Coverage currency="USD" premium="250" code="C1115"/>
        <Coverage currency="USD" premium="400" code="C116"/>
        <Location address="경주 김해시 김해동"/>
    </Object>

</Contract>  

다들 xml을 작성하셨나요? 이제 그럼 XPath를 사용하여 XSLT를 작성해야할 차례입니다. 제가 먼저 작성을 해보았는데요. 한번 보시죠.

<?xml version="1.0" encoding="UTF-8"?>  
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  
    <xsl:template match="/">
        <html>
            <body>
                <h2>출력결과</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th colspan="6">Quotation</th>
                    </tr>
                    <tr>
                        <th align="left">No.</th>
                        <td colspan="5">
                            <xsl:value-of select="//@quotationNo"/>
                        </td>
                    </tr>
                    <tr>
                        <th align="left">Contractor</th>
                        <td colspan="2">
                            <xsl:value-of select="//Contractor/@name"/>
                        </td>
                        <th align="left">Age</th>
                        <td colspan="2">
                            <xsl:value-of select="//Contractor/@age"/>
                        </td>
                    </tr>
                    <tr>
                        <th align="left">Address</th>
                        <td colspan="5">
                            <xsl:value-of select="//Contractor/@address"/>
                        </td>
                    </tr>
                    <xsl:for-each select="Contract/Object">
                        <tr>
                            <td>목적물<xsl:value-of select="position()"/></td>
                            <td colspan="2">
                                <xsl:value-of select="@name"/>
                            </td>
                            <td>Cur.</td>
                            <td colspan="2">금액</td>
                                <xsl:for-each select="child::Coverage">
                                    <tr>
                                        <td>담보<xsl:value-of select="position()"/></td>
                                        <td>코드</td><td><xsl:value-of select="@code"/></td>
                                        <td><xsl:value-of select="@currency"/></td>
                                        <td colspan="2"><xsl:value-of select="@premium"/></td>
                                    </tr>
                                    <xsl:if test="position() = last()">
                                        <tr>
                                            <td colspan="3" align="right">합계</td>
                                            <td><xsl:value-of select="@currency"/></td>
                                            <td colspan="2"><xsl:value-of select="sum(parent::Object/Coverage/@premium)"/></td>
                                        </tr>
                                    </xsl:if>
                                </xsl:for-each>
                        </tr>
                        <xsl:if test="position() = last()">
                                        <tr>
                                            <td colspan="3" align="right">총합계</td>
                                            <td><xsl:value-of select="child::Coverage/@currency"/></td>
                                            <td colspan="2"><xsl:value-of select="sum(//Coverage/@premium)"/></td>
                                        </tr>
                                    </xsl:if>
                    </xsl:for-each>
                    <tr>
                        <td colspan="6" align="right">
                            상기 내용과 같이 가입을 증명합니다.
                        </td>
                    </tr>
                    <tr>
                        <td colspan="3" align="right">
                            출력일
                        </td>
                        <td colspan="3">
                            <xsl:value-of select="//@issueDate"/>
                        </td>
                    </tr>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>  

중간중간 앞에서 학습한 XPath문법이 보이시나요? 마치 jsp의 EL과 비슷하고 FreeMarker와도 닮았습니다. 한번 살펴 보시고 따라해 보시기 바랍니다.

가장 중요한 결과화면 입니다.

실행결과

결론

프로젝트를 하다 보면 뜻하지 않은 곳에서 의외의 별에서 온 복병을 만날 때가있습니다. 그럴 땐 보도듣도 못한 복병에게 무작정 대들거나 무시하기 보다는 가만히 별에서 온 복병과 있는 힘껏 대화를 시도해 보세요. 그 복병이 정말 별에서 왔는지, 달에서 왔는지 아님 나와 같은 지구인인지.. (정말 별에서 왔다면 대화 조차 불가능 하겠군요.. ) 지구인 이라면 땡큐! 아니라면 오마이갓~ 을 외치시고 다시 자기 자신을 돌아 보세요. 분명 어디선가 본듯한 기술일 테니까요. 왜냐면 그 기술은 400년전부터 이 세상에 살았을 기술일 테니까요. (모든 기술은 하나로 귀결 됩니다. ) 저는 이 XPath란 기술이 바로 그런 기술이었습니다. 부디 여러분이 수행하는 업무에 조금이나마 도움이 되었으면 하는 바램입니다.

감사합니다.

참고사이트

  1. http://www.w3.org/TR/xpath20/
  2. http://www.w3schools.com/xpath/default.asp
  3. http://hyeonstorage.tistory.com/

[출처] http://www.nextree.co.kr/p6278/