2026년 08월 18일 | DBMS Error 가이드
이 글에서 다루는 내용
22P04 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22P04 bad copy file format 는?
PostgreSQL 에러 코드 22P04는 bad copy file format으로, COPY 명령을 사용하여 데이터를 가져오거나 내보낼 때 파일의 형식이 올바르지 않을 때 발생합니다. 주로 CSV, 텍스트, 바이너리 등의 포맷 옵션과 실제 파일의 내용이 일치하지 않을 때 PostgreSQL이 파싱 과정에서 이 오류를 던집니다. 데이터 마이그레이션, ETL 파이프라인, 대용량 데이터 적재 작업 등 실무에서 매우 빈번하게 마주치는 에러이므로 원인과 해결책을 정확히 이해하는 것이 중요합니다.
주요 발생 원인
- 파일 포맷과 COPY 옵션의 불일치
가장 흔한 원인으로, 실제 파일은 CSV 형식인데 COPY 명령에서 FORMAT TEXT로 지정하거나, 반대로 탭 구분자 텍스트 파일을 FORMAT CSV로 읽으려 할 때 발생합니다. PostgreSQL은 파일의 내용을 지정된 포맷 규칙에 따라 엄격하게 파싱하기 때문에, 헤더 행 처리나 구분자(delimiter) 설정이 조금만 어긋나도 즉시 이 에러를 반환합니다. 예를 들어, 실제 파일에는 헤더 행이 있는데 HEADER 옵션을 지정하지 않으면 첫 번째 행을 데이터로 인식하여 타입 변환 오류와 함께 포맷 에러가 발생할 수 있습니다.
- 바이너리 포맷 파일의 손상 또는 버전 불일치
PostgreSQL의 바이너리 COPY 형식(FORMAT BINARY)은 파일 헤더에 특정 시그니처(PGCOPY\n\377\r\n\0)와 플래그 값을 포함합니다. 이 시그니처가 파일 전송 중 손상되거나, 다른 도구로 생성된 바이너리 파일을 PostgreSQL COPY로 읽으려 할 때, 또는 서로 다른 PostgreSQL 메이저 버전 간에 바이너리 파일을 교환할 때 포맷 검증에 실패하여 22P04 에러가 발생합니다. 특히 FTP나 이메일 등으로 바이너리 파일을 전송할 때 텍스트 모드로 전송되면 줄바꿈 문자가 변환되어 파일이 손상되는 경우가 많습니다.
- OS별 줄바꿈 문자(Line Ending) 문제
Windows 환경에서 생성된 파일(\r\n, CRLF)을 Linux/Unix 기반의 PostgreSQL 서버에서 텍스트 모드로 읽을 때 \r 문자가 데이터의 일부로 인식되어 포맷 오류가 발생합니다. 반대로 Unix 방식의 줄바꿈(\n, LF)을 가진 파일을 Windows 환경에서 처리할 때도 유사한 문제가 생길 수 있습니다. 이 문제는 눈에 잘 띄지 않아서 원인을 파악하는 데 시간이 많이 걸리는 경우가 많고, 특히 엑셀에서 CSV로 내보낸 파일을 그대로 사용할 때 자주 발생합니다.
해결 방법
원인 1: 파일 포맷과 COPY 옵션 불일치 해결
먼저 파일의 실제 포맷을 확인하고 COPY 명령의 옵션을 정확히 맞춰야 합니다.
-- 잘못된 예시: CSV 파일을 TEXT 포맷으로 읽으려는 경우
COPY employees FROM '/data/employees.csv' WITH (FORMAT TEXT);
-- 올바른 예시: CSV 파일에 맞는 포맷 지정
COPY employees FROM '/data/employees.csv' WITH (
FORMAT CSV,
HEADER true,
DELIMITER ',',
NULL 'NULL',
ENCODING 'UTF8'
);
-- 탭 구분자 텍스트 파일인 경우
COPY employees FROM '/data/employees.tsv' WITH (
FORMAT TEXT,
DELIMITER E'\t',
NULL '\N'
);
-- 파일 내용을 먼저 확인하기 위한 임시 테이블 활용
CREATE TEMP TABLE raw_import (line TEXT);
COPY raw_import FROM '/data/employees.csv' WITH (FORMAT TEXT);
SELECT * FROM raw_import LIMIT 10;
원인 2: 바이너리 포맷 문제 해결
바이너리 파일의 경우 PostgreSQL에서 생성된 파일인지 확인하고, 가능하면 텍스트/CSV 포맷으로 변환하여 사용하는 것을 권장합니다.
-- 바이너리 파일 확인 및 텍스트 포맷으로 재생성
-- 원본 DB에서 텍스트 포맷으로 내보내기
COPY employees TO '/data/employees_safe.csv' WITH (
FORMAT CSV,
HEADER true,
ENCODING 'UTF8'
);
-- 대상 DB에서 텍스트 포맷으로 가져오기
COPY employees FROM '/data/employees_safe.csv' WITH (
FORMAT CSV,
HEADER true,
ENCODING 'UTF8'
);
-- 바이너리 COPY를 사용해야 할 경우 반드시 같은 PostgreSQL 버전에서 생성된 파일 사용
-- psql 명령줄에서 바이너리 파일 무결성 간접 확인
-- \copy employees TO '/tmp/test.bin' WITH (FORMAT BINARY)
-- \copy employees_backup FROM '/tmp/test.bin' WITH (FORMAT BINARY)
-- 바이너리 파일 헤더 검증 (PostgreSQL 시그니처 확인용 쿼리)
-- 아래는 pg_read_binary_file로 시그니처를 확인하는 방법 (superuser 필요)
SELECT encode(pg_read_binary_file('/tmp/test.bin', 0, 11), 'escape') AS binary_header;
원인 3: 줄바꿈 문자 문제 해결
OS 간 줄바꿈 문자 차이로 인한 문제는 파일 전처리를 통해 해결할 수 있습니다.
-- psql에서 \copy 사용 시 클라이언트 측에서 변환 가능
-- Linux/Mac에서 CRLF 파일을 처리하는 방법 (셸 명령어 활용)
-- dos2unix employees_windows.csv employees_unix.csv
-- 또는
-- sed -i 's/\r//' employees_windows.csv
-- PostgreSQL 내에서 COPY 후 데이터 정제
COPY employees_staging FROM '/data/employees_windows.csv' WITH (
FORMAT CSV,
HEADER true
);
-- \r 문자 제거
UPDATE employees_staging
SET employee_name = REPLACE(employee_name, E'\r', '')
WHERE employee_name LIKE E'%\r%';
-- 정제된 데이터를 실제 테이블로 이동
INSERT INTO employees
SELECT * FROM employees_staging;
-- 프로그램 코드에서 STDIN을 이용한 스트리밍 방식으로 줄바꿈 처리
-- Python 예시 (참고용 주석)
-- conn.cursor().copy_expert(
-- "COPY employees FROM STDIN WITH (FORMAT CSV, HEADER true)",
-- open('employees.csv', 'r', newline='')
-- )
예방 방법
- COPY 작업 전 파일 검증 자동화
데이터 파이프라인에 COPY 명령을 실행하기 전, 항상 파일의 인코딩, 줄바꿈 방식, 구분자, 컬럼 수 등을 자동으로 검증하는 스크립트를 포함시키세요. 아래와 같이 임시 테이블을 활용하여 작은 샘플로 먼저 테스트하는 습관을 들이면 대규모 적재 실패를 사전에 방지할 수 있습니다.
“`sql
— 사전 검증용 임시 테이블로 소량 테스트
CREATE TEMP TABLE validation_test (LIKE employees);
— 파일 앞부분만 읽어서 포맷 검증 (PostgreSQL 16 이상 COPY … LIMIT 지원)
COPY validation_test FROM ‘/data/employees.csv’ WITH (
FORMAT CSV,
HEADER true,
ENCODING ‘UTF8’
);
— 검증 후 컬럼 수, 타입 등 확인
SELECT count(*), pg_typeof(employee_id), pg_typeof(employee_name)
FROM validation_test
LIMIT 5;
— 문제 없으면 실제 테이블에 적재
DROP TABLE validation_test;
“`
- 표준화된 데이터 교환 포맷 및 메타데이터 관리
팀 내에서 COPY에 사용할 파일 포맷을 FORMAT CSV, ENCODING 'UTF8', HEADER true, DELIMITER ','으로 표준화하고, 이를 문서화하여 공유하세요. 또한 데이터 파일과 함께 포맷 정보를 담은 메타데이터 파일(예: .meta.json)을 함께 배포하거나, 파이프라인 설정 파일에 COPY 옵션을 명시적으로 버전 관리하에 두는 것이 좋습니다. 이를 통해 담당자가 바뀌거나 환경이 달라져도 항상 동일한 방식으로 데이터를 처리할 수 있습니다.
관련 에러
22P05(untranslatable_character): COPY 중 지정된 인코딩으로 변환할 수 없는 문자가 포함된 경우 발생하며,22P04와 함께 인코딩 관련 COPY 오류로 자주 묶여서 나타납니다.22007(invalid_datetime_format): COPY 중 날짜/시간 컬럼의 포맷이 맞지 않을 때 발생하며, 파일 포맷 자체는 맞더라도 개별 필드 값의 형식 오류로 이어질 수 있습니다.42601(syntax_error): COPY 명령문 자체의 문법 오류로, 옵션 키워드를 잘못 사용했을 때 발생합니다.58030(io_error): 파일 자체에 접근할 수 없거나 읽기 권한이 없을 때 발생하며, 파일이 존재하더라도 OS 레벨 권한 문제로 COPY가 실패할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.