PostgreSQL 22P04 오류 원인과 해결 방법 완벽 가이드

22P04
2026년 10월 22일 | DBMS Error 가이드

이 글에서 다루는 내용

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

22P04 bad copy file format 는?

PostgreSQL 에러 코드 22P04는 COPY 명령어를 사용하여 데이터를 가져오거나 내보낼 때 파일의 형식이 올바르지 않을 경우 발생하는 오류입니다. 이 에러는 주로 CSV, TEXT, BINARY 등의 포맷으로 데이터를 적재하는 과정에서 파일 구조가 PostgreSQL이 기대하는 형식과 일치하지 않을 때 나타납니다. 실무에서는 외부 시스템에서 생성된 파일을 PostgreSQL로 가져올 때, 혹은 자동화된 ETL 파이프라인에서 파일 인코딩이나 구분자 설정이 잘못된 경우 빈번하게 발생합니다.


주요 발생 원인

1. 잘못된 구분자(Delimiter) 또는 따옴표(Quote) 설정

가장 흔한 원인 중 하나로, CSV 파일의 실제 구분자와 COPY 명령어에서 지정한 구분자가 다를 때 발생합니다. 예를 들어 파일은 세미콜론(;)으로 구분되어 있는데 명령어에서 쉼표(,)를 구분자로 지정하면, PostgreSQL은 데이터를 파싱하지 못하고 형식 에러를 발생시킵니다. 따옴표 처리 방식이 맞지 않는 경우(예: 이중 따옴표 대신 단일 따옴표 사용)도 동일한 에러를 유발할 수 있습니다.

2. 파일 인코딩 불일치

파일이 UTF-8이 아닌 EUC-KR, CP949, Latin-1 등의 인코딩으로 저장되어 있는데 PostgreSQL 데이터베이스의 인코딩과 다를 경우 에러가 발생합니다. 특히 한국어 데이터를 다루는 환경에서는 윈도우에서 생성된 파일이 CP949 인코딩으로 저장되는 경우가 많아, UTF-8 기반의 PostgreSQL 환경과 충돌이 자주 발생합니다. 인코딩 문제는 눈으로 식별하기 어렵기 때문에 반드시 파일 인코딩을 사전에 확인하는 습관이 필요합니다.

3. 헤더 행(Header Row) 처리 오류 또는 열(Column) 수 불일치

COPY 명령어에서 HEADER 옵션을 지정하지 않았는데 파일에 헤더 행이 포함되어 있거나, 반대로 헤더가 없는 파일에 HEADER 옵션을 주는 경우 형식 에러가 발생합니다. 또한 CSV 파일의 열 수가 대상 테이블의 열 수와 다르거나, 특정 행에 불필요한 줄바꿈이 삽입된 경우에도 동일한 에러가 나타납니다. 대용량 파일을 다룰 때는 첫 몇 줄을 미리 확인하는 것이 중요합니다.


해결 방법

원인 1: 구분자 및 따옴표 설정 수정

파일의 실제 구분자를 확인한 후 COPY 명령어의 옵션을 맞게 조정합니다.

-- 기본 쉼표(,) 구분자 사용 (기본값)
COPY employees FROM '/data/employees.csv'
WITH (FORMAT CSV, HEADER true, DELIMITER ',', QUOTE '"');

-- 세미콜론(;) 구분자를 사용하는 파일의 경우
COPY employees FROM '/data/employees_semicolon.csv'
WITH (FORMAT CSV, HEADER true, DELIMITER ';', QUOTE '"');

-- 탭(TAB) 구분자를 사용하는 TEXT 포맷 파일의 경우
COPY employees FROM '/data/employees.tsv'
WITH (FORMAT TEXT, DELIMITER E'\t');

-- NULL 값 처리와 이스케이프 문자 설정 포함
COPY employees FROM '/data/employees_full.csv'
WITH (
    FORMAT CSV,
    HEADER true,
    DELIMITER ',',
    QUOTE '"',
    ESCAPE '"',
    NULL ''
);

원인 2: 파일 인코딩 불일치 해결

ENCODING 옵션을 사용하여 파일의 실제 인코딩을 명시적으로 지정합니다.

-- CP949(EUC-KR) 인코딩으로 저장된 파일 가져오기
COPY employees FROM '/data/employees_korean.csv'
WITH (FORMAT CSV, HEADER true, DELIMITER ',', ENCODING 'CP949');

-- UTF-8 인코딩 명시적 지정
COPY employees FROM '/data/employees_utf8.csv'
WITH (FORMAT CSV, HEADER true, DELIMITER ',', ENCODING 'UTF8');

-- 현재 데이터베이스의 인코딩 확인
SELECT pg_encoding_to_char(encoding) AS db_encoding
FROM pg_database
WHERE datname = current_database();

-- 파일 인코딩을 미리 변환한 후 적재하는 방법 (Shell 활용)
-- iconv -f CP949 -t UTF-8 employees_korean.csv > employees_utf8.csv
-- 변환 후 UTF-8로 COPY 실행
COPY employees FROM '/data/employees_utf8_converted.csv'
WITH (FORMAT CSV, HEADER true, ENCODING 'UTF8');

원인 3: 헤더 및 열 수 불일치 해결

헤더 옵션을 정확히 지정하고, 특정 열만 로드하거나 임시 테이블을 활용하는 방법을 사용합니다.

-- 헤더가 있는 파일에 HEADER 옵션 명시
COPY employees (id, name, department, salary)
FROM '/data/employees_with_header.csv'
WITH (FORMAT CSV, HEADER true);

-- 헤더가 없는 파일 처리
COPY employees (id, name, department, salary)
FROM '/data/employees_no_header.csv'
WITH (FORMAT CSV, HEADER false);

-- 열 수 불일치 해결: 임시 테이블을 활용한 단계적 로드
CREATE TEMP TABLE temp_employees (
    id        INTEGER,
    name      VARCHAR(100),
    dept      VARCHAR(50),
    salary    NUMERIC,
    extra_col TEXT  -- 파일에는 있지만 대상 테이블에 없는 열
);

COPY temp_employees FROM '/data/employees_extra.csv'
WITH (FORMAT CSV, HEADER true);

-- 필요한 열만 대상 테이블에 삽입
INSERT INTO employees (id, name, department, salary)
SELECT id, name, dept, salary
FROM temp_employees;

DROP TABLE temp_employees;

-- 특정 열만 지정하여 COPY 수행
COPY employees (name, department)
FROM '/data/partial_data.csv'
WITH (FORMAT CSV, HEADER true);

에러 로그 확인 및 디버깅

-- PostgreSQL 로그에서 에러 상세 정보 확인
-- postgresql.conf에서 log_error_verbosity = verbose 설정 후 로그 확인

-- 문제가 있는 행 번호 확인을 위한 방법 (psql에서 실행)
-- \copy 명령 사용 시 클라이언트 측에서 에러 위치 확인 가능
\copy employees FROM '/data/employees.csv' WITH (FORMAT CSV, HEADER true);

-- 소량의 샘플 데이터로 먼저 테스트
CREATE TEMP TABLE test_import (LIKE employees);
COPY test_import FROM '/data/employees_sample.csv'
WITH (FORMAT CSV, HEADER true, DELIMITER ',');
SELECT COUNT(*) FROM test_import;

예방 방법

1. 데이터 파일 사전 검증 자동화

COPY 명령을 실행하기 전에 파일의 인코딩, 구분자, 행 수, 열 수 등을 자동으로 검증하는 스크립트를 파이프라인에 포함시키는 것이 중요합니다. 리눅스 환경에서는 file 명령어로 인코딩을, head 명령어로 첫 줄의 구조를 사전에 확인하고, Python의 csv 모듈이나 chardet 라이브러리를 활용하여 파일 형식을 자동으로 감지하는 유효성 검사 단계를 ETL 워크플로우에 추가하세요. 이를 통해 잘못된 파일이 데이터베이스에 적재되기 전에 사전에 차단할 수 있습니다.

2. 표준화된 데이터 교환 포맷 및 COPY 템플릿 정의

팀 내에서 데이터 파일 생성 및 교환을 위한 표준 포맷(예: UTF-8 인코딩, 쉼표 구분자, 이중 따옴표 처리, HEADER 포함)을 문서화하고 이를 강제화하는 것이 중요합니다. 자주 사용하는 COPY 명령어 패턴을 함수나 저장 프로시저로 래핑하여 재사용하고, 개발팀 전체가 동일한 설정을 사용하도록 가이드라인을 수립하면 형식 불일치로 인한 에러를 크게 줄일 수 있습니다.


관련 에러

  • 22P05 (untranslatable_character): 데이터베이스 인코딩으로 변환할 수 없는 문자가 포함된 경우 발생하며, 22P04와 함께 인코딩 문제에서 자주 함께 나타납니다.
  • 22007 (invalid_datetime_format): COPY 중 날짜/시간 형식이 올바르지 않을 때 발생하는 에러로, 파일 포맷 문제와 연관될 수 있습니다.
  • 22P02 (invalid_text_representation): 숫자 열에 문자열이 들어오는 등 데이터 타입 변환 실패 시 발생하며, COPY 작업 중 형식 불일치와 관련이 있습니다.
  • 23502 (not_null_violation): COPY 중 NOT NULL 제약이 있는 열에 빈 값이 들어올 때 발생하며, 파일 형식 설정 오류(특히 NULL 옵션)와 연관될 수 있습니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기