Back to blog

미세한 변화: PostgreSQL의 NOT VALID와 NOT ENFORCED 제약 조건 이해하기

September 17, 2026

작성자: Amul Sul

작성일: 2026년 3월 27일

PostgreSQL은 뛰어난 데이터 무결성(Data Integrity)을 제공하는 데이터베이스로 잘 알려져 있습니다. 그러나 데이터 세트가 테라바이트 단위로 커지면 제약 조건을 검사하고 검증하는 비용이 시스템 운영의 부담으로 이어질 수 있습니다. PostgreSQL은 이러한 상황에 대응할 수 있도록 제약 조건에 여러 상태를 제공합니다.

기존의 NOT VALID 옵션은 대용량 테이블에 제약 조건을 추가할 때 널리 활용돼 왔습니다. PostgreSQL 18에는 SQL:2023 표준의 개념인 NOT ENFORCED가 공식 도입됐습니다. 이름은 비슷하지만 두 옵션은 기존 데이터와 새 데이터의 검사 여부, 내부 트리거 생성 여부, 활용 목적에서 뚜렷한 차이가 있습니다.

PostgreSQL 18 제약 조건 상태 핵심 요약

PostgreSQL의 제약 조건은 단순히 엄격하게 켜지거나 꺼진 상태로만 존재하지 않습니다. NOT VALIDNOT ENFORCED는 다음과 같이 서로 다른 목적으로 사용됩니다.

  • NOT VALID: 긴 잠금(Lock) 시간 없이 대용량 테이블에 제약 조건을 추가할 때 사용할 수 있습니다. 수동 검증(Validation)을 실행하기 전까지 기존 데이터 검사는 생략하지만, 새로 추가되거나 변경되는 데이터에는 규칙을 강제합니다.
  • NOT ENFORCED: 스키마 문서화 등의 목적으로 제약 조건을 정의하되, 강제 상태로 전환하기 전까지 데이터베이스 엔진이 데이터 규칙 위반을 차단하지 않도록 설정합니다.

참고: PostgreSQL 18에서 NOT ENFORCED 상태는 CHECK 및 외래 키(FOREIGN KEY) 제약 조건에 대해서만 지원됩니다.


NOT VALID와 NOT ENFORCED는 무엇이 다를까요?

NOT ENFORCED가 필요한 이유를 이해하려면 먼저 기존 NOT VALID 기능의 동작 방식을 살펴봐야 합니다.

  • NOT VALID — 임시 마이그레이션 상태: 제약 조건을 NOT VALID로 추가하면 기존 데이터 검사는 생략하지만, 해당 시점 이후 새로 추가되거나 변경되는 행(Row)에는 규칙을 적용합니다. 관리자는 이후 유효성 검사 스캔(Validation Scan)을 실행해 제약 조건을 완전히 검증할 수 있습니다.
  • NOT ENFORCED — 기능적 메타데이터 상태: 제약 조건과 관계를 메타데이터로 정의하지만, 새로운 데이터와 기존 데이터 모두 검사하지 않습니다.

핵심 차이점은 새 데이터 검사 여부입니다. NOT VALID는 기존 데이터 검증만 미루고 새 데이터에는 제약 조건을 적용합니다. 반면 NOT ENFORCED는 강제 상태로 전환하기 전까지 검사를 수행하지 않습니다.

기능(Feature)NOT VALIDNOT ENFORCED
새로운 데이터 검사 여부예(Yes)아니요(강제 활성화 전까지)
기존 데이터 검사 여부아니요(검증 스캔 전까지)아니요(강제 활성화 전까지)
시스템 오버헤드높음(검사 및 트리거 활성 유지)낮음(검사 및 트리거 생략)

외래 키에서 NOT ENFORCED를 사용할 때 내부 트리거는 어떻게 달라질까요?

PostgreSQL 18의 중요한 아키텍처 변화 중 하나는 외래 키(Foreign Key, FK)를 NOT ENFORCED로 생성할 때의 동작 방식입니다.

CHECK 제약 조건은 데이터 무결성을 유지하기 위해 다른 데이터베이스 객체에 의존하지 않으므로 비교적 단순합니다. 반면 일반적인 외래 키 제약 조건은 INSERT, UPDATE, DELETE 작업에서 실행되는 내부 트리거(Internal Trigger)에 의존합니다.

외래 키가 NOT ENFORCED로 정의되면 PostgreSQL은 이러한 내부 트리거 생성을 건너뛰고, CHECK 제약 조건과 마찬가지로 시스템 카탈로그(System Catalog)에 상태를 기록합니다. 이에 따라 트리거 실행 비용이 발생하지 않으므로 대용량 쓰기(High-volume Write) 작업에서 오버헤드를 줄일 수 있습니다. 트리거는 이후 제약 조건을 ENFORCED로 변경할 때 생성됩니다.


NOT ENFORCED와 ENFORCED 상태를 전환하는 방법

적용되지 않은 상태(Unenforced)에서 강제 적용 상태(Enforced)로 전환하려면 데이터 검증 과정이 필요합니다. 다음 예시는 외래 키 제약 조건을 생성하고 상태를 전환하는 흐름을 보여줍니다.

1. NOT ENFORCED로 제약 조건 생성하기

다음 SQL은 트리거를 생성하지 않고 테이블 간 관계를 정의합니다.

ALTER TABLE orders 
ADD CONSTRAINT fk_product 
FOREIGN KEY (product_no) REFERENCES products(product_no) 
NOT ENFORCED;

2. ENFORCED로 전환하기

데이터베이스가 무결성 규칙을 강제하도록 전환하려면 다음 SQL을 실행합니다.

ALTER TABLE orders 
ALTER CONSTRAINT fk_product ENFORCED;

이 변경 과정에서는 데이터 검증(Validation)이 수행됩니다. PostgreSQL은 제약 조건이 적용되지 않았던 기간에 추가된 데이터를 그대로 신뢰할 수 없으므로, 제약 조건을 강제하기 전에 전체 스캔(Full Scan)을 통해 모든 행이 규칙을 준수하는지 확인합니다.

외래 키 제약 조건의 경우 무결성 검사에 필요한 내부 트리거가 생성되고 유효성 확인을 위한 전체 스캔이 실행됩니다. 이때 유효하지 않은 행(Invalid Row)이 하나라도 발견되면 명령은 실패합니다.

참고: PostgreSQL 18에서 이러한 상태 변경(Alteration) 기능은 현재 외래 키 제약 조건에만 적용됩니다. CHECK 제약 조건의 강제 여부를 변경하는 지원 기능은 논의 중이며, 향후 버전을 위해 PostgreSQL 개발 메일링 리스트에 제안된 상태입니다.

3. 다시 NOT ENFORCED로 전환하기

대규모 벌크 로드(Bulk Load)를 위해 일시적으로 트리거 오버헤드를 비활성화하려면 다음 SQL을 실행합니다.

ALTER TABLE orders 
ALTER CONSTRAINT fk_product NOT ENFORCED;

상황별 NOT VALID와 NOT ENFORCED 선택 가이드

  • 데이터베이스가 규칙을 계속 강제해야 하지만 장시간의 접근 배타적 잠금(Access Exclusive Lock) 없이 대용량 테이블에 제약 조건을 추가해야 할 때NOT VALID를 사용합니다.
  • 애플리케이션 계층에서 데이터 무결성을 관리하고 있으며 데이터베이스의 검사와 트리거 실행을 생략하려는 경우에는 NOT ENFORCED를 검토할 수 있습니다. 현재 쿼리 옵티마이저(Query Optimizer)가 SELECT 쿼리 성능 향상을 위해 이 제약 조건을 직접 사용하지는 않지만, 제약 조건을 정의하면 스키마 문서화(Schema Documentation)에 활용할 수 있습니다.

NOT ENFORCED는 데이터베이스가 무결성을 검사하지 않는 상태이므로, 적용 전에는 데이터 품질을 보장할 다른 계층과 운영 절차가 마련돼 있는지 확인해야 합니다. 이후 ENFORCED로 전환할 계획이라면 전체 검증 스캔의 실행 시간과 잠금 영향을 고려해 작업 일정을 수립해야 합니다.


PostgreSQL 18 NOT ENFORCED 자주 묻는 질문

NOT VALID와 NOT ENFORCED의 가장 큰 차이는 무엇인가요?

NOT VALID는 기존 데이터의 검증을 미루지만 새로 추가되거나 변경되는 데이터에는 제약 조건을 적용합니다. NOT ENFORCED는 강제 상태로 전환하기 전까지 기존 데이터와 새 데이터 모두 검사하지 않습니다.

PostgreSQL 18에서 NOT ENFORCED를 사용할 수 있는 제약 조건은 무엇인가요?

PostgreSQL 18에서는 CHECK와 외래 키(FOREIGN KEY) 제약 조건에 NOT ENFORCED를 사용할 수 있습니다. 다만 제약 조건의 강제 상태를 변경하는 기능은 현재 외래 키에 적용됩니다.

NOT ENFORCED 외래 키를 생성하면 내부 트리거도 생성되나요?

아니요. 외래 키를 NOT ENFORCED로 생성하면 PostgreSQL은 무결성 검사에 사용하는 내부 트리거 생성을 건너뜁니다. 이후 해당 제약 조건을 ENFORCED로 전환할 때 트리거가 생성됩니다.

NOT ENFORCED 제약 조건을 ENFORCED로 바꾸면 어떻게 되나요?

PostgreSQL은 전체 스캔을 실행해 모든 행이 제약 조건을 준수하는지 검증합니다. 유효하지 않은 행이 하나라도 발견되면 전환 명령은 실패합니다.

대용량 테이블에는 NOT VALID와 NOT ENFORCED 중 무엇을 사용해야 하나요?

새 데이터의 무결성을 계속 보장하면서 기존 데이터 검증만 미루려면 NOT VALID가 적합합니다. 데이터베이스 수준의 검사를 수행하지 않고 제약 조건을 메타데이터로 정의하려면 NOT ENFORCED를 검토할 수 있지만, 별도의 데이터 무결성 관리 체계가 필요합니다.


메일: salesinquiry@enterprisedb.com

Share this