2026년 10월 20일 | DBMS Error 가이드
이 글에서 다루는 내용
22001 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22001 string data right truncation 는?
PostgreSQL 에러 코드 22001은 string data right truncation 으로, 문자열 데이터를 특정 컬럼에 삽입하거나 업데이트할 때 해당 컬럼에 정의된 최대 길이보다 더 긴 문자열을 저장하려 할 때 발생합니다. 예를 들어 VARCHAR(10)으로 정의된 컬럼에 11자 이상의 문자열을 넣으려 하면 PostgreSQL은 데이터를 자르는 대신 이 에러를 발생시킵니다. 이는 데이터 무결성을 보호하기 위한 PostgreSQL의 엄격한 타입 검사 정책의 일환으로, 일부 다른 DBMS가 조용히 데이터를 잘라내는 것과 달리 명시적으로 에러를 반환합니다.
주요 발생 원인
1. VARCHAR 또는 CHAR 컬럼의 길이 제한 초과
가장 흔한 원인으로, 테이블 스키마를 설계할 때 충분한 길이를 예측하지 못하거나 외부 시스템에서 받은 데이터가 예상보다 긴 경우 발생합니다. 특히 사용자 입력을 그대로 DB에 저장하는 구조에서 자주 발생하며, 나중에 요구사항 변경으로 더 긴 문자열이 들어오게 될 때 문제가 됩니다. 예를 들어, 초기에 VARCHAR(50)으로 설계한 이메일 컬럼에 50자를 초과하는 이메일 주소가 들어오는 경우가 대표적입니다.
2. 애플리케이션 레이어에서 유효성 검사 없이 DB에 직접 저장
백엔드 애플리케이션에서 입력값에 대한 길이 검증 없이 곧바로 SQL을 실행하면, 예상치 못한 긴 문자열이 DB까지 도달하게 됩니다. 특히 외부 API 응답값, 파일 업로드 처리, 또는 마이그레이션 스크립트를 통해 데이터를 적재할 때 이런 상황이 자주 발생합니다. 애플리케이션과 DB 사이에 아무런 안전망이 없다면 에러가 런타임에 터지게 됩니다.
3. 데이터 마이그레이션 또는 ETL 과정에서의 스키마 불일치
레거시 시스템이나 다른 데이터베이스로부터 데이터를 이관할 때, 원본 시스템의 컬럼 길이 정의와 대상 PostgreSQL 스키마의 컬럼 길이가 맞지 않아 발생합니다. Oracle, MySQL 등 다른 DBMS에서 PostgreSQL로 마이그레이션하는 과정에서 특히 자주 발생하며, 각 DBMS의 문자 인코딩 처리 방식 차이(예: UTF-8 멀티바이트 문자)로 인해 바이트 수가 달라져 예상치 못한 길이 초과가 일어나기도 합니다.
해결 방법
원인 1: 컬럼 길이 제한 초과 해결
컬럼의 길이를 늘리는 것이 가장 직접적인 해결책입니다. ALTER TABLE을 사용해 기존 컬럼의 길이를 늘릴 수 있으며, PostgreSQL에서는 길이를 늘리는 작업은 테이블 재작성 없이도 처리됩니다.
-- 기존 컬럼 길이 확인
SELECT column_name, character_maximum_length, data_type
FROM information_schema.columns
WHERE table_name = 'users'
AND column_name = 'email';
-- 컬럼 길이 확장 (VARCHAR(50) -> VARCHAR(255))
ALTER TABLE users
ALTER COLUMN email TYPE VARCHAR(255);
-- 길이 제한이 불필요한 경우 TEXT 타입으로 변경
ALTER TABLE users
ALTER COLUMN email TYPE TEXT;
만약 길이 제한이 필요 없는 컬럼이라면 TEXT 타입으로 변경하는 것도 좋은 방법입니다. PostgreSQL에서 TEXT와 VARCHAR는 내부 저장 방식이 동일하므로 성능 차이는 없습니다.
원인 2: 데이터 삽입 전 길이 검증 및 자르기
애플리케이션 레이어에서 수정이 어렵거나 임시방편이 필요한 경우, DB 레벨에서 데이터를 자르는 방법을 사용할 수 있습니다.
-- 삽입 전 데이터 길이 확인
SELECT length('이 문자열이 얼마나 긴지 확인합니다 - check this string length');
-- LEFT() 함수를 이용한 안전한 삽입
INSERT INTO users (username, email)
VALUES (
LEFT('매우_긴_사용자이름_that_exceeds_limit', 50),
LEFT('very_long_email_address@extremely_long_domain_name.com', 100)
);
-- SUBSTRING을 이용한 방법
INSERT INTO products (description)
VALUES (SUBSTRING('매우 긴 제품 설명 텍스트...', 1, 200));
-- UPDATE 시에도 동일하게 적용
UPDATE users
SET bio = LEFT(bio_input, 500)
WHERE user_id = 123;
단, 이 방법은 데이터가 잘리므로 비즈니스 로직에 따라 허용 여부를 신중하게 판단해야 합니다.
원인 3: 마이그레이션 시 길이 초과 데이터 사전 탐지
ETL 또는 마이그레이션 작업 전에 반드시 데이터 프로파일링을 수행하여 길이 초과 데이터를 미리 찾아내야 합니다.
-- 특정 컬럼에서 길이 제한을 초과하는 데이터 탐지
SELECT id, email, length(email) AS email_length
FROM staging_users
WHERE length(email) > 100
ORDER BY email_length DESC;
-- 마이그레이션 전 최대 길이 확인
SELECT
MAX(length(email)) AS max_email_len,
MAX(length(username)) AS max_username_len,
MAX(length(phone)) AS max_phone_len
FROM staging_users;
-- 멀티바이트 문자(한국어 등) 길이 확인 - 바이트 수 vs 문자 수
SELECT
username,
length(username) AS char_length, -- 문자 수
octet_length(username) AS byte_length -- 바이트 수
FROM staging_users
WHERE octet_length(username) > 100;
-- 대상 테이블 컬럼 정의 확인
SELECT
column_name,
data_type,
character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users';
예방 방법
1. 처음부터 넉넉한 컬럼 길이 설계 및 TEXT 타입 적극 활용
컬럼 길이를 설계할 때 현재 요구사항보다 충분히 넉넉하게 잡거나, 길이 제한이 비즈니스적으로 의미 없는 컬럼은 처음부터 TEXT 타입으로 선언하세요. PostgreSQL에서는 VARCHAR(n)과 TEXT의 성능 차이가 없으므로, 굳이 짧은 길이로 제한할 이유가 없다면 TEXT를 사용하는 것이 유지보수 측면에서 훨씬 유리합니다. 실무에서는 이메일, 주소, 설명 등 자유 입력 필드에 TEXT를 적용하는 패턴을 권장합니다.
-- 권장 스키마 설계 예시
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL, -- 비즈니스 규칙이 있는 경우 길이 제한
email TEXT NOT NULL, -- 자유 형식은 TEXT 사용
bio TEXT, -- 긴 텍스트는 TEXT
phone VARCHAR(20), -- 포맷이 정해진 경우 VARCHAR
created_at TIMESTAMPTZ DEFAULT now()
);
2. CHECK 제약 조건 및 애플리케이션 레이어 유효성 검사 이중화
DB에만 의존하지 말고, 애플리케이션 레이어에서도 입력 길이를 반드시 검증하도록 이중 방어선을 구축하세요. DB에서는 CHECK 제약 조건으로 한 번 더 보호하고, 애플리케이션에서는 DB에 전송하기 전에 길이를 검사하는 로직을 추가합니다. 이 두 가지 방어선을 함께 사용하면 런타임 에러 없이 사용자에게 친절한 메시지를 보여줄 수 있습니다.
-- CHECK 제약 조건으로 이중 보호
ALTER TABLE users
ADD CONSTRAINT chk_username_length CHECK (length(username) <= 100);
-- 트리거를 활용한 자동 처리 (선택적)
CREATE OR REPLACE FUNCTION truncate_long_strings()
RETURNS TRIGGER AS $$
BEGIN
IF length(NEW.username) > 100 THEN
RAISE EXCEPTION 'username이 100자를 초과합니다: % 자', length(NEW.username);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_check_user_lengths
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION truncate_long_strings();
관련 에러
- 22000 (data exception): 22001의 상위 카테고리 에러로, 데이터 관련 예외의 일반적인 분류입니다.
- 22P02 (invalid_text_representation): 문자열을 숫자나 날짜 등 다른 타입으로 캐스팅할 때 형식이 맞지 않아 발생하는 에러로, 타입 변환 과정에서 22001과 함께 자주 등장합니다.
- 23502 (not_null_violation): NULL 값을 NOT NULL 컬럼에 삽입할 때 발생하며, 데이터 삽입/업데이트 시 22001과 함께 발생하는 대표적인 제약 조건 에러입니다.
- 42804 (datatype_mismatch): 타입이 맞지 않는 데이터를 컬럼에 넣으려 할 때 발생하며, 마이그레이션 작업 중 22001과 동시에 마주치는 경우가 많습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.