2026년 06월 14일 | DBMS Error 가이드
이 글에서 다루는 내용
22P02 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22P02 invalid text representation 는?
PostgreSQL 에러 코드 22P02 invalid text representation은 문자열(텍스트)을 특정 데이터 타입으로 변환(캐스팅)할 때, 그 형식이 올바르지 않아 발생하는 에러입니다. 예를 들어, integer 컬럼에 'abc'라는 문자열을 삽입하거나, uuid 타입 컬럼에 유효하지 않은 형식의 값을 넣으려 할 때 이 에러가 발생합니다. 주로 애플리케이션에서 사용자 입력을 그대로 SQL 쿼리에 바인딩하거나, ETL 파이프라인에서 데이터 타입을 명시적으로 검증하지 않을 때 실무에서 자주 마주치는 에러입니다.
주요 발생 원인
1. 잘못된 타입 캐스팅 (숫자형, 날짜형 등)
가장 흔한 원인으로, 숫자 타입(integer, bigint, numeric)이나 날짜/시간 타입(date, timestamp)으로 변환할 수 없는 문자열 값을 직접 삽입하거나 캐스팅할 때 발생합니다. 예를 들어 사용자 입력 폼에서 나이 필드에 '스물다섯' 같은 문자가 들어오거나, 날짜 필드에 '2024/13/45'처럼 존재하지 않는 날짜가 들어오는 경우가 이에 해당합니다. ETL 작업 중 원천 데이터의 품질이 낮을 때도 이 문제가 자주 발생합니다.
2. UUID 형식 오류
uuid 데이터 타입은 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 형식의 매우 엄격한 규칙을 따릅니다. 애플리케이션에서 UUID를 생성하거나 전달할 때 하이픈이 빠지거나, 길이가 맞지 않거나, 유효하지 않은 문자가 포함된 경우 이 에러가 발생합니다. 특히 마이크로서비스 환경에서 서로 다른 서비스 간에 ID를 주고받을 때 포맷 불일치로 인해 발생하는 경우가 많습니다.
3. ENUM 타입에 정의되지 않은 값 삽입
PostgreSQL의 ENUM 타입은 미리 정의된 값 목록 외의 문자열을 허용하지 않습니다. 코드 배포 후 ENUM에 새로운 값이 추가되었는데 데이터베이스 마이그레이션이 제대로 적용되지 않았거나, 대소문자가 달라도 이 에러가 발생합니다. 예를 들어 ENUM에 'active'가 정의되어 있는데 'Active' 또는 'ACTIVE'를 삽입하려 하면 에러가 납니다.
해결 방법
원인 1: 잘못된 타입 캐스팅 해결
캐스팅 전 값의 유효성을 검사하는 것이 핵심입니다. PostgreSQL에서는 REGEXP_LIKE 또는 예외 처리를 활용할 수 있습니다.
-- 문제가 되는 쿼리 예시
INSERT INTO users (age) VALUES ('스물다섯'); -- 에러 발생
-- 해결 1: 애플리케이션에서 값을 검증 후 삽입
INSERT INTO users (age) VALUES (25); -- 올바른 정수값 삽입
-- 해결 2: NULLIF와 CASE를 활용해 안전하게 변환
SELECT
CASE
WHEN input_val ~ '^[0-9]+$' THEN input_val::integer
ELSE NULL
END AS safe_age
FROM (SELECT '스물다섯' AS input_val) t;
-- 해결 3: 잘못된 날짜 형식 안전하게 처리
SELECT
CASE
WHEN input_date ~ '^\d{4}-\d{2}-\d{2}$'
THEN input_date::date
ELSE NULL
END AS safe_date
FROM staging_table;
-- 해결 4: to_date 함수로 안전한 날짜 파싱
SELECT to_date('2024-12-25', 'YYYY-MM-DD'); -- 성공
-- SELECT to_date('2024/13/45', 'YYYY-MM-DD'); -- 에러 발생하므로 사전 검증 필요
원인 2: UUID 형식 오류 해결
UUID 값을 다루기 전에 형식을 검증하거나, 문자열을 올바른 UUID 형식으로 정규화하는 작업이 필요합니다.
-- 문제가 되는 쿼리 예시 (하이픈 없는 UUID)
SELECT * FROM orders WHERE order_id = 'a0eebc999c0b4ef8bb6d61bd1a200001'::uuid;
-- ERROR: invalid input syntax for type uuid
-- 해결 1: 하이픈 없는 32자리 문자열을 UUID로 변환
SELECT (
SUBSTRING('a0eebc999c0b4ef8bb6d61bd1a200001', 1, 8) || '-' ||
SUBSTRING('a0eebc999c0b4ef8bb6d61bd1a200001', 9, 4) || '-' ||
SUBSTRING('a0eebc999c0b4ef8bb6d61bd1a200001', 13, 4) || '-' ||
SUBSTRING('a0eebc999c0b4ef8bb6d61bd1a200001', 17, 4) || '-' ||
SUBSTRING('a0eebc999c0b4ef8bb6d61bd1a200001', 21, 12)
)::uuid;
-- 해결 2: UUID 형식 검증 함수 생성
CREATE OR REPLACE FUNCTION is_valid_uuid(input TEXT)
RETURNS BOOLEAN AS $$
BEGIN
PERFORM input::uuid;
RETURN TRUE;
EXCEPTION WHEN invalid_text_representation THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
-- 함수 사용 예시
SELECT is_valid_uuid('a0eebc99-9c0b-4ef8-bb6d-61bd1a200001'); -- true
SELECT is_valid_uuid('not-a-uuid'); -- false
-- 해결 3: 유효한 UUID만 필터링하여 조회
SELECT *
FROM staging_orders
WHERE is_valid_uuid(order_id_text)
AND order_id_text::uuid IS NOT NULL;
원인 3: ENUM 타입 오류 해결
ENUM 타입의 현재 허용 값을 확인하고, 필요한 경우 새로운 값을 추가하거나 입력 값을 정규화합니다.
-- 현재 ENUM 타입에 정의된 값 확인
SELECT enumlabel
FROM pg_enum
JOIN pg_type ON pg_enum.enumtypid = pg_type.oid
WHERE pg_type.typname = 'user_status';
-- 문제가 되는 쿼리 예시
UPDATE users SET status = 'Active'::user_status; -- 에러 발생 (대소문자 불일치)
-- 해결 1: 소문자로 정규화 후 캐스팅
UPDATE users SET status = LOWER('Active')::user_status; -- 'active'로 변환 후 삽입
-- 해결 2: ENUM에 새 값 추가 (마이그레이션)
ALTER TYPE user_status ADD VALUE IF NOT EXISTS 'suspended';
-- 해결 3: ENUM 값 목록과 비교 후 안전하게 업데이트
UPDATE staging_users su
SET status = su.raw_status::user_status
WHERE su.raw_status IN (
SELECT enumlabel::text
FROM pg_enum
JOIN pg_type ON pg_enum.enumtypid = pg_type.oid
WHERE pg_type.typname = 'user_status'
);
예방 방법
1. 입력 데이터 검증 레이어 도입 (CHECK 제약 조건 + 도메인 타입 활용)
데이터가 데이터베이스에 들어오기 전, 애플리케이션 레벨과 데이터베이스 레벨 모두에서 유효성 검사를 이중으로 적용하는 것이 최선입니다. PostgreSQL의 CHECK 제약 조건과 DOMAIN 타입을 활용하면 잘못된 형식의 데이터가 테이블에 저장되는 것을 원천 차단할 수 있습니다.
-- 도메인 타입을 활용한 이메일 형식 강제 예시
CREATE DOMAIN valid_email AS TEXT
CHECK (VALUE ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$');
CREATE TABLE contacts (
id SERIAL PRIMARY KEY,
email valid_email NOT NULL
);
-- 잘못된 이메일 삽입 시 도메인 레벨에서 차단
INSERT INTO contacts (email) VALUES ('not-an-email'); -- 에러 발생하여 차단
2. 스테이징 테이블 + 변환 검증 패턴 사용 (ETL 파이프라인)
ETL 파이프라인에서는 원천 데이터를 바로 운영 테이블에 삽입하지 말고, 모든 컬럼을 TEXT 타입으로 받는 스테이징 테이블에 먼저 적재한 뒤, 타입 변환 가능 여부를 검증하고 이상 데이터를 격리한 후 운영 테이블로 이동하는 패턴을 사용하세요. 이를 통해 22P02 에러로 인한 배치 작업 전체 실패를 방지할 수 있습니다.
-- 스테이징 테이블: 모든 컬럼을 TEXT로 받음
CREATE TABLE stg_users (
raw_id TEXT,
raw_age TEXT,
raw_signup TEXT
);
-- 검증 후 운영 테이블로 이동
INSERT INTO users (id, age, signup_date)
SELECT
raw_id::integer,
raw_age::integer,
raw_signup::date
FROM stg_users
WHERE raw_id ~ '^[0-9]+$'
AND raw_age ~ '^[0-9]+$'
AND raw_signup ~ '^\d{4}-\d{2}-\d{2}$';
-- 이상 데이터는 별도 오류 테이블로 격리
INSERT INTO stg_users_errors
SELECT *, NOW() AS detected_at
FROM stg_users
WHERE NOT (
raw_id ~ '^[0-9]+$'
AND raw_age ~ '^[0-9]+$'
AND raw_signup ~ '^\d{4}-\d{2}-\d{2}$'
);
관련 에러
22003 numeric_value_out_of_range: 숫자 형식은 올바르지만 해당 타입의 범위를 초과한 경우 발생합니다.22P02와 유사하지만 형식 자체는 유효할 때 나타납니다.22007 invalid_datetime_format: 날짜/시간 문자열의 형식 자체가 잘못되었을 때 발생하며,22P02의 날짜 관련 케이스와 혼동될 수 있습니다.23502 not_null_violation: 타입 변환 실패로NULL이 생성된 뒤 NOT NULL 컬럼에 삽입될 때 연쇄적으로 발생할 수 있습니다.42804 datatype_mismatch: 함수나 연산자에 잘못된 타입의 인자를 전달할 때 발생하며, 암시적 캐스팅 실패와 관련이 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.