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

01004
2026년 08월 01일 | DBMS Error 가이드

이 글에서 다루는 내용

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

01004 string data right truncation 는?

PostgreSQL 에러 코드 01004(string_data_right_truncation)는 문자열 데이터를 특정 컬럼이나 변수에 저장할 때, 대상의 최대 길이보다 긴 문자열이 입력되어 데이터가 오른쪽에서 잘리는(truncation) 상황에서 발생합니다. 표준 SQL에서는 이 상황을 경고(WARNING) 레벨로 처리하지만, PostgreSQL은 기본적으로 이를 에러로 간주하여 작업을 중단시킵니다. 주로 VARCHAR(n), CHAR(n) 등 길이 제한이 있는 문자열 타입의 컬럼에 데이터를 INSERT하거나 UPDATE할 때, 혹은 PL/pgSQL 함수 내부에서 변수에 값을 할당할 때 빈번하게 나타납니다.


주요 발생 원인

1. 컬럼 길이 정의보다 긴 문자열 삽입 (INSERT/UPDATE)

가장 흔한 원인으로, 테이블 설계 시 정의한 VARCHAR(n) 또는 CHAR(n) 컬럼의 최대 길이를 초과하는 문자열을 삽입하거나 업데이트하려 할 때 발생합니다. 예를 들어 VARCHAR(10)으로 정의된 컬럼에 11자 이상의 문자열을 넣으면 PostgreSQL은 이를 즉시 에러로 처리합니다. 이는 데이터 마이그레이션, 외부 시스템 연동, 사용자 입력값 검증 누락 등 다양한 상황에서 발생할 수 있습니다.

2. PL/pgSQL 함수/프로시저 내 변수 타입 불일치

PL/pgSQL로 작성된 함수나 프로시저 내부에서 길이 제한이 있는 변수(VARCHAR(n), CHAR(n))에 그보다 긴 문자열 값을 할당하려 할 때 에러가 발생합니다. 특히 외부에서 받아온 파라미터를 그대로 내부 변수에 넣거나, 문자열을 조합(concatenation)하여 고정 길이 변수에 저장하는 로직에서 쉽게 발생합니다. 함수 내부이기 때문에 에러 메시지만으로는 정확한 발생 위치를 파악하기 어려워 디버깅이 번거로울 수 있습니다.

3. 외부 데이터 로딩 (COPY, ETL, 마이그레이션) 과정에서 발생

CSV 파일이나 외부 데이터베이스에서 COPY 명령 또는 ETL 도구를 통해 데이터를 적재할 때, 원본 데이터의 문자열 길이가 대상 테이블의 컬럼 정의를 초과하는 경우 이 에러가 발생합니다. 특히 레거시 시스템에서 마이그레이션하거나, 타 시스템과의 데이터 연동 시 컬럼 길이 스펙을 사전에 충분히 검토하지 않으면 대량의 에러가 발생할 수 있습니다. 이 경우 단순히 한 건이 아니라 수만 건의 데이터가 실패할 수 있어 운영상 큰 문제가 됩니다.


해결 방법

원인 1: 컬럼 길이 초과 INSERT/UPDATE 해결

방법 A – 컬럼 타입 길이를 늘리기 (ALTER TABLE)

-- 기존 컬럼이 VARCHAR(10)이고 더 긴 데이터가 필요한 경우
ALTER TABLE users ALTER COLUMN username TYPE VARCHAR(50);

-- 변경 후 데이터 삽입 테스트
INSERT INTO users (username) VALUES ('this_is_a_longer_username_now');

방법 B – 삽입 전 데이터를 명시적으로 자르기 (SUBSTRING 사용)

-- 원본 데이터를 컬럼 길이에 맞게 강제 절단
INSERT INTO users (username)
VALUES (SUBSTRING('this_is_a_very_long_username', 1, 10));

-- UPDATE 시에도 동일하게 적용
UPDATE users
SET username = SUBSTRING(new_username_value, 1, 10)
WHERE user_id = 123;

방법 C – 데이터 삽입 전 길이 검증

-- 삽입 전 길이 체크 후 조건 분기
DO $$
DECLARE
    v_input TEXT := 'this_is_a_very_long_username_value';
BEGIN
    IF LENGTH(v_input) > 10 THEN
        RAISE NOTICE '입력값이 너무 깁니다: % 문자 (최대 10자)', LENGTH(v_input);
    ELSE
        INSERT INTO users (username) VALUES (v_input);
    END IF;
END;
$$;

원인 2: PL/pgSQL 내 변수 타입 불일치 해결

방법 A – 변수의 길이 제한 확대

CREATE OR REPLACE FUNCTION process_user_input(p_name TEXT)
RETURNS VOID AS $$
DECLARE
    -- 기존: v_name VARCHAR(10);  -- 너무 짧아서 에러 발생
    v_name VARCHAR(255);  -- 충분히 크게 변경
BEGIN
    v_name := p_name;
    RAISE NOTICE '처리된 이름: %', v_name;
END;
$$ LANGUAGE plpgsql;

방법 B – TEXT 타입으로 변수 선언 후 처리

CREATE OR REPLACE FUNCTION safe_insert_user(p_name TEXT, p_email TEXT)
RETURNS VOID AS $$
DECLARE
    v_name TEXT;   -- TEXT는 길이 제한 없음
    v_email TEXT;
BEGIN
    -- 필요한 경우 길이 검증 후 절단
    v_name  := LEFT(p_name, 50);   -- 컬럼 길이에 맞게 절단
    v_email := LEFT(p_email, 100);

    INSERT INTO users (username, email)
    VALUES (v_name, v_email);

    RAISE NOTICE '사용자 삽입 완료: %', v_name;
END;
$$ LANGUAGE plpgsql;

원인 3: COPY/ETL 과정에서의 해결

방법 A – COPY 전 임시 테이블 활용

-- 1단계: 임시 테이블에 TEXT 타입으로 전부 로드
CREATE TEMP TABLE temp_users (
    username TEXT,
    email    TEXT,
    phone    TEXT
);

-- CSV에서 임시 테이블로 적재 (길이 제한 없음)
COPY temp_users FROM '/path/to/users.csv' WITH (FORMAT csv, HEADER true);

-- 2단계: 길이 검증 및 정제 후 실제 테이블에 삽입
INSERT INTO users (username, email, phone)
SELECT
    LEFT(TRIM(username), 50),   -- 50자 초과분 절단
    LEFT(TRIM(email), 100),
    LEFT(TRIM(phone), 20)
FROM temp_users
WHERE username IS NOT NULL;

-- 3단계: 문제 데이터 확인
SELECT username, LENGTH(username) AS len
FROM temp_users
WHERE LENGTH(username) > 50;

방법 B – 마이그레이션 전 사전 검증 쿼리

-- 마이그레이션 전 원본 데이터 길이 분포 확인
SELECT
    MAX(LENGTH(username))  AS max_username_len,
    MAX(LENGTH(email))     AS max_email_len,
    MAX(LENGTH(phone))     AS max_phone_len,
    COUNT(*) FILTER (WHERE LENGTH(username) > 50) AS username_overflow_count
FROM source_table;

예방 방법

1. 컬럼 설계 시 충분한 여유 길이 확보 및 TEXT 타입 우선 고려

실무에서는 초기 설계 시 지나치게 작은 VARCHAR(n) 길이를 지정하지 않도록 주의해야 합니다. 업무 도메인에서 최대로 예상되는 문자열 길이보다 최소 1.5~2배 이상 여유를 두고 설계하는 것이 좋습니다. 길이 제한이 비즈니스적으로 명확히 필요한 경우가 아니라면 PostgreSQL의 TEXT 타입을 사용하는 것도 현명한 선택입니다. TEXT 타입은 PostgreSQL 내부적으로 VARCHAR와 동일한 스토리지 구조를 가지므로 성능 차이가 없으며, 불필요한 길이 제한 에러를 사전에 방지할 수 있습니다.

-- 권장: 길이 제한이 명확히 필요한 경우만 VARCHAR(n) 사용
CREATE TABLE users_recommended (
    user_id   SERIAL PRIMARY KEY,
    username  VARCHAR(100),   -- 비즈니스 규칙상 100자 제한
    bio       TEXT,           -- 자유 형식 텍스트는 TEXT 사용
    phone     VARCHAR(20),    -- 국제 전화번호 형식 고려
    created_at TIMESTAMPTZ DEFAULT NOW()
);

2. 입력 데이터 검증 레이어 구축 (CHECK 제약 + 애플리케이션 레이어 이중 검증)

데이터베이스 레벨에서 CHECK 제약조건이나 트리거를 활용하여 사전에 데이터 길이를 검증하고, 애플리케이션 레이어에서도 이중으로 검증하는 구조를 갖추는 것이 가장 효과적인 예방책입니다. 특히 외부 시스템과의 인터페이스 지점에서 입력값 검증을 강화해야 합니다.

-- CHECK 제약조건으로 길이 제한 명시
ALTER TABLE users
ADD CONSTRAINT chk_username_length CHECK (LENGTH(username) BETWEEN 3 AND 50);

-- 트리거를 활용한 자동 절단 및 경고 처리
CREATE OR REPLACE FUNCTION trg_truncate_username()
RETURNS TRIGGER AS $$
BEGIN
    IF LENGTH(NEW.username) > 50 THEN
        RAISE WARNING '사용자명이 50자를 초과하여 절단됩니다: %', NEW.username;
        NEW.username := LEFT(NEW.username, 50);
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_before_insert_users
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION trg_truncate_username();

관련 에러

  • 22001 (string_data_right_truncation): 01004와 동일한 상황이지만 에러(ERROR) 레벨로 발생하는 코드입니다. PostgreSQL에서 실제로 가장 많이 마주치는 코드로, 트랜잭션이 즉시 롤백됩니다.
  • 22000 (data_exception): 다양한 데이터 관련 예외의 상위 카테고리 에러로, 01004 역시 이 범주에 속합니다.
  • 42804 (datatype_mismatch): 데이터 타입 자체가 맞지 않을 때 발생하며, 문자열을 숫자형 컬럼에 넣으려 할 때 주로 나타납니다.
  • 23514 (check_violation): CHECK 제약조건을 위반했을 때 발생하며, 위에서 소개한 chk_username_length 같은 제약 위반 시 이 에러가 발생합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기