XQuery, XML 문서에 조건과 정렬을 거는 법
XML 데이터 모델(XDM) 위에서 FLWOR 표현식으로 질의·변환하는 XQuery의 구조, BaseX/Saxon 실행 예시, 문서지향 DB·B2B 변환 활용법을 정리한다.
2026-08-13 · 최초 발행 2025-12-03
조건을 걸 수 있는 XML
XML 문서를 다루다 보면 결국 관계형 데이터베이스에서 SQL로 하던 일과 똑같은 요구가 생긴다 — 특정 조건의 항목만 골라내고, 정렬하고, 다른 형태로 바꿔 내보내는 일이다. XQuery는 이 요구를 XML 데이터 모델(XDM) 위에서 해결하는 W3C 표준 선언적 질의·변환 언어다. XPath를 상위 집합으로 포함하며, FLWOR(For-Let-Where-Order-Return) 표현식으로 데이터 흐름을 기술한다. 표준 스택은 XPath 3.1, Functions & Operators 3.1, Serialization 3.1, XQuery 3.1, Update Facility로 구성되고, XQuery 3.1에서 배열·맵 타입이 도입돼 JSON 상호운용까지 지원 범위가 넓어졌다.
질의는 정적 컨텍스트(네임스페이스, 변수·함수 시그니처, 옵션)와 동적 컨텍스트(변수 바인딩, 현재 노드, 문서 순서)로 나뉘어 실행된다. 이 분리 덕분에 구현체는 인덱스 활용, 스트리밍, 조기 소거 같은 최적화를 정적 단계에서 미리 적용할 수 있다.
XDM: 트리와 시퀀스로 이루어진 세계
XQuery가 다루는 데이터 모델은 트리형 노드와 원자값이 순서화된 시퀀스로 표현된다. 문서 순서, 안정적 비교 규칙, 불변 시퀀스 의미론을 보장해 같은 질의를 반복 실행해도 결과가 흔들리지 않는다. 스키마 인식 모드에서는 XML Schema 타입을 활용한 정적 타입 검사가 가능하고, 이는 런타임 동적 타입 검사와 병행 운용된다.
FLWOR, SQL과 닮았지만 함수형이다
FLWOR는 필터링·정렬·그룹핑·투영을 선언적으로 기술하는 핵심 구문이다. 조인·집계 작업을 SQL과 유사한 문법으로 표현할 수 있으면서도, 1급 함수와 고차 함수를 지원하는 함수형 패러다임 위에 서 있다. 재귀와 함수 모듈화로 재사용성과 테스트 용이성을 확보하는 것도 이 함수형 성격 덕분이다.
라이브러리 모듈은 네임스페이스 기반으로 함수를 배포하는 단위이며, EXPath·EXQuery 같은 생태계 확장 모듈을 얹을 수 있다. 구현체마다 인덱스 힌트, 파일·HTTP 모듈 같은 확장 함수를 제공하는데, 이런 확장을 쓸수록 이식성과 성능 사이의 트레이드오프를 감수해야 한다.
업데이트와 직렬화까지 한 언어 안에서
XQuery Update Facility는 노드 삽입·삭제·대체·재명명을 지원한다. 트랜잭션 수준의 원자성은 표준이 아니라 각 데이터베이스 구현(BaseX·eXist·MarkLogic)에 달려 있어, 동시 업데이트 시 잠금·충돌 정책을 구현체별로 확인해야 한다. Serialization 3.1은 XML·HTML·TEXT·JSON 출력을 제어하며, JSON과 XDM 사이의 매핑을 통해 하이브리드 처리가 가능해졌다.
정적 분석이 실행 계획을 짠다
정적 분석 단계에서 상수 전개, 서브식 공통화, 조기 실패 검출이 이뤄진다. 동적 단계에서는 인덱스 기반 탐색과 스트리밍 파이프라인이 적용된다. 대용량 입력을 다룰 때는 메모리 상주를 최소화하는 전략이 필요하고, 문서 순서와 안정 정렬을 보장하려면 그만큼의 비용도 감수해야 한다.
어디에 쓰이는가
문서지향 데이터베이스(BaseX, eXist-db, MarkLogic)에서는 컬렉션을 로드하고 경로·텍스트 인덱스를 만든 뒤 FLWOR 기반 필터·정렬·하이라이팅을 거쳐 페이징 직렬화까지 이어지는 질의·검색 흐름을 담당한다. B2B 메시지 변환·검증에서는 XBRL·UBL·HL7/FHIR 같은 표준 메시지를 수신해 파싱하고 스키마 검증을 거친 뒤 비즈니스 규칙 함수를 적용해 JSON·CSV로 재직렬화한다. 로그·구성 관리와 리포팅 자동화에서는 XML 기반 설정과 JUnit XML 테스트 리포트, 커버리지 리포트를 여러 파일에 걸쳐 집계하고 에러율·소요시간을 그룹화해 HTML·Markdown 보고서로 만든다. 데이터 통합·ETL 파이프라인에서는 XML API나 파일 드롭, RDB에서 뽑은 SQL/XML을 정규화하고 엔티티를 매핑·조인한 뒤 중복을 제거해 타깃에 증분 로딩한다. RESTXQ를 얹으면 eXist-db·BaseX 위에서 경로-함수 데코레이터로 XQuery 로직을 바로 REST API 백엔드로 노출할 수 있다.
질의가 흐르는 경로
실제로 돌려보기
전제조건은 Java 11 이상, Saxon HE 12.x 또는 BaseX 10.x 이상, UTF-8 환경이다. 테스트 데이터는 books.xml 파일과 컬렉션 디렉터리를 쓴다.
입력 books.xml:
<books>
<book id="b1"><title>XQuery Basics</title><price>29.9</price><year>2021</year></book>
<book id="b2"><title>Advanced XML</title><price>49.0</price><year>2019</year></book>
</books>
기본 FLWOR 질의와 정렬은 이렇게 쓴다.
let $doc := doc('books.xml')/books/book
for $b in $doc
where xs:decimal($b/price) < 40
order by xs:int($b/year) descending
return <item id="{ $b/@id }">{ $b/title/text() }</item>
BaseX에서는 다음처럼 실행한다.
basex -q query.xq
결과는 아래와 같다.
<item id="b1">XQuery Basics</item>
XML을 JSON으로 바꿔 직렬화하려면 배열·맵 타입을 쓴다.
declare variable $src := doc('books.xml')/books/book;
let $arr :=
array {
for $b in $src
return map {
'id': string($b/@id),
'title': string($b/title),
'price': xs:decimal($b/price)
}
}
return serialize($arr, map { 'method': 'json', 'indent': true() })
예상 출력:
[
{ "id": "b1", "title": "XQuery Basics", "price": 29.9 },
{ "id": "b2", "title": "Advanced XML", "price": 49.0 }
]
Update Facility로 값을 바꾸고 노드를 삽입하면서 오류를 잡는 예시는 다음과 같다.
declare namespace err = "http://www.w3.org/2005/xqt-errors";
try {
let $b := doc('books.xml')/books/book[@id = 'b2']
return (
replace value of node $b/price with 45.0,
insert node <tag>new</tag> into $b
)
}
catch * {
(: 오류 로깅 :)
<error code="{ $err:code }">{ $err:description }</error>
}
트랜잭션 격리는 BaseX·eXist·MarkLogic마다 차이가 있으므로, 동시 업데이트가 일어나는 환경이라면 잠금·충돌 정책을 반드시 구현체 문서에서 확인해야 한다.
인접 기술과 나란히 놓아보면
| 기술 | 성능 | 확장성 | 일관성 | 안정성 | 운영 편의 | 주사용 목적 |
|---|---|---|---|---|---|---|
| XQuery | 중~고, 인덱스 최적화 시 고 | 수평 확장 DB(분산 인덱스) 의존 | 문서 순서·타입 일관 보장 | 표준 기반, 구현체에 의존 | 모듈·REPL·서버 통합 용이 | XML 질의·변환·API |
| XPath | 고(경로 탐색 한정) | 라이브러리 내장 수준 | 표현 범위 제한 | 광범위 지원 | 간단 | 노드 탐색·필터 |
| XSLT | 중, 템플릿 매칭 비용 | 배치 처리 적합 | 템플릿 우선순위 규칙 | 성숙 | 디자이너 친화 | XML→XML/HTML 변환 |
| SQL/XML | RDB 엔진 수준 | RDB 확장성 상속 | 트랜잭션 강함 | 매우 높음 | DBA 친화 | RDB와 XML 혼합 질의 |
도입할 때 따져야 할 것들
인덱스는 경로·텍스트·속성 인덱스를 활성화하고 선택도 높은 조건을 좌측에 배치하는 게 기본이다. 대용량 컬렉션은 분할·샤딩까지 고려해야 하지만, 그만큼 인덱스 구축·유지 비용과 업데이트 쓰기 성능 저하를 감수해야 한다. 스트리밍·메모리 관리는 saxon:stream()이나 직렬 파이프를 활용하고 materialize()를 최소화하는 방향인데, 이는 표현식 제약 증가와 디버깅 난이도 상승이라는 대가를 동반한다. 타입 사용 전략은 스키마 인식으로 정적 오류를 조기에 잡되 스키마 관리 비용과 유연성 저하를 함께 짊어져야 하고, 이식성이냐 벤더 기능이냐의 선택은 표준 우선 설계에 경계 어댑터에서만 벤더 확장(파일·HTTP·DB)을 쓰는 절충으로 풀리는 경우가 많다. 라이브러리 모듈과 순수 함수 중심의 모듈화, 샘플 데이터 기반 유닛 테스트는 초기 설계 비용을 늘리는 대신 유지보수성을 끌어올린다.
도입 절차는 입력 포맷(XML/JSON/CSV)과 처리량, 일관성·지연 요구사항, 트랜잭션·보안·감사 요건을 정의하는 요구 분석에서 시작해 임베디드(Saxon)냐 서버형 데이터베이스(BaseX·eXist·MarkLogic)냐를 가르는 아키텍처·제품 선정으로 이어진다. 이후 문서 경로 패턴을 식별해 컬렉션·네임스페이스와 인덱스·전처리 파이프라인을 설계하고, 모듈·패키지 구조와 공통 함수 라이브러리를 구현하며 대표 쿼리 기준 p95 지연·메모리 피크를 벤치마킹하고 회귀 테스트를 거친다. 운영 단계에서는 캐시·풀 크기와 동시성·잠금 정책을 튜닝하고 쿼리 계획·인덱스 히트율·GC를 모니터링하며 장애 대응 플레이북을 마련한다. 마지막으로 입력 검증과 스키마 검증, 외부 자원 접근 제한, 코드 리뷰·아티팩트 서명·감사 로그 관리로 보안·거버넌스를 갖춘다. 제품별 XQuery 3.1 지원 범위와 Update Facility·JSON 직렬화 옵션 차이는 여전히 존재하므로 최신 정보 확인이 필요하다.
선언적 질의·변환으로 스크립트 대비 개발·유지 시간을 3050% 절감할 수 있고, 인덱스를 제대로 적용하면 대량 문서 검색의 p95 지연을 40% 이상 단축한 사례도 있다(환경 의존). 스키마·타입·함수 재사용은 변환 오류율을 낮추고 회귀 테스트를 쉽게 만들며, ETL·템플릿 코드 축소로 운영 스크립트 규모를 2040% 줄이는 비용 효과도 보고된다.
표준 하나로 질의·변환·API를 묶는다
XQuery는 문서지향 데이터베이스와 통합 파이프라인에서 표현력과 최적화 이점을 함께 제공하는 XML 표준 질의·변환 언어다. 인덱스·스트리밍·타입 전략을 병행해 성능·안정성을 확보하고, 모듈화로 이식성과 유지보수성을 챙기는 게 실무 적용의 핵심이다. 제품별 기능 차이가 남아 있는 만큼, 벤더 중립 설계와 운영 자동화 체계를 함께 갖추는 편이 안전하다.