DEV Community

umzzil nng
umzzil nng

Posted on • Originally published at oraerror.com

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

01004 string data right truncation 는?

01004 string data right truncation은 문자열 데이터를 특정 길이의 컬럼에 삽입하거나 업데이트할 때, 원본 데이터의 길이가 대상 컬럼의 최대 허용 길이를 초과하여 뒷부분이 잘려나가는 상황에서 발생하는 경고(Warning) 코드입니다. 엄밀히 말하면 에러가 아닌 경고(SQLSTATE 01004)로 분류되지만, 드라이버나 ORM에 따라 예외로 처리되어 애플리케이션 장애를 유발할 수 있습니다. 특히 CHAR(n), VARCHAR(n) 타입의 컬럼에 데이터를 넣을 때 자주 발생하며, 데이터 무결성 문제로 이어질 수 있어 반드시 주의해야 합니다.


주요 발생 원인

1. 컬럼 길이보다 긴 문자열 직접 삽입

가장 흔한 원인으로, VARCHAR(50)으로 정의된 컬럼에 50자를 초과하는 문자열을 INSERT 또는 UPDATE할 때 발생합니다. 특히 외부 API 응답값, 사용자 입력값, 혹은 레거시 시스템에서 마이그레이션된 데이터가 예상보다 긴 경우 이 문제가 빈번하게 나타납니다.

2. 스키마 변경 없이 데이터 요구사항이 변화한 경우

서비스 초기에는 VARCHAR(20)으로 충분했던 주소, 이름, 상품명 등의 컬럼이 시간이 지남에 따라 더 긴 데이터를 수용해야 하는 상황이 됩니다. 개발자가 애플리케이션 로직은 수정했지만 DB 스키마 변경을 누락하면 이 경고가 지속적으로 발생하며, 잘린 데이터가 실제 DB에 저장되는 심각한 결과를 초래할 수 있습니다.

3. 문자 인코딩 차이로 인한 바이트 길이 초과

PostgreSQL에서 VARCHAR(n)n은 문자(character) 수를 의미하지만, JDBC나 ODBC 드라이버에서 내부적으로 바이트 기준으로 길이를 계산하는 경우가 있습니다. UTF-8 환경에서 한글, 일본어, 중국어 등 멀티바이트 문자는 한 글자가 3바이트를 차지하므로, 드라이버 레벨에서 길이 초과로 잘못 판단하거나 실제로 바이트 제한에 걸리는 상황이 발생할 수 있습니다.


해결 방법

원인 1 해결: 삽입 전 문자열 길이 검증 및 명시적 트리밍

데이터 삽입 전에 길이를 미리 확인하거나, LEFT() 또는 SUBSTRING() 함수로 명시적으로 잘라서 넣는 방법입니다. 다만 이 방법은 데이터 손실을 감수하는 것이므로, 비즈니스 로직상 허용되는지 반드시 검토해야 합니다.

-- 문제가 되는 상황 재현
CREATE TABLE users (
    id      SERIAL PRIMARY KEY,
    username VARCHAR(10) NOT NULL
);

-- 10자 초과 데이터 삽입 시도
INSERT INTO users (username) VALUES ('이것은열글자를초과하는사용자이름입니다');
-- ERROR: value too long for type character varying(10)

-- 해결 1: 삽입 전 길이 확인
SELECT LENGTH('이것은열글자를초과하는사용자이름입니다'); -- 문자 수 반환

-- 해결 2: LEFT()로 명시적 절삭 후 삽입 (데이터 손실 감수)
INSERT INTO users (username)
VALUES (LEFT('이것은열글자를초과하는사용자이름입니다', 10));

-- 해결 3: CASE 구문으로 조건 분기
INSERT INTO users (username)
SELECT
    CASE
        WHEN LENGTH(username_input) > 10
            THEN SUBSTRING(username_input FROM 1 FOR 10)
        ELSE username_input
    END
FROM (SELECT '이것은열글자를초과하는사용자이름입니다' AS username_input) t;
Enter fullscreen mode Exit fullscreen mode

원인 2 해결: 컬럼 타입 길이 확장

근본적인 해결책은 컬럼의 최대 길이를 늘리는 것입니다. PostgreSQL에서는 VARCHAR 길이를 늘리는 작업은 테이블 재작성 없이 즉시 처리되므로 운영 중인 서비스에 큰 영향 없이 적용할 수 있습니다.

-- 현재 컬럼 정의 확인
SELECT
    column_name,
    data_type,
    character_maximum_length
FROM information_schema.columns
WHERE table_name = 'users'
  AND column_name = 'username';

-- VARCHAR 길이 확장 (PostgreSQL에서는 길이 증가는 테이블 락 없이 즉시 반영)
ALTER TABLE users
    ALTER COLUMN username TYPE VARCHAR(100);

-- 길이 제한이 사실상 불필요한 경우 TEXT 타입으로 변경 (권장)
ALTER TABLE users
    ALTER COLUMN username TYPE TEXT;

-- 변경 후 확인
\d users
Enter fullscreen mode Exit fullscreen mode

실무 팁: 가변 길이 문자열에는 VARCHAR(n) 대신 TEXT 타입을 사용하는 것을 권장합니다. PostgreSQL에서 TEXTVARCHAR는 내부적으로 동일하게 처리되며, TEXT는 길이 제한이 없어 불필요한 트런케이션 문제를 사전에 방지합니다.

원인 3 해결: 인코딩 문제 진단 및 바이트 길이 기준 처리

-- 데이터베이스 인코딩 확인
SHOW server_encoding;
SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database();

-- 문자 수 vs 바이트 수 비교 확인
SELECT
    '한글테스트' AS sample_text,
    LENGTH('한글테스트')          AS char_length,   -- 문자 수: 5
    OCTET_LENGTH('한글테스트')    AS byte_length;   -- 바이트 수: 15 (UTF-8 기준)

-- 바이트 기준으로 길이를 제한해야 하는 경우
-- (외부 시스템과 연동 시 바이트 제한이 있을 때)
SELECT
    input_text,
    SUBSTRING(input_text FROM 1 FOR
        -- 바이트 30 이내에서 최대한 많은 문자 포함
        LENGTH(CONVERT_TO(input_text, 'UTF8')::text)
    ) AS safe_text
FROM (SELECT '한글이포함된긴문자열데이터입니다' AS input_text) t;

-- pg_column_size로 실제 저장 크기 확인
SELECT pg_column_size('한글테스트'::text) AS storage_size;
Enter fullscreen mode Exit fullscreen mode

예방 방법

1. 컬럼 타입 설계 시 TEXT 우선 원칙과 CHECK 제약 조건 활용

초기 스키마 설계 단계에서 길이 제한이 비즈니스 규칙상 명확히 필요한 경우(예: 우편번호는 정확히 5자리)가 아니라면 TEXT 타입을 기본으로 사용하고, 길이 제한은 CHECK 제약 조건으로 명시적으로 관리하는 것이 좋습니다. 이렇게 하면 데이터베이스 레벨에서 에러 메시지가 더 명확해지고, 제약 조건만 수정하면 되므로 유지보수성이 높아집니다.

-- 권장하는 컬럼 설계 패턴
CREATE TABLE products (
    id           SERIAL PRIMARY KEY,
    product_code TEXT NOT NULL,
    product_name TEXT NOT NULL,
    description  TEXT,

    -- 비즈니스 규칙이 있는 경우에만 CHECK 제약 조건 사용
    CONSTRAINT chk_product_code_length CHECK (LENGTH(product_code) <= 20),
    CONSTRAINT chk_product_name_length CHECK (LENGTH(product_name) <= 200)
);

-- 길이 제한 변경 시 CHECK 제약 조건만 수정하면 됨
ALTER TABLE products
    DROP CONSTRAINT chk_product_name_length;

ALTER TABLE products
    ADD CONSTRAINT chk_product_name_length CHECK (LENGTH(product_name) <= 500);
Enter fullscreen mode Exit fullscreen mode

2. 데이터 파이프라인에 길이 검증 로직 내재화

ETL 작업, 배치 처리, API 연동 등 외부 데이터를 수신하는 모든 파이프라인에 사전 검증 쿼리를 추가합니다. 운영 환경에 반영하기 전, 스테이징 환경에서 실제 데이터로 길이 초과 여부를 항상 점검하는 습관을 팀 차원에서 정착시키는 것이 중요합니다.

-- 마이그레이션 또는 대량 적재 전 사전 검증 쿼리
-- 각 컬럼의 최대 길이를 컬럼 정의와 비교
SELECT
    'username'                          AS column_name,
    MAX(LENGTH(username))               AS max_data_length,
    10                                  AS column_limit,
    COUNT(CASE WHEN LENGTH(username) > 10 THEN 1 END) AS violation_count
FROM staging_users

UNION ALL

SELECT
    'email'                             AS column_name,
    MAX(LENGTH(email))                  AS max_data_length,
    255                                 AS column_limit,
    COUNT(CASE WHEN LENGTH(email) > 255 THEN 1 END) AS violation_count
FROM staging_users;

-- 위반 데이터 상세 조회
SELECT id, username, LENGTH(username) AS len
FROM staging_users
WHERE LENGTH(username) > 10
ORDER BY len DESC
LIMIT 20;
Enter fullscreen mode Exit fullscreen mode

관련 에러

  • 22001 (string_data_right_truncation): 01004의 에러 버전으로, 경고가 아닌 실제 에러로 처리됩니다. PostgreSQL에서 VARCHAR(n) 초과 삽입 시 기본적으로 이 에러 코드가 발생하며, "value too long for type character varying(n)" 메시지와 함께 트랜잭션이 롤백됩니다.

  • 22007 (invalid_datetime_format): 문자열을 날짜/시간 타입으로 변환할 때 형식이 맞지 않아 발생하는 에러로, 문자열 데이터 타입 불일치 문제의 연장선에 있습니다.

  • 42804 (datatype_mismatch): 컬럼 타입과 삽입하려는 데이터 타입이 완전히 다를 때 발생하며, 문자열 관련 스키마 설계 오류에서 함께 나타나는 경우가 많습니다.

  • 22P02 (invalid_text_representation): 문자열을 다른 타입(예: INTEGER, UUID 등)으로 캐스팅할 때 변환 불가능한 형식이면 발생하며, 타입 변환 관련 오류 디버깅 시 함께 확인해야 합니다.

Top comments (0)