PostgreSQL 2200M 오류 원인과 해결 방법 완벽 가이드

2200M
2026년 08월 19일 | DBMS Error 가이드

이 글에서 다루는 내용

2200M 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

2200M invalid xml document 는?

PostgreSQL 에러 코드 2200M (invalid xml document)은 XML 데이터를 처리할 때 입력된 문자열이 유효한 XML 문서 형식을 만족하지 못할 경우 발생합니다. 이 에러는 주로 XMLPARSE, xml 타입 캐스팅, 또는 XML 관련 함수(xpath, xmlelement, xmlforest 등)를 사용할 때 나타납니다. PostgreSQL은 XML 표준(W3C XML 1.0)을 엄격하게 준수하므로, 단 하나의 태그 누락이나 인코딩 문제만으로도 이 에러가 트리거됩니다.


주요 발생 원인

1. XML 구조적 오류 (태그 불일치, 루트 요소 누락)

XML 문서는 반드시 하나의 루트 요소를 가져야 하며, 모든 열린 태그는 반드시 닫혀야 합니다. 홍길동 처럼 닫는 꺽쇠(>)가 빠지거나, 처럼 태그 순서가 맞지 않으면 PostgreSQL XML 파서가 즉시 에러를 반환합니다. 실무에서는 애플리케이션에서 동적으로 XML을 생성하다가 문자열 연결 오류로 인해 이런 구조적 문제가 빈번하게 발생합니다.

2. 잘못된 문자 인코딩 또는 특수문자 미이스케이프

XML에서 &, <, >, ", ' 같은 특수문자는 반드시 엔티티로 이스케이프(&, <, >, ", ')해야 합니다. 예를 들어 홍길동 & 이순신 처럼 &를 그대로 사용하면 XML 파서는 이를 엔티티 참조의 시작으로 인식하고 파싱에 실패합니다. 또한 UTF-8이 아닌 EUC-KR 등 다른 인코딩으로 인코딩된 문자열을 XML로 처리하려 할 때도 이 에러가 발생할 수 있습니다.

3. XML 선언부(Prolog) 불일치 또는 잘못된 DTD/네임스페이스

와 같은 XML 선언부에서 선언된 인코딩이 실제 데이터의 인코딩과 다를 경우 파싱 오류가 발생합니다. 또한 네임스페이스 접두사를 선언 없이 사용하거나( 에서 ns가 정의되지 않은 경우), 잘못된 DTD 참조가 포함된 경우에도 PostgreSQL XML 파서는 2200M 에러를 반환합니다.


해결 방법

원인 1: XML 구조적 오류 해결

먼저 문제가 되는 XML 문자열을 확인하고, XMLPARSE 함수를 사용해 디버깅합니다.

-- 에러 발생 예시: 루트 요소가 없는 경우
SELECT XMLPARSE(DOCUMENT '<name>홍길동</name><age>30</age>');
-- ERROR: invalid xml document
-- DETAIL: line 1: Extra content at the end of the document

-- 해결: 루트 요소로 감싸기
SELECT XMLPARSE(DOCUMENT '<person><name>홍길동</name><age>30</age></person>');

-- 에러 발생 예시: 태그가 닫히지 않은 경우
SELECT XMLPARSE(DOCUMENT '<person><name>홍길동</person>');
-- ERROR: invalid xml document

-- 해결: 올바른 태그 닫기
SELECT XMLPARSE(DOCUMENT '<person><name>홍길동</name></person>');

-- CONTENT 모드 사용 (루트 요소 없이도 허용)
SELECT XMLPARSE(CONTENT '<name>홍길동</name><age>30</age>');
-- 이 경우는 성공 (CONTENT는 여러 루트 노드 허용)

원인 2: 특수문자 이스케이프 처리

-- 에러 발생: & 특수문자 미처리
SELECT XMLPARSE(DOCUMENT '<company>홍길동 & 이순신 상회</company>');
-- ERROR: invalid xml document

-- 해결 방법 1: xmlelement 함수 사용 (자동 이스케이프)
SELECT xmlelement(name company, '홍길동 & 이순신 상회');
-- 결과: <company>홍길동 &amp; 이순신 상회</company>

-- 해결 방법 2: replace 함수로 수동 이스케이프
SELECT XMLPARSE(DOCUMENT 
    '<company>' || 
    replace(replace(replace('홍길동 & 이순신 상회', '&', '&amp;'), '<', '&lt;'), '>', '&gt;') || 
    '</company>'
);

-- 해결 방법 3: 테이블 데이터를 XML로 변환할 때 안전하게 처리
SELECT xmlelement(
    name employee,
    xmlelement(name name, emp_name),
    xmlelement(name department, dept_name)
)
FROM employees
WHERE emp_id = 1001;

-- 실무에서 자주 쓰는 패턴: 테이블 전체를 XML로 변환
SELECT table_to_xml('employees', true, false, '');

원인 3: XML 선언부 및 인코딩 문제 해결

-- 에러 발생: 인코딩 선언 불일치
SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="EUC-KR"?><name>테스트</name>');
-- ERROR: invalid xml document (인코딩 불일치)

-- 해결: UTF-8 선언 사용 또는 선언 제거
SELECT XMLPARSE(DOCUMENT '<?xml version="1.0" encoding="UTF-8"?><name>테스트</name>');

-- 선언부 없이도 동작 (PostgreSQL은 기본 UTF-8 사용)
SELECT XMLPARSE(DOCUMENT '<name>테스트</name>');

-- 네임스페이스 문제 해결
-- 에러: 선언되지 않은 네임스페이스 접두사 사용
SELECT XMLPARSE(DOCUMENT '<ns:person><ns:name>홍길동</ns:name></ns:person>');
-- ERROR: invalid xml document

-- 해결: 네임스페이스 선언 포함
SELECT XMLPARSE(DOCUMENT '
<ns:person xmlns:ns="http://example.com/schema">
    <ns:name>홍길동</ns:name>
</ns:person>
');

-- XML 유효성 검사를 위한 사전 확인 함수 만들기
CREATE OR REPLACE FUNCTION is_valid_xml(p_xml TEXT)
RETURNS BOOLEAN AS $$
BEGIN
    PERFORM XMLPARSE(DOCUMENT p_xml);
    RETURN TRUE;
EXCEPTION
    WHEN invalid_xml_document THEN
        RETURN FALSE;
    WHEN others THEN
        RETURN FALSE;
END;
$$ LANGUAGE plpgsql;

-- 사용 예시
SELECT is_valid_xml('<person><name>홍길동</name></person>');  -- true
SELECT is_valid_xml('<person><name>홍길동</person>');          -- false

-- 배치 처리 중 유효하지 않은 XML 행 찾기
SELECT id, raw_xml_column
FROM import_staging
WHERE NOT is_valid_xml(raw_xml_column);

저장된 XML 데이터 마이그레이션 시 안전 처리

-- XML 컬럼 업데이트 시 유효성 검사 후 처리
UPDATE documents
SET xml_content = XMLPARSE(DOCUMENT new_xml_text)
WHERE is_valid_xml(new_xml_text) = TRUE;

-- 실패한 행 별도 로깅
INSERT INTO xml_error_log (source_id, raw_text, error_time)
SELECT id, raw_xml_column, NOW()
FROM import_staging
WHERE NOT is_valid_xml(raw_xml_column);

예방 방법

1. 애플리케이션 레벨에서 XML 생성 시 전용 라이브러리 사용

절대로 문자열 연결(concatenation)로 XML을 직접 생성하지 마세요. Java의 JAXB, Python의 lxml, ElementTree, Node.js의 xml-builder 같은 검증된 XML 라이브러리를 사용하면 특수문자 이스케이프와 구조적 유효성이 자동으로 보장됩니다. PostgreSQL 내부에서 XML을 생성해야 한다면 문자열 직접 조합 대신 반드시 xmlelement(), xmlforest(), xmlagg() 같은 내장 XML 함수를 사용하세요.

2. 데이터 입력 시점에 CHECK 제약 조건 또는 트리거로 사전 차단

-- XML 컬럼에 CHECK 제약 조건 추가
CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    title VARCHAR(200),
    xml_content XML  -- xml 타입 자체가 유효성 검사를 수행
);

-- 또는 TEXT로 저장하는 경우 CHECK 제약 추가
CREATE TABLE documents_text (
    id SERIAL PRIMARY KEY,
    raw_xml TEXT,
    CONSTRAINT chk_valid_xml CHECK (is_valid_xml(raw_xml))
);

-- INSERT 시점에 자동으로 검증됨
INSERT INTO documents_text (raw_xml) VALUES ('<invalid>');
-- ERROR: new row for relation "documents_text" violates check constraint "chk_valid_xml"

관련 에러

  • 2200N (invalid xml content): XMLPARSE(CONTENT ...) 사용 시 content 형식이 유효하지 않을 때 발생합니다. 2200M과 유사하지만 DOCUMENT 모드가 아닌 CONTENT 모드에서 나타납니다.
  • 22000 (data exception): XML 처리 중 일반적인 데이터 예외 상황에서 발생하는 상위 에러 클래스입니다.
  • 42804 (datatype mismatch): XML 타입과 다른 타입 간 캐스팅이 불가능할 때 발생하며, XML 관련 작업에서 종종 함께 나타납니다.
  • 22021 (character_not_in_repertoire): XML 문서에 현재 인코딩에서 표현 불가능한 문자가 포함된 경우 발생할 수 있습니다.
DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기