2026년 10월 22일 | DBMS Error 가이드
이 글에서 다루는 내용
22P02 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22P02 invalid text representation 는?
PostgreSQL 에러 코드 22P02 invalid text representation은 문자열 형태의 입력값을 특정 데이터 타입으로 변환(캐스팅)할 때, 해당 형식이 올바르지 않아 변환에 실패하는 경우 발생합니다. 예를 들어, INTEGER 컬럼에 'abc'와 같은 숫자가 아닌 문자열을 삽입하거나, UUID 컬럼에 유효하지 않은 형식의 문자열을 넣으려 할 때 이 에러가 나타납니다. 애플리케이션 레이어에서 데이터 검증 없이 사용자 입력을 바로 쿼리에 사용할 때 자주 마주치게 되며, 특히 ORM 프레임워크나 마이그레이션 작업 중에도 빈번하게 발생합니다.
주요 발생 원인
- 잘못된 타입 캐스팅 (Type Casting 오류)
가장 흔한 원인으로, 문자열 리터럴을 숫자형(INTEGER, BIGINT, NUMERIC)이나 날짜형(DATE, TIMESTAMP), UUID 등 특정 타입으로 변환할 때 형식이 맞지 않으면 발생합니다. 예컨대 사용자 입력을 별도의 유효성 검사 없이 곧바로 ::integer 캐스팅하는 코드가 운영 환경에서 예상치 못한 입력을 받을 때 이 에러를 일으킵니다. 특히 프론트엔드에서 넘어오는 폼 데이터나 API 파라미터가 빈 문자열('')이거나 공백 문자인 경우에도 동일한 문제가 발생합니다.
- UUID 형식 불일치
UUID는 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 형식의 32개 16진수 문자와 4개의 하이픈으로 구성되어야 합니다. 하이픈 없이 연속된 32자리 문자열이거나, 대괄호로 감싸진 형태({uuid})를 그대로 삽입하려는 경우 이 에러가 발생합니다. 마이크로서비스 아키텍처에서 서로 다른 시스템 간 UUID 포맷이 통일되지 않은 채로 데이터가 연동될 때 특히 자주 목격됩니다.
- ENUM 타입 또는 배열 타입 입력 오류
PostgreSQL의 사용자 정의 ENUM 타입에 정의되지 않은 값을 삽입하거나, 배열(ARRAY) 타입 컬럼에 올바르지 않은 리터럴 형식을 사용할 때도 이 에러가 발생합니다. 예를 들어 'active', 'inactive'만 허용하는 ENUM에 'ACTIVE'(대소문자 불일치)를 넣거나, '{1,2,3}' 대신 '[1,2,3]' 형식의 JSON 스타일 배열을 넣으려는 경우가 해당됩니다. 외부 시스템과의 데이터 연동이나 레거시 코드 마이그레이션 시 특히 주의가 필요합니다.
해결 방법
원인 1: 잘못된 타입 캐스팅 해결
입력값이 올바른 형식인지 먼저 확인하고, 변환 전에 유효성을 체크하는 방어 코드를 추가하세요. NULLIF와 CASE 구문을 활용하거나, 정규식으로 사전 필터링하는 방법이 실무에서 효과적입니다.
-- 잘못된 방식: 검증 없이 직접 캐스팅
SELECT '123abc'::INTEGER; -- ERROR: 22P02
-- 해결 방법 1: CASE 문으로 유효성 검사 후 변환
SELECT
CASE
WHEN input_val ~ '^[0-9]+$' THEN input_val::INTEGER
ELSE NULL
END AS safe_int
FROM (SELECT '123abc' AS input_val) t;
-- 해결 방법 2: NULLIF를 활용한 빈 문자열 처리
SELECT NULLIF(trim(' '), '')::INTEGER; -- NULL 반환 (에러 없음)
-- 해결 방법 3: PostgreSQL 함수를 이용한 안전한 캐스팅 함수 생성
CREATE OR REPLACE FUNCTION safe_cast_to_int(p_input TEXT)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
BEGIN
RETURN p_input::INTEGER;
EXCEPTION
WHEN invalid_text_representation THEN
RETURN NULL;
END;
$$;
-- 사용 예시
SELECT safe_cast_to_int('456'); -- 456
SELECT safe_cast_to_int('abc'); -- NULL (에러 없음)
SELECT safe_cast_to_int(''); -- NULL (에러 없음)
-- DATE 타입 안전 변환 예시
SELECT
CASE
WHEN input_date ~ '^\d{4}-\d{2}-\d{2}$' THEN input_date::DATE
ELSE NULL
END AS safe_date
FROM (SELECT '2024-13-45' AS input_date) t; -- 형식은 맞지만 유효하지 않은 날짜
원인 2: UUID 형식 불일치 해결
UUID 관련 에러는 입력 전에 형식을 표준화하거나, uuid-ossp 확장을 활용하여 안전하게 처리할 수 있습니다.
-- UUID 에러 예시
SELECT 'not-a-uuid'::UUID; -- ERROR: 22P02
SELECT '{550e8400-e29b-41d4-a716-446655440000}'::UUID; -- 중괄호 포함 시 에러
-- 해결 방법 1: 중괄호 제거 후 변환
SELECT replace(replace('{550e8400-e29b-41d4-a716-446655440000}', '{', ''), '}', '')::UUID;
-- 해결 방법 2: 정규식으로 UUID 형식 검증 후 캐스팅
SELECT
CASE
WHEN input_uuid ~ '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
THEN input_uuid::UUID
ELSE NULL
END AS safe_uuid
FROM (SELECT '550e8400-e29b-41d4-a716-446655440000' AS input_uuid) t;
-- 해결 방법 3: 안전한 UUID 캐스팅 함수
CREATE OR REPLACE FUNCTION safe_cast_to_uuid(p_input TEXT)
RETURNS UUID
LANGUAGE plpgsql
AS $$
BEGIN
RETURN p_input::UUID;
EXCEPTION
WHEN invalid_text_representation THEN
RETURN NULL;
END;
$$;
-- uuid-ossp 확장 활용
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SELECT uuid_generate_v4(); -- 새 UUID 생성
원인 3: ENUM 및 배열 타입 오류 해결
ENUM은 대소문자를 구별하므로 입력값을 소문자로 통일하거나, 삽입 전 허용된 값인지 확인하는 로직이 필요합니다.
-- ENUM 타입 생성 및 에러 예시
CREATE TYPE user_status AS ENUM ('active', 'inactive', 'suspended');
-- 잘못된 삽입 시도
INSERT INTO users (status) VALUES ('ACTIVE'); -- ERROR: 22P02
-- 해결 방법 1: lower() 함수로 소문자 변환 후 캐스팅
INSERT INTO users (status) VALUES (lower('ACTIVE')::user_status); -- 성공
-- 해결 방법 2: 허용된 ENUM 값 사전 조회 및 검증
SELECT enumlabel
FROM pg_enum
JOIN pg_type ON pg_enum.enumtypid = pg_type.oid
WHERE pg_type.typname = 'user_status';
-- 배열 타입 올바른 사용법
CREATE TABLE tag_data (
id SERIAL PRIMARY KEY,
tags INTEGER[]
);
-- 잘못된 배열 삽입
INSERT INTO tag_data (tags) VALUES ('[1,2,3]'); -- ERROR: 22P02
-- 올바른 배열 삽입 방법
INSERT INTO tag_data (tags) VALUES ('{1,2,3}'); -- PostgreSQL 배열 리터럴
INSERT INTO tag_data (tags) VALUES (ARRAY[1,2,3]); -- ARRAY 생성자 사용
INSERT INTO tag_data (tags) VALUES ('ARRAY[1,2,3]'::TEXT::INTEGER[]); -- 텍스트에서 변환 시
-- JSON 배열을 PostgreSQL 배열로 변환
SELECT ARRAY(SELECT jsonb_array_elements_text('[1,2,3]'::jsonb))::INTEGER[];
예방 방법
- 애플리케이션 레이어에서 사전 유효성 검사 강화
데이터베이스에 값이 도달하기 전에 애플리케이션 코드(Java, Python, Node.js 등)에서 타입 검증을 철저히 수행하세요. 특히 ORM을 사용하는 경우에도 원시 쿼리(raw query)를 직접 사용하는 구간이 있다면, 파라미터 바인딩을 반드시 활용하여 문자열 연결로 쿼리를 구성하는 패턴을 배제해야 합니다. 아래와 같이 PostgreSQL의 도메인(Domain) 타입과 CHECK 제약을 활용하면 DB 레벨에서 이중으로 검증할 수 있습니다.
“`sql
— 도메인 타입으로 양의 정수만 허용하는 타입 정의
CREATE DOMAIN positive_int AS INTEGER
CHECK (VALUE > 0);
— 해당 도메인 타입 컬럼 사용
CREATE TABLE orders (
order_id positive_int PRIMARY KEY,
quantity positive_int NOT NULL
);
“`
- 안전한 캐스팅 함수 라이브러리 구축 및 공유
팀 내에서 공통으로 사용할 수 있는 안전 캐스팅 함수(safe_cast 패턴)를 PostgreSQL 스키마 내에 표준화하여 관리하세요. 개별 개발자가 각자 캐스팅 로직을 구현하면 일관성이 떨어지고 같은 에러가 반복됩니다. 공통 유틸리티 스키마(utils 스키마 등)를 만들어 안전 변환 함수들을 모아두고, 코드 리뷰 시 직접 캐스팅(::type) 대신 안전 함수 사용을 권장하는 가이드라인을 수립하는 것이 실무에서 매우 효과적입니다.
관련 에러
22003 numeric_value_out_of_range: 숫자 형식은 올바르나 컬럼의 허용 범위를 초과한 경우 발생합니다. 예를 들어SMALLINT컬럼에99999를 삽입하려 할 때 나타납니다.22007 invalid_datetime_format: 날짜/시간 문자열의 형식이 PostgreSQL이 인식할 수 없는 패턴일 때 발생하며,22P02와 혼동되기 쉽습니다.23502 not_null_violation: 안전 캐스팅 함수가NULL을 반환했는데 해당 컬럼이NOT NULL제약을 가진 경우 연쇄적으로 발생할 수 있어 함께 관리가 필요합니다.42804 datatype_mismatch: 함수나 연산자에 전달된 인자의 데이터 타입이 기대 타입과 다를 때 발생하며, 캐스팅 문제와 함께 나타나는 경우가 많습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.