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

55P04
2026년 09월 21일 | DBMS Error 가이드

이 글에서 다루는 내용

55P04 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

55P04 unsafe new enum value usage 는?

PostgreSQL 에러 코드 55P04 unsafe new enum value usage는 현재 트랜잭션 내에서 새로 추가된 ENUM 값을 사용하려 할 때 발생합니다. PostgreSQL에서 ALTER TYPE ... ADD VALUE 명령으로 ENUM 타입에 새 값을 추가하면, 해당 값은 트랜잭션이 커밋되기 전까지 동일 트랜잭션 내에서 안전하게 사용할 수 없습니다. 이는 PostgreSQL의 MVCC(다중 버전 동시성 제어) 메커니즘과 시스템 카탈로그 처리 방식에서 비롯된 근본적인 제약 사항입니다.

주요 발생 원인

  • 동일 트랜잭션 내에서 ENUM 값 추가 후 즉시 사용

가장 흔한 원인으로, 단일 트랜잭션 안에서 ALTER TYPE ... ADD VALUE로 새 ENUM 값을 추가한 직후 해당 값을 INSERT, UPDATE, 또는 비교 연산에 사용하려 할 때 발생합니다. PostgreSQL은 아직 커밋되지 않은 ENUM 값이 시스템 카탈로그에 완전히 반영되지 않았기 때문에 이를 안전하지 않다고 판단하여 에러를 발생시킵니다.

“`sql

— 에러 발생 예시

BEGIN;

ALTER TYPE order_status ADD VALUE ‘pending_review’;

INSERT INTO orders (status) VALUES (‘pending_review’); — ❌ 55P04 에러 발생

COMMIT;

“`

  • 마이그레이션 스크립트에서 DDL과 DML을 하나의 트랜잭션으로 묶는 경우

애플리케이션 배포 시 데이터베이스 마이그레이션 도구(Flyway, Liquibase, Alembic 등)를 사용할 때 ENUM 타입 변경과 해당 값을 사용하는 데이터 삽입/수정을 하나의 트랜잭션 블록으로 실행하면 이 에러가 발생합니다. 자동화된 마이그레이션 환경에서는 기본적으로 모든 SQL이 하나의 트랜잭션으로 묶이는 경우가 많아 이 문제를 사전에 인지하지 못하는 경우가 많습니다.

“`sql

— Alembic 또는 수동 마이그레이션 스크립트에서 자주 발생하는 패턴

BEGIN;

ALTER TYPE user_role ADD VALUE ‘super_admin’;

ALTER TABLE users ALTER COLUMN role SET DEFAULT ‘super_admin’; — ❌ 에러 발생

COMMIT;

“`

  • 함수 또는 저장 프로시저 내에서 동적으로 ENUM 값 추가 후 사용

PL/pgSQL 함수나 저장 프로시저 내에서 동적 SQL(EXECUTE)을 사용해 ENUM 값을 추가한 뒤, 같은 함수 실행 컨텍스트(즉, 같은 트랜잭션) 내에서 해당 값을 참조하는 경우에도 동일한 에러가 발생합니다. 이 경우는 에러 발생 위치를 특정하기 어려워 디버깅이 더 까다롭습니다.

“`sql

— 프로시저 내 문제 패턴

CREATE OR REPLACE PROCEDURE add_and_use_enum()

LANGUAGE plpgsql AS $$

BEGIN

EXECUTE ‘ALTER TYPE product_category ADD VALUE ”electronics_refurb”’;

— 같은 트랜잭션 내에서 즉시 사용 시 에러 발생

INSERT INTO products (category) VALUES (‘electronics_refurb’); — ❌

END;

$$;

“`

해결 방법

원인 1 & 2 해결: 트랜잭션 분리

가장 근본적인 해결책은 ENUM 값 추가와 해당 값 사용을 별도의 트랜잭션으로 분리하는 것입니다.

-- 1단계: ENUM 값 추가 (트랜잭션 1)
ALTER TYPE order_status ADD VALUE 'pending_review';
-- 트랜잭션 커밋 후 (autocommit 환경 또는 명시적 COMMIT)

-- 2단계: 새 값 사용 (트랜잭션 2 - 별도 세션 또는 이후 실행)
INSERT INTO orders (status) VALUES ('pending_review'); -- ✅ 정상 동작

마이그레이션 도구 사용 시 해결책

Flyway나 Liquibase를 사용하는 경우, ENUM 추가 스크립트와 데이터 변경 스크립트를 별도의 마이그레이션 파일로 분리하세요.

-- V1__add_enum_value.sql (트랜잭션 비활성화 필요 시 주석 추가)
-- flyway: disableChecksum=false
ALTER TYPE order_status ADD VALUE IF NOT EXISTS 'pending_review';

-- V2__use_new_enum_value.sql (별도 마이그레이션 파일)
UPDATE orders SET status = 'pending_review' WHERE review_flag = true;

Alembic을 사용하는 경우:

# migration_001_add_enum.py
def upgrade():
    # transaction=False 옵션으로 별도 트랜잭션 처리
    op.execute("ALTER TYPE order_status ADD VALUE 'pending_review'")

# migration_002_use_enum.py (별도 리비전)
def upgrade():
    op.execute("UPDATE orders SET status = 'pending_review' WHERE review_flag = true")

원인 3 해결: 프로시저 내 해결책

함수/프로시저에서 ENUM 값 추가 후 사용이 필요하다면, dblink 또는 별도 세션을 활용하거나 pg_enum 시스템 카탈로그를 직접 수정하는 방법(주의 필요)을 사용할 수 있습니다.

-- pg_enum 직접 수정 방식 (PostgreSQL 12 이상, 주의해서 사용)
-- 단일 트랜잭션 내에서도 동작하나 공식 지원 방법은 아님
DO $$
DECLARE
  v_type_oid OID;
BEGIN
  SELECT oid INTO v_type_oid FROM pg_type WHERE typname = 'order_status';
  
  -- 이미 존재하지 않을 때만 추가
  IF NOT EXISTS (
    SELECT 1 FROM pg_enum 
    WHERE enumtypid = v_type_oid AND enumlabel = 'pending_review'
  ) THEN
    ALTER TYPE order_status ADD VALUE 'pending_review';
  END IF;
END;
$$;

-- 커밋 후 다음 트랜잭션에서 사용
INSERT INTO orders (status) VALUES ('pending_review'); -- ✅
-- IF NOT EXISTS 활용으로 멱등성 보장 (PostgreSQL 9.6+)
ALTER TYPE order_status ADD VALUE IF NOT EXISTS 'pending_review';

예방 방법

  • ENUM 변경과 DML을 항상 분리된 마이그레이션 단계로 관리하기

팀 개발 표준으로 ALTER TYPE ... ADD VALUE 명령은 항상 독립적인 마이그레이션 스크립트에서 단독으로 실행하는 규칙을 정립하세요. CI/CD 파이프라인에서 마이그레이션 스크립트를 검토할 때 동일 파일 내에 ALTER TYPE ADD VALUE와 그 값을 사용하는 DML이 함께 있는지 린팅(linting) 도구로 자동 검사하는 것이 좋습니다. 예를 들어, sqlfluff 같은 SQL 린터나 커스텀 스크립트를 Git pre-commit 훅에 연결하면 이런 실수를 사전에 방지할 수 있습니다.

  • ENUM 대신 참조 테이블(Lookup Table) 패턴 검토하기

장기적으로 자주 변경되는 ENUM 타입이라면 별도의 참조 테이블(status_codes, categories 등)로 관리하는 것을 고려하세요. 참조 테이블 방식은 값 추가/삭제/수정이 일반적인 INSERT/DELETE/UPDATE 문으로 처리되어 이 에러가 원천 차단되고, 향후 값에 대한 메타데이터(설명, 정렬 순서 등)를 추가하기도 훨씬 유연합니다.

“`sql

— ENUM 대신 참조 테이블 사용 예시

CREATE TABLE order_status_codes (

code VARCHAR(50) PRIMARY KEY,

description TEXT,

is_active BOOLEAN DEFAULT true,

sort_order INT

);

INSERT INTO order_status_codes (code, description, sort_order)

VALUES (‘pending_review’, ‘검토 대기 중’, 5); — ✅ 트랜잭션 제약 없음

“`

관련 에러

  • 42710 duplicate_object: ALTER TYPE ... ADD VALUE 사용 시 이미 존재하는 ENUM 값을 추가하려 할 때 발생합니다. IF NOT EXISTS 옵션으로 방지할 수 있습니다.
  • 0A000 feature_not_supported: 트랜잭션 블록 내에서 ALTER TYPE ... ADD VALUE를 사용할 때 일부 PostgreSQL 버전이나 설정에서 명시적으로 차단하는 경우 발생하는 에러입니다. PostgreSQL 12 이전 버전에서는 이 에러가 더 자주 등장했습니다.
  • 25001 active_sql_transaction: DDL 작업 중 활성 트랜잭션 상태와 충돌할 때 발생하며, ENUM 관련 DDL 실행 시 트랜잭션 컨텍스트를 확인하지 않으면 마주칠 수 있는 에러입니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기