2026년 06월 15일 | DBMS Error 가이드
이 글에서 다루는 내용
2200M 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2200M invalid xml document 는?
PostgreSQL 에러 코드 2200M: invalid xml document는 XML 데이터를 처리하는 과정에서 입력된 XML 문서가 W3C XML 표준 규격을 준수하지 않을 때 발생하는 에러입니다. 특히 XMLPARSE 함수나 XML 타입 컬럼에 데이터를 삽입하거나, xml 타입으로 캐스팅할 때 문서 구조 자체가 올바르지 않은 경우 이 에러가 트리거됩니다. “invalid xml content”(에러코드 2200N)와는 달리, 2200M은 루트 엘리먼트가 없거나 복수의 루트 엘리먼트가 존재하는 등 문서(document) 레벨의 구조적 오류를 의미합니다.
주요 발생 원인
- 루트 엘리먼트 누락 또는 복수 루트 엘리먼트 존재
XML 문서는 반드시 단 하나의 루트 엘리먼트를 가져야 합니다. 루트 엘리먼트 없이 텍스트만 존재하거나, 처럼 최상위 엘리먼트가 두 개 이상 존재하는 경우 PostgreSQL은 이를 유효한 XML 문서로 인정하지 않습니다. 이는 실무에서 문자열을 단순 접합(concatenation)하여 XML을 생성하거나, 외부 시스템에서 전송된 데이터를 검증 없이 저장할 때 매우 자주 발생합니다.
- XML 선언(Prolog)의 잘못된 사용 또는 인코딩 불일치
형태의 XML 선언이 문서 첫 번째 줄이 아닌 위치에 등장하거나, 실제 바이트 인코딩과 선언된 인코딩이 불일치할 경우 에러가 발생합니다. 예를 들어, EUC-KR로 인코딩된 데이터를 encoding="UTF-8"로 선언하면 파서가 문서를 올바르게 해석하지 못합니다. 외부 API 연동이나 레거시 시스템 데이터 마이그레이션 작업에서 특히 주의해야 하는 원인입니다.
- 태그 미닫힘(Unclosed Tag) 또는 잘못된 특수문자 사용
XML에서 <, >, & 등의 특수문자는 반드시 엔티티(<, >, &)로 이스케이프되어야 합니다. 태그가 제대로 닫히지 않거나(처럼 닫는 태그 누락), 속성값 내에 이스케이프 처리 없이 특수문자가 포함되면 XML 파서가 이를 비정형(malformed) 문서로 판단합니다. 애플리케이션 레이어에서 동적으로 XML을 생성할 때 문자열 조작 방식을 사용하면 이런 문제가 매우 빈번하게 발생합니다.
해결 방법
원인 1 해결: 루트 엘리먼트 확인 및 교정
XMLPARSE를 사용하기 전에 구조를 점검하고, 필요 시 래퍼 엘리먼트를 추가합니다.
-- 잘못된 예: 복수 루트 엘리먼트
SELECT XMLPARSE(DOCUMENT '<item>A</item><item>B</item>');
-- ERROR: invalid xml document
-- 올바른 예: 단일 루트 엘리먼트로 감싸기
SELECT XMLPARSE(DOCUMENT '<root><item>A</item><item>B</item></root>');
-- 테이블에 삽입 시에도 동일하게 적용
INSERT INTO xml_data (content)
VALUES (XMLPARSE(DOCUMENT '<root><item>A</item><item>B</item></root>'));
-- 기존 잘못된 데이터를 수정하는 예
UPDATE xml_data
SET content = XMLPARSE(DOCUMENT '<root>' || content::text || '</root>')
WHERE content IS NOT NULL;
원인 2 해결: 인코딩 선언 제거 또는 통일
PostgreSQL 내부에서는 encoding 선언이 실제 데이터베이스 인코딩과 다를 경우 에러가 발생합니다. 가장 안전한 방법은 선언을 제거하거나 DB 인코딩과 일치시키는 것입니다.
-- DB 인코딩 확인
SHOW server_encoding;
-- 잘못된 예: DB가 UTF8인데 다른 인코딩 선언
SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="EUC-KR"?><root>테스트</root>');
-- 인코딩 불일치 시 에러 발생 가능
-- 올바른 예: 선언 없이 사용하거나 UTF-8로 통일
SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><root>테스트</root>');
-- 혹은 XML 선언 자체를 제거
SELECT XMLPARSE(DOCUMENT '<root>테스트</root>');
-- 함수로 인코딩 선언 제거 처리
CREATE OR REPLACE FUNCTION clean_xml_declaration(p_xml TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN regexp_replace(p_xml, '^\s*<\?xml[^?]*\?>\s*', '', 'i');
END;
$$ LANGUAGE plpgsql;
SELECT XMLPARSE(DOCUMENT clean_xml_declaration(
'<?xml version="1.0" encoding="EUC-KR"?><root>데이터</root>'
));
원인 3 해결: 특수문자 이스케이프 처리
-- 잘못된 예: 특수문자 미이스케이프
SELECT XMLPARSE(DOCUMENT '<root><item>가격: 1 & 2</item></root>');
-- ERROR: invalid xml document
-- 올바른 예: 엔티티로 이스케이프
SELECT XMLPARSE(DOCUMENT '<root><item>가격: 1 & 2</item></root>');
-- PostgreSQL 내장 함수로 안전하게 XML 생성
SELECT XMLELEMENT(
NAME "root",
XMLELEMENT(NAME "item", '가격: 1 & 2')
);
-- 동적 XML 생성 시 xmlelement 활용 (특수문자 자동 이스케이프)
SELECT XMLELEMENT(
NAME "products",
XMLAGG(
XMLELEMENT(NAME "product",
XMLATTRIBUTES(id AS "id"),
name
)
)
)
FROM products;
-- 태그 닫힘 검증을 위한 간단한 유효성 검사 함수
CREATE OR REPLACE FUNCTION is_valid_xml_document(p_text TEXT)
RETURNS BOOLEAN AS $$
BEGIN
PERFORM XMLPARSE(DOCUMENT p_text);
RETURN TRUE;
EXCEPTION
WHEN invalid_xml_document THEN
RETURN FALSE;
WHEN invalid_xml_content THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
-- 사용 예
SELECT is_valid_xml_document('<root><item>정상</item></root>'); -- TRUE
SELECT is_valid_xml_document('<root><item>비정상</root>'); -- FALSE
예방 방법
- 애플리케이션 레이어에서 XML을 직접 문자열로 생성하지 말고, PostgreSQL 내장 XML 함수(
XMLELEMENT,XMLFOREST,XMLAGG)를 활용하세요.
XMLELEMENT와 같은 내장 함수는 특수문자 이스케이프와 구조 검증을 자동으로 처리해주기 때문에 수동으로 문자열을 조합하는 것보다 훨씬 안전합니다. 삽입 전 체크 제약(CHECK constraint)을 걸어두는 것도 좋은 방법입니다.
```sql
-- XML 컬럼에 유효성 검사 제약 추가
ALTER TABLE xml_data
ADD CONSTRAINT chk_valid_xml
CHECK (is_valid_xml_document(content::text));
```
- 외부 시스템에서 수신한 XML 데이터는 저장 전 반드시 검증 레이어를 거치도록 파이프라인을 설계하세요.
ETL 파이프라인이나 API 수신 처리 로직에 is_valid_xml_document() 같은 유효성 검사 함수를 포함시켜, 비정형 데이터가 데이터베이스에 유입되는 것을 사전에 차단해야 합니다. 또한 PostgreSQL의 xml 타입을 사용하면 text 타입 대비 삽입 시 자동으로 파싱 검증이 이루어지므로, 가능하면 xml 네이티브 타입 사용을 권장합니다.
관련 에러
2200N: invalid xml content—2200M과 유사하지만,XMLPARSE(CONTENT ...)를 사용할 때 발생합니다. CONTENT 모드는 루트 엘리먼트가 없어도 허용되나 그 외 구조적 오류 시 발생합니다.22000: data exception— XML 관련 일반 데이터 예외의 상위 카테고리 에러입니다.42804: datatype mismatch— XML이 아닌 타입을 xml 타입 컬럼에 삽입할 때 발생할 수 있습니다.39000: external routine exception— PL/Python 등 외부 언어에서 XML 처리 중 발생하는 연관 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.