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

22024
2026년 06월 13일 | DBMS Error 가이드

이 글에서 다루는 내용

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

22024 unterminated c string 는?

PostgreSQL 에러 코드 22024 - unterminated c string은 SQL 문자열 또는 이스케이프 시퀀스 내에서 C 스타일 문자열이 올바르게 종료되지 않았을 때 발생하는 오류입니다. 주로 문자열 리터럴 내에 백슬래시(\)로 시작하는 이스케이프 시퀀스가 불완전하거나, 문자열이 null 바이트로 잘못 끝날 때 발생합니다. 이 에러는 특히 외부 애플리케이션에서 동적으로 생성된 SQL 쿼리를 PostgreSQL로 전달할 때, 또는 데이터 마이그레이션 과정에서 비정상적인 문자 데이터를 삽입할 때 자주 목격됩니다.


주요 발생 원인

1. 이스케이프 문자열 처리 오류 (Escape String 미종료)

가장 흔한 원인으로, PostgreSQL의 이스케이프 문자열(E'' 또는 standard_conforming_strings = off 환경)에서 백슬래시로 시작되는 이스케이프 시퀀스가 올바르게 종결되지 않은 경우입니다. 예를 들어 E'\x00' 처럼 null 문자를 직접 삽입하거나, \ 뒤에 유효하지 않은 문자가 오는 경우 파서가 문자열 끝을 찾지 못해 이 에러가 발생합니다. 특히 standard_conforming_stringsoff로 설정된 구형 PostgreSQL 환경에서는 모든 문자열이 이스케이프 문자열로 해석되므로 더욱 주의가 필요합니다.

2. 애플리케이션 레벨에서의 문자열 연결(Concatenation) 오류

Python, Java, PHP 등의 애플리케이션에서 사용자 입력값을 SQL 쿼리에 직접 연결(concatenation)할 때 입력값 안에 따옴표, 백슬래시, 특수문자 등이 적절히 이스케이프되지 않으면 SQL 파서가 문자열의 시작과 끝을 올바르게 인식하지 못합니다. 이 방식은 SQL 인젝션 취약점을 함께 유발하기도 하며, 비정상 입력이 들어올 경우 unterminated c string 에러가 발생합니다. Prepared Statement나 파라미터 바인딩을 사용하지 않는 레거시 코드에서 특히 자주 나타나는 패턴입니다.

3. 데이터 마이그레이션 또는 COPY 명령 사용 시 비정상 데이터

COPY 명령이나 외부 ETL 도구를 통해 CSV, TSV 등의 파일을 PostgreSQL에 적재할 때, 원본 데이터 안에 null 바이트(\0), 잘못된 멀티바이트 문자, 또는 불완전한 이스케이프 시퀀스가 포함되어 있으면 PostgreSQL이 해당 문자열을 파싱하다가 에러를 발생시킵니다. 특히 레거시 MySQL이나 Oracle에서 마이그레이션한 데이터, 또는 Excel/CSV에서 직접 내보낸 파일에 이런 문제가 숨어있는 경우가 많습니다. 데이터 사전 검증 없이 바로 적재를 시도하면 이 에러가 발생할 가능성이 높습니다.


해결 방법

원인 1 해결: 이스케이프 문자열 올바르게 작성하기

standard_conforming_strings 설정을 확인하고, 이스케이프 문자열을 사용할 때는 E'' 구문을 명시적으로 사용하며 백슬래시를 이중으로 처리합니다.

-- 현재 설정 확인
SHOW standard_conforming_strings;

-- 권장: standard_conforming_strings = on 설정 (PostgreSQL 9.1+ 기본값)
SET standard_conforming_strings = on;

-- 잘못된 예: 미종료 이스케이프 시퀀스
-- SELECT E'Hello\' AS bad_string;  -- 에러 발생

-- 올바른 예: 이스케이프 문자열 처리
SELECT E'Hello\\World' AS escaped_string;      -- Hello\World 출력
SELECT E'Line1\nLine2' AS newline_string;       -- 줄바꿈 포함 문자열
SELECT 'Hello''World' AS quoted_string;         -- 표준 방식 작은따옴표 이스케이프

-- null 바이트가 포함된 경우 제거 후 삽입
INSERT INTO test_table (content)
VALUES (replace('Hello' || chr(0) || 'World', chr(0), ''));

원인 2 해결: Prepared Statement 및 파라미터 바인딩 사용

애플리케이션에서 반드시 Prepared Statement를 사용하여 사용자 입력값을 SQL과 분리합니다.

-- 잘못된 방식 (동적 SQL 직접 연결 - 절대 사용 금지)
-- EXECUTE 'SELECT * FROM users WHERE name = ''' || user_input || '''';

-- 올바른 방식: PL/pgSQL에서 파라미터 사용
CREATE OR REPLACE FUNCTION get_user_by_name(p_name TEXT)
RETURNS TABLE(id INT, name TEXT, email TEXT) AS $$
BEGIN
    RETURN QUERY
    SELECT u.id, u.name, u.email
    FROM users u
    WHERE u.name = p_name;  -- 파라미터로 안전하게 전달
END;
$$ LANGUAGE plpgsql;

-- 함수 호출 (안전한 파라미터 바인딩)
SELECT * FROM get_user_by_name('O''Brien');  -- 특수문자도 안전하게 처리

-- quote_literal 함수를 이용한 동적 SQL 안전 처리
DO $$
DECLARE
    v_input TEXT := 'User''s input with special \\ chars';
    v_sql TEXT;
BEGIN
    v_sql := 'SELECT * FROM users WHERE name = ' || quote_literal(v_input);
    RAISE NOTICE 'SQL: %', v_sql;
    -- EXECUTE v_sql;
END;
$$;

원인 3 해결: COPY 전 데이터 클렌징 및 검증

COPY 명령 사용 전 반드시 원본 데이터에서 문제가 되는 문자를 제거하거나 변환합니다.

-- null 바이트 및 비정상 문자 확인 쿼리
SELECT id, content,
       length(content) AS char_len,
       octet_length(content) AS byte_len
FROM staging_table
WHERE content ~ '\x00'   -- null 바이트 포함 행 탐지
   OR content !~ '^[\x20-\x7E\n\r\t]*$';  -- ASCII 범위 외 문자 탐지

-- null 바이트 일괄 제거
UPDATE staging_table
SET content = replace(content, chr(0), '')
WHERE content LIKE '%' || chr(0) || '%';

-- COPY 시 null 처리 옵션 활용
-- COPY test_table FROM '/path/to/data.csv'
--   WITH (FORMAT csv, HEADER true, NULL '');

-- 문제 데이터 임시 테이블로 분리 후 클렌징
CREATE TEMP TABLE clean_data AS
SELECT id,
       regexp_replace(content, '[\x00-\x08\x0B\x0C\x0E-\x1F]', '', 'g') AS content
FROM raw_data;

-- 클렌징된 데이터 적재
INSERT INTO production_table (id, content)
SELECT id, content FROM clean_data;

예방 방법

1. standard_conforming_strings 항상 ON으로 유지하고 코드 리뷰에 이스케이프 정책 포함

PostgreSQL 9.1 이상에서는 기본값이 on이지만, 레거시 환경이나 마이그레이션 환경에서는 의도치 않게 off로 설정되어 있을 수 있습니다. postgresql.conf에서 명시적으로 standard_conforming_strings = on을 설정하고, 애플리케이션 코드 리뷰 단계에서 문자열 직접 연결 방식을 금지하는 정책을 팀 내 코딩 표준에 반드시 포함시켜야 합니다. 또한 CI/CD 파이프라인에 SQL 정적 분석 도구(예: sqlfluff, pgFormatter)를 도입하여 배포 전 자동 검사를 수행하는 것이 좋습니다.

-- postgresql.conf 설정 확인 및 적용
-- standard_conforming_strings = on

-- 현재 세션에서 확인
SELECT name, setting, boot_val, reset_val
FROM pg_settings
WHERE name = 'standard_conforming_strings';

2. 데이터 입력 전 검증 함수 및 트리거 활용

데이터베이스 레벨에서 비정상 문자가 포함된 데이터가 삽입되지 않도록 CHECK 제약 조건이나 트리거를 활용합니다. 이렇게 하면 애플리케이션 레이어의 실수를 DB 레벨에서도 이중으로 방어할 수 있습니다.

-- 비정상 문자 검증 함수 생성
CREATE OR REPLACE FUNCTION validate_safe_string(p_input TEXT)
RETURNS BOOLEAN AS $$
BEGIN
    -- null 바이트 및 제어 문자 포함 여부 검사
    IF p_input ~ chr(0) THEN
        RAISE EXCEPTION 'Input contains null byte character';
    END IF;
    IF p_input ~ '[\x01-\x08\x0B\x0C\x0E-\x1F\x7F]' THEN
        RAISE EXCEPTION 'Input contains invalid control characters';
    END IF;
    RETURN TRUE;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- CHECK 제약 조건으로 적용
ALTER TABLE users
ADD CONSTRAINT chk_safe_name
CHECK (validate_safe_string(name));

-- 트리거를 통한 자동 클렌징 (강제 제거 방식)
CREATE OR REPLACE FUNCTION trg_clean_string()
RETURNS TRIGGER AS $$
BEGIN
    NEW.content := replace(NEW.content, chr(0), '');
    NEW.content := regexp_replace(NEW.content, '[\x01-\x08\x0B\x0C\x0E-\x1F]', '', 'g');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_before_insert_clean
BEFORE INSERT OR UPDATE ON documents
FOR EACH ROW EXECUTE FUNCTION trg_clean_string();

관련 에러

  • 22021 – character_not_in_repertoire: 데이터베이스 인코딩(예: UTF-8)에서 지원하지 않는 문자를 삽입하려 할 때 발생하며, 22024와 함께 인코딩 관련 문제에서 자주 동반 발생합니다.
  • 22P02 – invalid_text_representation: 텍스트 형식을 다른 데이터 타입(정수, UUID 등)으로 변환할 때 형식이 맞지 않을 경우 발생하며, 잘못된 문자열 처리에서 파생됩니다.
  • 42601 – syntax_error: 미종료 문자열이 SQL 파서 레벨까지 전달되면 22024 대신 문법 오류로 나타나는 경우도 있어 함께 확인이 필요합니다.
  • 22000 – data_exception: 22024의 상위 에러 클래스로, 데이터 처리 과정의 다양한 예외를 포괄합니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기