Java

자바 환경에서 XQuery 사용하기 (중)

griffy 2009. 5. 15. 09:11

자바 환경에서 XQuery 사용하기 (중)

for 문 사용하기

for 문은 자바와 C# 같은 프로그램 언어에서 사용하는 방법과 거의 비슷하게 쓴다. for 문의 형식은 다음과 같다.

for $variable-name in XPath
...
변수명은 어떤 식별자(예를 들면 x)라도 상관없다. 일반적으로 변수명은 용도(firstName, title)에 따라 정해지지만, 이 변수는 반복 카운터로만 사용할 것이므로, 문자 한 개만 사용하는 것도 나쁘지 않다.

XPath는 어떤 것이라도 사용할 수 있다. /cds/cd는 완벽한 예다. 예를 들어 다음과 같이 사용할 수 있다.

for $cd in doc("catalog.xml")/cds/cd
...

이것이 전부다. 변수 &cd는 XPath 경로 /cds/cd에 의해 반환되는 각각의 노드 값을 갖는다. 위 예에서, ...이라고 표시한 것은 XQuery 표현식의 나머지 부분들인데, 이에 대해서는 뒤에 알아 보겠다.

프로그래머들의 이해를 돕기 위해 예를 들면, 위 문장은 아래 문장과 다를 바 없다.

for (int i = 0; i CD cd = cdArray[i];
// Process CD
}

리스트로 표현해도 비슷하다.

for (Iterator i = cdList.iterator(); i.hasNext(); ) {
CD cd = (CD)i.next();
// Process each CD
}

let을 사용하여 변수 할당하기

let 절은 변수를 할당하는 데 쓴다. 이미 XQuery에서는 식별자 앞에 달러($) 표시를 붙임으로써 변수가 정의됨을 알았을 것이다. 대부분의 경우에는 앞에서 설명한 것처럼 XQuery에서는 let 절로 명확하게 변수를 생성하기보다는 for 절에서 묵시적으로 변수를 생성할 것이다.

그러나 명시적으로 변수를 생성하려면 아래와 같이 하면 된다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...
예제를 보면 검색 대상 문서가 변수에 할당되었다. XQuery는 변수에 할당을 위해 :=를 사용하는데, 파스칼(Pascal) 언어를 사용해 본 적이 없다면 이 기호가 조금 생소할 것이다. 좀 더 실질적인 상황에서는, XML 문서 목록을 순회하는 함수에서 $docName에 그 문서들의 이름을 차례로 할당한다. 더 큰 목록에 포함된 각 문서에서 cd 요소를 선택하고, 동일한 방식으로 처리할 수 있다.

return을 사용하여 쿼리 완성하기

FLWOR에서 order by는 잠시 건너 뛰고, 지금은 먼저 쿼리를 완성해보자. 지금까지 설명한 내용은 다음과 같다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...

다음 단계는 반환이다. 이 쿼리는 모든 cd 요소를 선택했고, 그것이 검색하려던 노드 집합이긴 하지만, 그대로 반환하면 사용하기 힘들다. 이러한 요소들을 그대로 반환하기보다는 title 요소에 저장된 각 CD의 제목처럼 좀 더 구체적인 데이터를 반환하려고 한다. 이때 return이 필요하다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return $cd/title/text()



설명하자면, 모든 cd 요소를 선택했고, 각 노드는 $cd에 차례로 할당된다. 마지막으로 return 절은 요소 그 자체를 반환하는 대신, 검색하려던 데이터를 가지고 있는 "title"이라는 이름의 자식 요소를 반환한다.

아래와 같은 단순한 실수를 하지 않도록 주의하라.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return /cds/cd/title/text()


위 예제는 세 가지 중대한 문제를 갖고 있다.

첫째, XQuery의 목적을 헛되게 한다. 아무런 쿼리도 수행하지 않고, XPath의 위치만 반환한다.
둘째, CD들을 검색할 문서를 가리키는 doc($docName)에 상관없이 데이터를 반환한다.
마지막이 가장 중대한 문제인데, for 절로 반환된 노드 집합에 적용한 필터나 순서를 무시한다.
다음 작업을 하려고 한다면 이 점은 매우 중요하다. for 절에서 정의하고, return 절에서 다시 사용할 수 있는 변수가 있다는 점을 명심해라. 이 단순하면서도 중요한 법칙을 잘 지키면, 쿼리가 의도한 대로 수행됨을 확신할 수 있다.

where 절로 검색 조건 지정하기

where 절을 사용하면 XQuery의 검색 조건을 더욱 세밀하게 지정할 수 있다. XQuery의 where 절은 SQL에서와 같이 동작한다. 결과 집합을 정제하려면 검색에 where 절을 사용하면 된다. 간단한 예제를 보자.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
return $cd/title/text()

위의 예제는 title이 reggae인 모든 CD를 반환한다. where 절이 꽤 직관적이어서 따로 설명할 것도 없지만, and와 함께 사용하면 좀 더 복잡한 조건을 적용할 수 있다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
and count($cd/track-listing/track) > 10
return $cd/title/text()

위 예제는 열 개 이상의 track을 가진 모든 reggae CD를 반환한다.

where 절의 또 하나의 중요한 용도는 조인(join)을 수행하는 것이다. Listing 2와 같은 XML 파일이 있다고 가정하자.

Listing 2. CD 목록 문서의 확장된 구조






Track title







Bob
Marley






위 예제는 Listing 1의 XML 구조를 확장한 것인데, id 속성으로 식별할 수 있는 artist 요소가 추가되었고, 각각의 cd 요소에 포함된 한 개 이상이 artist 요소에 의해 각 CD와 조인되어 있다.

XQuery를 사용하면, CD와 그 CD의 artist를 조인할 수 있다. 아래의 예를 보자.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
return $cd/title/text()

여기에서 눈 여겨 볼 것은 두 가지다. 첫 번째로, for 절은 한 개 이상의 변수를 정의할 수 있다. 단지 CD들을 식별하는 대신, 여기서는 $artist도 정의해 artist 요소를 다루기 쉽다.

다음으로, where 절은 CD와 그 CD의 artist를 조인한다: where $cd/artist/$id = $artist/$id. 이렇게 하면, 각각의 CD와 그 CD의 artist들이 연결되고, SQL의 join과 같은 결과를 가져온다는 것을 기억하라. 다음으로, 검색 조건을 더 정의했다: $artist/lastName = 'Marley'. 이렇게 하면, "Marley"라는 lastName을 가진 모든 artist를 검색한다. 조인을 한 다음, return 절로 CD의 title들을 반환한다. 그 결과, lastName이 "Marley"인 artist를 포함하는 모든 CD의 title을 얻는다.

이 예제를 통해 XQuery를 어디에 써먹을지 알 수 있을 것이다. (아마도 많은 문서가 고급 검색을 염두에 둔 구조를 갖고 있지 않겠지만) XML 문서에 대해 SQL과 비슷한 복잡한 조인과 선택을 수행할 수 있다.

노드 순서 지정하기

where 절은 SQL의 WHERE와 거의 비슷하게 동작하지만, order by 절은 SQL의 ORDER BY와 똑같이 동작한다. XPath로 참조할 수 있는 어떤 것으로든 결과를 정렬할 수 있다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date
return $cd/title/text()

위 예에서는, 반환된 CD의 title들을 각 CD의 자식 요소인 release의 date 속성에 따라 정렬한다. 기본적으로 정렬은 오름차순(ascending)이지만, 원한다면 아래와 같이 명시적으로 지정할 수 있다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date ascending
return $cd/title/text()

물론, 내림차순(descending)으로 정렬할 수도 있다. 아래 예는 최근에 발매된 CD를 가장 먼저 반환한다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date descending
return $cd/title/text()

주의: date 속성을 날짜 타입(date type)으로 인식하는 스키마(schema)나 XQuery 처리기를 사용하지 않는다면, 위 문장은 오류가 발생한다. 더 안 좋은 것은 date를 문자열로 인식해 알파벳 순서로 정렬한다는 사실이다. 거의 모든 현대적인 처리기는 날짜를 인식하기 때문에 별로 신경 쓸 필요는 없다.

한 번에 여러 개의 정렬 조건을 적용할 수 있다. 예를 들어, 위의 예는 "Marley"라는 lastName의 artist를 포함하는 모든 CD를 반환하면서, 발매일 순으로 정렬한다. 아래 표현식은 artist의 이름, 발매일 순으로 정렬한다.

let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $artist/firstName, $cd/release/@date
return $cd/title/text()

모든 결과의 lastName은 같으므로 $artist/firstName에 앞서 $artist/lastName으로 정렬할 필요가 없다는 점을 주목하라.

자바 기술에 접목하기

자바 환경에서 XQuery를 사용하는 방법에 대한 글을 많이 접했겠지만, XQJ(XQuery API for Java)를 선택하는 프로그래머는 대부분 XPath와 XQuery를 대충 보고 넘어간다. 이제 여기에서 조금 더 배운 기초를 바탕으로 자바 프로그램에 XQuery를 사용해 보자.

XQuery는 벤더 중립적인 스펙이다

XQJ는 썬의 후원 아래, 자바 커뮤니티 프로세스의 일부인 JSR 225(링크는 참고자료를 보라)로 개발되고 있다. 스펙에는 몇몇 다른 벤더(썬, 노키아부터 BEA, 오라클, 인텔에 이르기까지)뿐 아니라 소수의 핵심적인 개인들(서블릿, JDOM, XML로 유명한 Jason Hunter 같은)도 참여하는데, 그 나름대로 특정 데이터베이스 벤더나 XML 제품 제조사에 종속되는 것을 막기 위해 노력하고 있다.

XQJ는 벤더 종속적인 구현체다

안타깝지만, XQJ에 대한 썬 표준 구현체는 아직 없다. 전문가 그룹에 있는 벤더 대부분은 자사 제품으로 XQJ를 구현하는 회사에서 일하고 있는데, 이는 특정 벤더에 국한된 문제를 다뤄야 한다는 것을 의미한다. 물론, 오랫동안 XML을 다뤄왔던 이들에겐 2000년대 초반에 있었던 XML 파서와 XSL 처리기 전쟁과 별 다를 바가 없다. 적당한 때가 되면, XQJ는 표준화될 것이고, 썬은 자체적으로 XQJ를 구현하거나, XML 파서와 XSL 처리기가 JAXP가 동작하는 것처럼 XQJ 구현을 위한 추상화된 API를 발표할 것이다.

XQJ 구현체 얻기

XQJ를 시작하는 쉬운 방법은 DataDirect에서 무료 시험판을 다운로드하는 것이다. 양식을 작성하는 게 귀찮겠지만, 이 글을 따라가다 보면 충분한 시간을 벌 수 있다. DataDirect XQuery 다운로드 사이트를 방문하자(참고자료). XML Documents Onl y 옵션을 선택하고, 접근을 원하는 데이터베이스를 입력해야 한다. 이 옵션들을 선택하면 datadirectxquery.jar 파일을 받을 수 있는 곳에 대한 안내가 들어있는 이메일을 받는다.

JAR 파일 풀기

설치 과정은 조금 혼란스럽다. 먼저, jar 명령을 사용하여 다운로드한 datadirectxquery.jar의 압축을 풀어야 한다. 그러나 그 전에 먼저, 설치할 디렉터리를 만들고, 그 디렉터리에 JAR 파일을 푼 다음, jar 명령을 실행하자.

[bdm0509:~/Desktop] mkdir xqj
[bdm0509:~/Desktop] cd xqj
[bdm0509:~/Desktop/xqj] jar xvf ../datadirectxquery.jar
inflated: XQueryInstaller.jar
inflated: ddxqj.jar
inflated: ExtensionTool.jar
inflated: Readme.txt
inflated: 3rdPartySoftware.txt
inflated: Fixes.txt
inflated: installer.properties

설치 프로그램 실행하기

이제 조금 전에 만든 디렉터리를 열고 XQueryInstaller.jar 파일을 더블클릭하자. 자바가 설치되어 있다면, GUI 설치 프로그램이 실행될 것이다.

trial 또는 licensed version 중에 선택하는 단계에서, trial을 선택하자. 다음으로, 설치할 디렉터리를 선택해야 한다. 여기에서 입력한 디렉터리에 쓰기 권한이 있는지 확인하자. 이 예에서는 /usr/local/java/xqj를 선택했는데, 먼저 /usr/local/java 디렉터리에 쓰기 권한이 있는지 확인해야 한다. 설치 과정에서 마지막 하위 디렉터리(이 예에서는 xqj)가 만들어지고, 그 안에 DataDirectory XQuery 파일들이 설치된다. 마지막으로 Finish를 클릭하고 설치가 마무리된다.

모두 마쳤으면, 새 디렉터리의 내용을 확인하자. 아래에 보이는 목록과 비슷해야 한다.

[bdm0509:/usr/local/java] cd xqj
[bdm0509:/usr/local/java/xqj] ls
3rdPartySoftware.txt examples lib
Fixes.txt help planExplain
Readme.txt javadoc src

XQJ JAR를 클래스패스에 추가하기

이제, 클래스패스에 lib 디렉터리와 ddxq.jar에 있는 XQJ 클래스들과 XQuery JAR를 추가해야 한다. 이것은 다운로드했던 원래의 JAR 파일이 아니라, 앞에서 압축을 푼 JAR 파일에 의해 설치된 것이다(DataDirect는 ZIP이나 tar.gz 파일을 다운로드하도록 해서 혼란을 줄이고 있다). 클래스패스는 수동으로도 설정할 수도 있고, 셸 스크립트나 .profile 파일을 사용할 수도 있고, 또는 IDE에서 JAR 파일을 클래스패스에 추가할 수도 있다. 어찌 됐건 ddxq.jar가 클래스패스에 들어있으면 된다.

데이터베이스 연결 설정

DataDirect에서 다운로드한 제품은 관계형 데이터베이스에 대해 XQueiry를 실행할 수도 있고, 데이터베이스에 연결할 수도 있다. 다운로드 양식을 작성할 때 어떤 데이터베이스를 사용할 것인지를 꼭 선택해야 한다. 그렇게 해야 데이터베이스 설정이 가능하도록 조정된 파일을 다운로드할 수 있다. 이 부분은 이 글의 범위에서 약간 벗어나지만, DataDirect 라이브러리를 데이터베이스 연결에 사용하고, 디스크에 있는 XML 문서를 XQuery로 만드는 데 관심이 있다면, 참고자료에 있는 관련 링크를 확인해 보면 된다. 설치 디렉터리 아래의 lib 디렉터리에는 다른 JAR 파일이 많이 있지만, 단순한 파일 쿼리에는 필요가 없다. 나중에라도 DataDirect를 데이터베이스 연결에 사용하면, 그 JAR 파일들을 살펴보면 된다.

자바에서 XQuery 실행하기

지금까지 XPath와 XQuery에 대해 알아보고, 클래스패스에 XQJ를 추가했으니, 자바 코드를 작성하여 쿼리를 실행할 준비가 끝났다. 다음은 모든 프로그램 작성에서 따라야 하는 두 가지 기본 과정이다.

XQuery 데이터 소스를 만들고 접근하기
XQuery 실행하기
둘 다 단순한 과정이고, 다른 XQuery 구현체로 변경하지 않는다면, 첫 번째 과정은 모든 프로그램에서 똑같다. 사실, 데이터 소스 설정과 연결을 다루는 코드는 유틸리티 클래스로 만들면 된다(연습 삼아 해보자).

XQuery 데이터 소스 사용하기

데이터 소스와 데이터베이스

데이터 소스는 DataDirect처럼 데이터베이스 연결성과 함께 정적인 XML 문서에 대한 쿼리와 함께 만드는 벤더에게는 핵심이다. 연결 객체가 일단 만들어지고 나면, 그 연결에 대해 쿼리를 실행할 수 있고, 벤더는 정적인 XML 문서나 다른 여러 가지 관계형 데이터베이스에 대해 그 쿼리를 실행한다. 데이터베이스에 대해 XQuery를 실행하려면, DataDirect의 문서나 사용 중인 데이터베이스 벤더의 문서를 확인할 필요가 있다.