2026년 07월 18일 | DBMS Error 가이드
이 글에서 다루는 내용
55P04 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
55P04 unsafe new enum value usage 는?
PostgreSQL 에러 코드 55P04 (unsafe new enum value usage)는 트랜잭션 내에서 새로 추가된 ENUM 값을 동일 트랜잭션 안에서 즉시 사용하려 할 때 발생합니다. PostgreSQL은 ENUM 타입에 새 값을 추가하는 ALTER TYPE ... ADD VALUE 명령이 트랜잭션 커밋 이전에는 해당 값을 안전하게 사용할 수 없다고 간주하며, 이는 시스템 카탈로그 일관성을 보호하기 위한 의도적인 설계입니다. 즉, ALTER TYPE과 해당 새 ENUM 값을 사용하는 DML(INSERT, UPDATE 등)을 하나의 트랜잭션 블록 안에 묶으면 이 에러가 발생합니다.
주요 발생 원인
- 동일 트랜잭션 내에서 ENUM 값 추가 후 즉시 사용
가장 흔한 원인입니다. BEGIN 으로 트랜잭션을 시작하고 ALTER TYPE ... ADD VALUE로 새 ENUM 값을 추가한 직후, 같은 트랜잭션 블록에서 해당 값을 INSERT 또는 UPDATE에 사용하면 PostgreSQL은 카탈로그가 아직 완전히 커밋되지 않은 상태이므로 이를 차단합니다. 이 제약은 PostgreSQL 9.1 이후 ENUM의 트랜잭셔널 특성에서 비롯된 것으로, 새 ENUM 값은 트랜잭션이 커밋된 이후에야 다른 세션 또는 후속 트랜잭션에서 안전하게 참조될 수 있습니다.
- 마이그레이션 스크립트에서 DDL과 DML을 하나의 트랜잭션으로 묶는 경우
Flyway, Liquibase, Alembic 등 마이그레이션 툴은 기본적으로 모든 SQL 문을 하나의 트랜잭션으로 래핑합니다. 이때 ENUM 값 추가(DDL)와 해당 값을 삽입하는 DML이 동일 스크립트에 존재하면 55P04 에러가 발생합니다. 특히 자동화된 배포 파이프라인에서 발생하면 배포 자체가 실패하므로 매우 치명적입니다.
- 저장 프로시저 또는 DO 블록 내에서 ENUM 값 추가 후 즉시 참조
PL/pgSQL의 DO $$ ... $$ 익명 블록이나 프로시저 내에서 ALTER TYPE으로 ENUM 값을 추가하고 같은 블록 내에서 해당 값을 사용하는 경우에도 동일한 에러가 발생합니다. PL/pgSQL 블록은 암묵적으로 트랜잭션 컨텍스트 안에서 실행되기 때문에, 블록 내 모든 문은 사실상 하나의 트랜잭션으로 처리됩니다.
해결 방법
원인 1 해결: 트랜잭션을 분리하여 실행
ALTER TYPE ... ADD VALUE를 별도의 트랜잭션에서 먼저 커밋한 후, 새로운 트랜잭션에서 해당 값을 사용합니다.
-- 1단계: ENUM 값 추가 후 즉시 커밋 (트랜잭션 분리)
ALTER TYPE order_status ADD VALUE 'PENDING_REVIEW';
-- 이 문장은 autocommit 모드에서 단독 실행하거나 COMMIT을 명시해야 합니다.
-- 2단계: 새 트랜잭션에서 새 ENUM 값 사용
BEGIN;
UPDATE orders
SET status = 'PENDING_REVIEW'
WHERE created_at < NOW() - INTERVAL '7 days'
AND status = 'PENDING';
COMMIT;
원인 1 대안: IF NOT EXISTS 옵션 활용 (PostgreSQL 9.3+)
-- 이미 존재하는 경우 에러 없이 스킵
ALTER TYPE order_status ADD VALUE IF NOT EXISTS 'PENDING_REVIEW';
-- 반드시 별도 트랜잭션에서 실행
BEGIN;
INSERT INTO orders (customer_id, status, created_at)
VALUES (101, 'PENDING_REVIEW', NOW());
COMMIT;
원인 2 해결: 마이그레이션 스크립트 분리
Flyway/Liquibase 등에서는 ENUM 값 추가와 DML을 반드시 별도 마이그레이션 파일로 분리해야 합니다.
-- V1__add_enum_value.sql (DDL 전용 마이그레이션)
-- Flyway에서 트랜잭션 비활성화 옵션 사용 권장
-- @formatter:off
ALTER TYPE order_status ADD VALUE IF NOT EXISTS 'PENDING_REVIEW';
-- @formatter:on
-- V2__use_new_enum_value.sql (DML 마이그레이션 - 별도 파일)
BEGIN;
UPDATE orders
SET status = 'PENDING_REVIEW'
WHERE condition_column = TRUE;
COMMIT;
Flyway를 사용하는 경우 해당 마이그레이션 파일에 disableChecksum 또는 executeInTransaction=false 옵션을 설정하여 트랜잭션 래핑을 비활성화하는 것이 중요합니다.
원인 3 해결: DO 블록 또는 프로시저 분리
-- 잘못된 방법 (55P04 에러 발생)
DO $$
BEGIN
ALTER TYPE mood ADD VALUE 'ecstatic';
-- 아래 문장에서 55P04 에러 발생!
UPDATE user_profiles SET current_mood = 'ecstatic' WHERE user_id = 1;
END;
$$;
-- 올바른 방법: DO 블록 밖에서 ALTER TYPE 먼저 실행 후 커밋
ALTER TYPE mood ADD VALUE IF NOT EXISTS 'ecstatic';
-- 위 명령을 독립 트랜잭션으로 커밋한 뒤 아래 실행
DO $$
BEGIN
UPDATE user_profiles SET current_mood = 'ecstatic' WHERE user_id = 1;
END;
$$;
현재 ENUM 값 확인 방법
-- 특정 ENUM 타입의 현재 값 목록 조회
SELECT enumlabel, enumsortorder
FROM pg_enum
JOIN pg_type ON pg_type.oid = pg_enum.enumtypid
WHERE pg_type.typname = 'order_status'
ORDER BY enumsortorder;
-- 모든 ENUM 타입과 값 목록 조회
SELECT n.nspname AS schema_name,
t.typname AS enum_name,
e.enumlabel AS enum_value,
e.enumsortorder AS sort_order
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_namespace n ON n.oid = t.typnamespace
ORDER BY n.nspname, t.typname, e.enumsortorder;
예방 방법
- DDL과 DML을 반드시 분리된 트랜잭션으로 실행하는 배포 정책 수립
팀 내 마이그레이션 규칙으로 “ENUM 타입 변경(ADD VALUE)은 항상 독립적인 트랜잭션 또는 독립된 마이그레이션 파일에서 실행한다”는 원칙을 문서화하고 코드 리뷰 체크리스트에 포함시키세요. CI/CD 파이프라인에서 Flyway를 사용한다면, ENUM 값 추가가 포함된 마이그레이션 파일에는 executeInTransaction=false를 명시적으로 설정하여 트랜잭션 자동 래핑을 방지합니다. 이 한 가지 규칙만 지켜도 운영 환경 배포 실패의 상당 부분을 예방할 수 있습니다.
- ENUM 대신 참조 테이블(Lookup Table) 패턴 고려
ENUM 타입은 값 추가 시 위와 같은 트랜잭션 제약이 있고, 값 삭제나 순서 변경이 매우 어렵다는 단점이 있습니다. 자주 변경될 가능성이 있는 상태값(status), 카테고리(category) 등은 ENUM 대신 별도의 참조 테이블과 외래 키(FK)를 활용하는 설계를 채택하면 이 문제를 근본적으로 회피할 수 있습니다. 참조 테이블 방식은 값 추가/삭제/수정이 일반 DML로 가능하므로 트랜잭션 제약에서 자유롭고, 나중에 값별 메타데이터(설명, 표시 순서 등)를 추가하기도 용이합니다.
관련 에러
42809(wrong_object_type): ENUM이 아닌 타입에ALTER TYPE ... ADD VALUE를 시도할 때 발생합니다.42710(duplicate_object):IF NOT EXISTS없이 이미 존재하는 ENUM 값을 추가하려 할 때 발생합니다. 항상ADD VALUE IF NOT EXISTS를 사용하는 습관을 들이세요.25001(active_sql_transaction): 일부 DDL 명령이 트랜잭션 블록 내에서 허용되지 않을 때 발생하며, 55P04와 유사한 맥락에서 DDL/트랜잭션 충돌 상황에 마주칠 수 있습니다.0A000(feature_not_supported): PostgreSQL 버전에 따라 특정 ENUM 조작이 지원되지 않을 때 발생할 수 있으며, ENUM 관련 작업 시 PostgreSQL 버전 호환성을 반드시 확인해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.