2026년 08월 19일 | DBMS Error 가이드
이 글에서 다루는 내용
2200L 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2200L not an xml document 는?
PostgreSQL 에러 코드 2200L(not an xml document)은 XML 데이터를 처리할 때 입력값이 유효한 XML 문서 형식을 갖추지 못한 경우에 발생합니다. 특히 PostgreSQL의 XML 관련 함수(xmlparse(), xpath(), xmlelement() 등)나 XML 타입 컬럼에 데이터를 삽입하거나 조회할 때 자주 마주치게 됩니다. 이 에러는 단순히 XML 문법 오류뿐 아니라, XML 선언부 누락이나 루트 엘리먼트 부재처럼 XML 문서로서의 구조적 요건을 충족하지 못할 때도 발생하므로, XML 처리 파이프라인 전반에서 주의가 필요합니다.
주요 발생 원인
1. XML 루트 엘리먼트 부재 또는 복수의 루트 엘리먼트 존재
XML 문서는 반드시 단 하나의 루트 엘리먼트를 가져야 한다는 규칙이 있습니다. 루트 엘리먼트가 없거나, 최상위에 두 개 이상의 엘리먼트가 존재하는 경우 PostgreSQL은 해당 입력을 유효한 XML 문서로 인식하지 못하고 2200L 에러를 발생시킵니다. 예를 들어, 처럼 두 개의 최상위 태그가 존재하는 문자열은 XML 프래그먼트(fragment)이지, 완전한 XML 문서가 아닙니다.
2. 잘못된 XML 문법 또는 인코딩 문제
태그가 제대로 닫히지 않거나, 속성값에 따옴표가 누락되거나, 특수 문자(&, <, >)가 이스케이프 처리 없이 사용된 경우 XML 파서가 문서를 해석하지 못합니다. 또한 데이터베이스의 인코딩 설정과 XML 문서의 인코딩 선언이 불일치하는 경우(예: 데이터베이스는 UTF-8이지만 XML은 EUC-KR로 선언된 경우)에도 이 에러가 발생할 수 있습니다. 애플리케이션에서 외부 API나 파일로부터 XML을 수신하여 DB에 저장하는 시나리오에서 특히 빈번하게 나타납니다.
3. XMLPARSE 함수의 DOCUMENT vs CONTENT 옵션 혼용
PostgreSQL의 XMLPARSE() 함수는 DOCUMENT와 CONTENT 두 가지 모드를 지원합니다. DOCUMENT 모드는 완전한 XML 문서(단일 루트 엘리먼트 포함)를 요구하지만, CONTENT 모드는 XML 프래그먼트도 허용합니다. 많은 개발자가 이 차이를 인식하지 못하고 XML 프래그먼트를 DOCUMENT 모드로 파싱하려다 에러를 발생시킵니다. 실무에서는 외부에서 수신한 XML 데이터의 성격에 따라 적절한 모드를 선택해야 합니다.
해결 방법
원인 1 해결: 루트 엘리먼트 래핑
복수의 루트 엘리먼트가 있는 경우, 하나의 루트 엘리먼트로 감싸주면 해결됩니다.
-- 에러 발생 예시: 루트 엘리먼트가 없는 경우
SELECT XMLPARSE(DOCUMENT '<name>홍길동</name><age>30</age>');
-- ERROR: invalid XML document
-- DETAIL: not an xml document
-- 해결: 단일 루트 엘리먼트로 감싸기
SELECT XMLPARSE(DOCUMENT '<person><name>홍길동</name><age>30</age></person>');
-- 또는 CONTENT 모드 활용 (프래그먼트 허용)
SELECT XMLPARSE(CONTENT '<name>홍길동</name><age>30</age>');
-- 기존 XML 컬럼에서 루트 엘리먼트 확인 후 래핑
SELECT
id,
XMLPARSE(DOCUMENT '<root>' || raw_xml_text || '</root>') AS wrapped_xml
FROM staging_xml_data
WHERE raw_xml_text IS NOT NULL;
원인 2 해결: 특수 문자 이스케이프 및 인코딩 정규화
-- 특수 문자가 포함된 잘못된 XML
-- 아래는 에러 발생: & 문자 미이스케이프
-- SELECT XMLPARSE(DOCUMENT '<company>AT&T</company>');
-- 해결: replace()로 특수문자 이스케이프 처리
SELECT XMLPARSE(DOCUMENT
'<company>' ||
REPLACE(REPLACE(REPLACE(company_name, '&', '&'), '<', '<'), '>', '>') ||
'</company>'
)
FROM companies
WHERE id = 1;
-- 인코딩 확인
SHOW server_encoding;
-- XML 문서 삽입 전 유효성 사전 체크 함수 예시
CREATE OR REPLACE FUNCTION is_valid_xml_document(input_text TEXT)
RETURNS BOOLEAN AS $$
BEGIN
PERFORM XMLPARSE(DOCUMENT input_text);
RETURN TRUE;
EXCEPTION
WHEN sqlstate '2200L' THEN
RETURN FALSE;
WHEN others THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
-- 활용 예시
SELECT is_valid_xml_document('<person><name>홍길동</name></person>'); -- TRUE
SELECT is_valid_xml_document('<name>홍길동</name><age>30</age>'); -- FALSE
원인 3 해결: XMLPARSE 모드 적절히 선택
-- 상황에 따른 DOCUMENT vs CONTENT 선택
-- 완전한 XML 문서 처리
SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><root><item>값</item></root>');
-- XML 프래그먼트(부분 XML) 처리 시 CONTENT 사용
SELECT XMLPARSE(CONTENT '<item>첫번째</item><item>두번째</item>');
-- XML 타입 컬럼 삽입 시 주의
CREATE TABLE xml_documents (
id SERIAL PRIMARY KEY,
doc_type VARCHAR(20),
xml_data XML
);
-- DOCUMENT 형식으로 삽입
INSERT INTO xml_documents (doc_type, xml_data)
VALUES (
'full_document',
XMLPARSE(DOCUMENT '<?xml version="1.0"?><catalog><book><title>PostgreSQL 완전정복</title></book></catalog>')
);
-- xpath를 이용한 데이터 추출 (올바른 XML 문서 필요)
SELECT
id,
(xpath('//title/text()', xml_data))[1]::TEXT AS book_title
FROM xml_documents
WHERE doc_type = 'full_document';
-- 문제가 있는 XML을 테이블에서 필터링
SELECT id, xml_data
FROM xml_documents
WHERE is_valid_xml_document(xml_data::TEXT) = TRUE;
예방 방법
1. 입력 데이터 검증 레이어 구축
애플리케이션 계층과 데이터베이스 계층 양쪽에서 XML 유효성 검사를 이중으로 수행하는 것이 가장 확실한 예방책입니다. 위에서 작성한 is_valid_xml_document() 함수를 CHECK 제약조건이나 트리거에 활용하면, 유효하지 않은 XML이 테이블에 삽입되는 상황을 원천적으로 차단할 수 있습니다.
-- CHECK 제약조건으로 유효하지 않은 XML 삽입 방지
ALTER TABLE xml_documents
ADD CONSTRAINT chk_valid_xml
CHECK (is_valid_xml_document(xml_data::TEXT));
-- 또는 트리거를 통한 사전 검증
CREATE OR REPLACE FUNCTION validate_xml_before_insert()
RETURNS TRIGGER AS $$
BEGIN
IF NOT is_valid_xml_document(NEW.xml_data::TEXT) THEN
RAISE EXCEPTION 'Invalid XML document: %', NEW.xml_data::TEXT
USING ERRCODE = '2200L';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_validate_xml
BEFORE INSERT OR UPDATE ON xml_documents
FOR EACH ROW EXECUTE FUNCTION validate_xml_before_insert();
2. 표준화된 XML 생성 함수 사용
애플리케이션에서 임의로 문자열을 조합하여 XML을 생성하는 방식은 구조적 오류를 야기할 가능성이 높습니다. PostgreSQL이 제공하는 XMLELEMENT(), XMLFOREST(), XMLAGG() 등의 내장 XML 생성 함수를 적극 활용하면 문법적으로 올바른 XML을 안전하게 생성할 수 있습니다.
-- 문자열 직접 조합 (위험한 방식)
-- SELECT '<user><name>' || user_name || '</name></user>' ...
-- PostgreSQL 내장 XML 함수 활용 (권장)
SELECT XMLELEMENT(
NAME "users",
XMLAGG(
XMLELEMENT(
NAME "user",
XMLFOREST(id AS "id", username AS "name", email AS "email")
)
)
)
FROM users
WHERE active = TRUE;
관련 에러
- 2200M (
invalid XML content): XML 콘텐츠 자체의 구조는 있으나 내용이 XML 규격에 맞지 않을 때 발생합니다. - 2200N (
invalid XML comment): XML 주석 형식이 올바르지 않을 때 발생합니다. (--로 끝나는 주석 등) - 2200S (
invalid XML processing instruction): XML 처리 명령(Processing Instruction)의 형식이 잘못된 경우입니다. - 22000 (
data exception): 데이터 예외의 상위 카테고리 에러로, 2200L은 이 카테고리에 속합니다. - 42804 (
datatype mismatch): XML 타입과 다른 타입 간의 암묵적 변환이 불가능할 때 발생하며, XML 처리 시 간접적으로 연관될 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.