2026년 06월 15일 | DBMS Error 가이드
이 글에서 다루는 내용
2200N 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2200N invalid xml content 는?
PostgreSQL 에러 코드 2200N: invalid xml content는 XML 타입 컬럼이나 XML 관련 함수에 유효하지 않은 XML 데이터를 삽입하거나 처리하려 할 때 발생하는 오류입니다. PostgreSQL은 XML 데이터의 구조적 유효성을 엄격하게 검사하며, W3C XML 표준을 준수하지 않는 데이터는 모두 이 에러를 발생시킵니다. 주로 외부 시스템에서 받아온 XML 문자열을 직접 DB에 저장하거나, 애플리케이션에서 XML을 동적으로 생성하는 과정에서 자주 발생합니다.
주요 발생 원인
- 잘못된 XML 구조 또는 태그 불일치
XML은 모든 여는 태그에 반드시 대응하는 닫는 태그가 있어야 하며, 태그가 올바르게 중첩되어야 합니다. 실무에서는 외부 API로부터 수신한 XML 데이터나 사용자 입력값을 그대로 사용할 때 태그가 닫히지 않거나 순서가 잘못 중첩되어 이 에러가 발생하는 경우가 많습니다. 예를 들어 처럼 중첩 순서가 뒤바뀐 경우가 대표적입니다.
- 잘못된 특수 문자 또는 인코딩 문제
XML에서는 &, <, >, ", ' 같은 특수 문자를 그대로 사용할 수 없으며, 반드시 XML 엔티티(&, <, >, ", ')로 이스케이프 처리해야 합니다. 또한 UTF-8이 아닌 인코딩이 섞이거나 BOM(Byte Order Mark)이 포함된 경우, XML 선언부의 인코딩 설정과 실제 데이터 인코딩이 다를 경우에도 이 에러가 발생합니다.
- 여러 루트 엘리먼트 존재 또는 루트 엘리먼트 부재
XML 문서는 반드시 단 하나의 루트 엘리먼트만 가져야 합니다. 여러 개의 최상위 엘리먼트가 존재하거나, 루트 엘리먼트 없이 텍스트만 있는 경우, 또는 XML 선언() 이후에 루트 엘리먼트가 없는 경우에도 이 에러가 발생합니다. 이는 여러 XML 조각을 단순 문자열 연결로 합칠 때 자주 발생합니다.
해결 방법
원인 1: 잘못된 XML 구조 수정
먼저 문제가 되는 XML 문자열을 XMLPARSE 함수로 테스트하여 구조를 확인합니다.
-- 잘못된 XML 삽입 시도 (에러 발생)
INSERT INTO orders (order_id, xml_data)
VALUES (1, XMLPARSE(DOCUMENT '<order><item>사과</order></item>'));
-- ERROR: invalid xml content
-- 올바른 중첩 구조로 수정
INSERT INTO orders (order_id, xml_data)
VALUES (1, XMLPARSE(DOCUMENT '<order><item>사과</item></order>'));
-- 기존 데이터 중 유효한 XML인지 확인하는 방법 (함수 작성)
CREATE OR REPLACE FUNCTION is_valid_xml(p_text TEXT)
RETURNS BOOLEAN AS $$
BEGIN
PERFORM XMLPARSE(DOCUMENT p_text);
RETURN TRUE;
EXCEPTION
WHEN invalid_xml_content THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
-- 활용 예시
SELECT id, raw_xml
FROM staging_xml_data
WHERE is_valid_xml(raw_xml) = FALSE;
원인 2: 특수 문자 이스케이프 처리
-- 특수 문자가 포함된 경우 (에러 발생)
INSERT INTO products (prod_id, xml_info)
VALUES (1, XMLPARSE(DOCUMENT '<product><name>AT&T</name></product>'));
-- ERROR: invalid xml content
-- xmlescape를 활용한 올바른 처리
-- PostgreSQL에서는 xmlelement 함수를 사용하면 자동으로 이스케이프됩니다
SELECT xmlelement(name product,
xmlelement(name name, 'AT&T'),
xmlelement(name price, 100)
);
-- 결과: <product><name>AT&T</name><price>100</price></product>
-- 문자열에서 직접 이스케이프 처리가 필요한 경우
SELECT XMLPARSE(DOCUMENT
replace(
replace(
replace(raw_text, '&', '&'),
'<', '<'),
'>', '>')
)
FROM staging_table
WHERE id = 1;
-- 인코딩 선언 제거 후 재시도 (BOM 문제 해결)
SELECT XMLPARSE(DOCUMENT
regexp_replace(raw_xml, '^\s*<\?xml[^?]*\?>\s*', '', 'i')
)
FROM staging_table;
원인 3: 단일 루트 엘리먼트로 감싸기
-- 여러 루트가 있는 경우 (에러 발생)
SELECT XMLPARSE(DOCUMENT '<item>A</item><item>B</item>');
-- ERROR: invalid xml content
-- 단일 루트로 감싸서 해결
SELECT XMLPARSE(DOCUMENT '<root><item>A</item><item>B</item></root>');
-- 여러 XML 조각을 안전하게 합치는 방법
WITH xml_fragments AS (
SELECT '<item>A</item>' AS frag
UNION ALL
SELECT '<item>B</item>'
)
SELECT XMLPARSE(DOCUMENT
'<root>' || string_agg(frag, '') || '</root>'
)
FROM xml_fragments;
-- XML 타입 컬럼에서 문제 데이터 업데이트
UPDATE product_catalog
SET xml_description = XMLPARSE(DOCUMENT
'<description>' || raw_text || '</description>'
)
WHERE xml_description IS NULL
AND raw_text IS NOT NULL
AND is_valid_xml('<description>' || raw_text || '</description>') = TRUE;
예방 방법
- 애플리케이션 레벨에서 XML 유효성 검증 후 DB 저장
DB에 XML 데이터를 저장하기 전에 반드시 애플리케이션 단에서 XML 파서를 통한 유효성 검증을 수행하고, 유효한 데이터만 INSERT/UPDATE 하도록 합니다. 또한 스테이징 테이블(TEXT 타입)에 먼저 적재한 후 위에서 작성한 is_valid_xml() 함수로 배치 검증을 거쳐 XML 타입 컬럼으로 이관하는 2단계 파이프라인을 구축하면 운영 테이블에 유효하지 않은 XML이 유입되는 것을 원천 차단할 수 있습니다.
```sql
-- 스테이징에서 운영 테이블로 안전하게 이관
INSERT INTO production_xml_table (id, xml_data, created_at)
SELECT id, XMLPARSE(DOCUMENT raw_xml), NOW()
FROM staging_xml_table
WHERE is_valid_xml(raw_xml) = TRUE
AND processed = FALSE;
-- 실패 건은 에러 로그 테이블로 이동
INSERT INTO xml_error_log (id, raw_xml, error_time)
SELECT id, raw_xml, NOW()
FROM staging_xml_table
WHERE is_valid_xml(raw_xml) = FALSE
AND processed = FALSE;
```
- XML 생성 시 문자열 연결 대신 PostgreSQL 내장 XML 함수 사용
XML 데이터를 동적으로 생성할 때 문자열 연결(||) 방식을 지양하고, xmlelement(), xmlforest(), xmlagg(), xmlcomment() 등 PostgreSQL 내장 XML 함수를 사용합니다. 이 함수들은 특수 문자를 자동으로 이스케이프하고 올바른 XML 구조를 보장하므로 2200N 에러 발생 가능성을 크게 줄여줍니다.
```sql
-- 권장: 내장 함수 사용 (특수문자 자동 이스케이프)
SELECT xmlelement(name order,
xmlattributes(o.order_id AS id, o.order_date AS date),
xmlforest(
c.customer_name AS customer,
o.total_amount AS total
)
)
FROM orders o
JOIN customers c ON o.customer_id = c.id;
```
관련 에러
2200M(invalid XML document): XML 문서 선언은 올바르지만 내용이 유효하지 않을 때 발생하며,2200N과 혼동될 수 있습니다.22000(data exception): XML 관련 에러의 상위 카테고리 에러 코드입니다.42804(datatype mismatch):TEXT타입 데이터를XML타입 컬럼에 명시적 캐스팅 없이 삽입할 때 발생합니다.22021(character not in repertoire): XML 처리 중 지원되지 않는 문자 인코딩이 발견될 때 함께 발생할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.