2026년 06월 27일 | DBMS Error 가이드
이 글에서 다루는 내용
2BP01 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2BP01 dependent objects still exist 는?
PostgreSQL 에러 코드 2BP01 (dependent_objects_still_exist) 는 특정 데이터베이스 객체(테이블, 뷰, 함수, 스키마 등)를 삭제하려 할 때, 해당 객체에 의존하는 다른 객체가 아직 존재하는 경우 발생하는 에러입니다. 예를 들어, 어떤 테이블을 참조하는 뷰(View)나 외래 키(Foreign Key)가 남아 있는 상태에서 해당 테이블을 DROP 하려 하면 PostgreSQL은 이 에러를 발생시켜 데이터 무결성을 보호합니다. 이 에러는 PostgreSQL의 의존성 추적 메커니즘이 정상적으로 동작하고 있다는 신호이며, 무분별한 객체 삭제로 인한 시스템 장애를 방지하기 위한 안전장치입니다.
주요 발생 원인
1. 뷰(View) 또는 Materialized View가 해당 테이블/컬럼을 참조하는 경우
가장 흔하게 발생하는 원인으로, 특정 테이블이나 컬럼을 기반으로 생성된 뷰가 존재하는 상태에서 원본 객체를 삭제하려 할 때 발생합니다. 뷰는 원본 테이블의 구조에 강하게 의존하기 때문에, PostgreSQL은 원본 객체가 삭제되면 뷰도 무효화된다는 것을 인지하고 삭제를 차단합니다. 실무에서는 데이터 마트나 리포팅 레이어에서 복수의 뷰가 체인 형태로 연결되어 있어, 한 테이블만 삭제하려 해도 수십 개의 의존성이 걸려 있는 경우가 빈번합니다.
2. 외래 키(Foreign Key) 제약 조건이 존재하는 경우
부모 테이블(참조되는 테이블)을 삭제하려 할 때, 자식 테이블에 외래 키 제약 조건이 설정되어 있으면 이 에러가 발생합니다. 외래 키는 두 테이블 간의 참조 무결성을 보장하는 핵심 메커니즘이므로, PostgreSQL은 부모 테이블이 사라지면 자식 테이블의 데이터 정합성이 깨진다는 이유로 삭제를 허용하지 않습니다. 특히 ORM(Hibernate, SQLAlchemy 등)을 통해 자동 생성된 스키마에서는 외래 키가 암묵적으로 생성되어 있어, DBA가 직접 파악하지 못한 상태로 운영되는 경우가 많습니다.
3. 함수(Function), 트리거(Trigger), 또는 타입(Type) 의존성
사용자 정의 함수나 프로시저가 특정 테이블 타입, 복합 타입(Composite Type), 또는 도메인(Domain)에 의존하고 있을 때도 이 에러가 발생합니다. 트리거 함수가 연결된 테이블을 삭제하거나, 복합 타입을 기반으로 한 함수를 삭제하려 할 때도 마찬가지입니다. 이 경우는 의존 관계가 눈에 잘 보이지 않아 원인 파악에 시간이 더 걸리는 경향이 있으므로, 시스템 카탈로그를 통한 의존성 조회가 필수적입니다.
해결 방법
방법 1: CASCADE 옵션 사용 (즉각적인 해결)
가장 빠른 해결책은 DROP 명령에 CASCADE 옵션을 추가하는 것입니다. 이 옵션은 대상 객체와 함께 의존하는 모든 객체를 연쇄적으로 삭제합니다. 단, 프로덕션 환경에서는 반드시 어떤 객체들이 함께 삭제되는지 사전에 확인한 후 사용해야 합니다.
-- 뷰가 참조 중인 테이블을 CASCADE로 삭제
DROP TABLE orders CASCADE;
-- 스키마와 그 안의 모든 객체를 CASCADE로 삭제
DROP SCHEMA reporting CASCADE;
-- 함수를 CASCADE로 삭제
DROP FUNCTION calculate_total(integer) CASCADE;
-- Materialized View CASCADE 삭제
DROP MATERIALIZED VIEW mv_sales_summary CASCADE;
방법 2: 의존 객체를 먼저 확인하고 순서대로 삭제
CASCADE 는 편리하지만 위험할 수 있습니다. 아래 쿼리로 의존성을 먼저 파악한 후, 안전하게 순서대로 삭제하는 것이 Best Practice입니다.
-- 특정 테이블에 의존하는 모든 객체 조회
SELECT
dependent_ns.nspname AS dependent_schema,
dependent_view.relname AS dependent_object,
dependent_view.relkind AS object_type,
source_ns.nspname AS source_schema,
source_table.relname AS source_table
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class AS dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_class AS source_table ON pg_depend.refobjid = source_table.oid
JOIN pg_namespace AS dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace AS source_ns ON source_ns.oid = source_table.relnamespace
WHERE source_table.relname = 'orders' -- 삭제하려는 테이블명
AND source_ns.nspname = 'public'
AND dependent_view.relname != source_table.relname;
-- 외래 키 제약 조건 확인
SELECT
tc.table_schema,
tc.table_name,
kcu.column_name,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name,
tc.constraint_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND ccu.table_name = 'orders'; -- 부모 테이블명
-- 의존성 확인 후 안전하게 순서대로 삭제
-- 1단계: 자식 테이블의 외래 키 제약 조건 먼저 제거
ALTER TABLE order_items DROP CONSTRAINT fk_order_items_orders;
-- 2단계: 뷰 삭제
DROP VIEW v_order_summary;
DROP VIEW v_daily_orders;
-- 3단계: 원본 테이블 삭제
DROP TABLE orders;
방법 3: pg_depend 카탈로그로 정밀 의존성 분석
-- pg_depend를 활용한 전체 의존성 트리 조회
SELECT
classid::regclass AS "의존 객체 카탈로그",
objid::regclass AS "의존 객체",
deptype AS "의존 타입",
refclassid::regclass AS "참조 카탈로그",
refobjid::regclass AS "참조 대상 객체"
FROM pg_depend
WHERE refobjid = 'public.orders'::regclass
AND deptype IN ('n', 'a', 'i');
-- 트리거 의존성 확인
SELECT
trigger_name,
event_object_table,
action_statement
FROM information_schema.triggers
WHERE event_object_table = 'orders';
예방 방법
1. 삭제 전 의존성 체크를 표준 운영 절차(SOP)로 수립하기
어떤 데이터베이스 객체를 삭제하기 전에는 반드시 위에서 소개한 의존성 조회 쿼리를 실행하고, 그 결과를 운영 일지에 기록하는 것을 팀 표준으로 만들어야 합니다. 특히 프로덕션 환경에서는 DROP ... CASCADE 명령을 단독으로 실행하는 것을 금지하고, 반드시 트랜잭션(BEGIN / ROLLBACK / COMMIT) 안에서 실행하여 실수로 삭제된 경우 롤백할 수 있도록 해야 합니다.
-- 안전한 삭제 패턴: 트랜잭션 활용
BEGIN;
-- 의존성 확인
SELECT objid::regclass FROM pg_depend WHERE refobjid = 'orders'::regclass;
-- 삭제 실행 (먼저 ROLLBACK으로 테스트)
DROP TABLE orders CASCADE;
-- 결과 검토 후 확정
-- ROLLBACK; -- 문제가 있으면 롤백
COMMIT; -- 이상 없으면 커밋
2. 스키마 변경 이력 관리와 마이그레이션 도구 활용
Flyway, Liquibase, Alembic 같은 데이터베이스 마이그레이션 도구를 도입하면 스키마 변경 이력이 코드로 관리되며, 의존 관계를 고려한 순서로 마이그레이션 스크립트를 작성하게 됩니다. 이를 통해 아무도 직접 프로덕션 DB에 즉흥적인 DROP 명령을 실행하지 못하도록 프로세스를 통제할 수 있으며, 코드 리뷰 단계에서 의존성 문제를 사전에 발견할 수 있습니다.
관련 에러
- 0A000 (feature_not_supported): 특정 버전에서 지원되지 않는 DROP 옵션 사용 시 발생하며, 2BP01과 혼동되는 경우가 있습니다.
- 23503 (foreign_key_violation): 외래 키 위반 에러로, 2BP01이 DDL 레벨의 의존성 에러라면 23503은 DML 레벨(INSERT/UPDATE/DELETE)에서 발생하는 참조 무결성 에러입니다. 두 에러 모두 외래 키와 밀접한 관련이 있습니다.
- 42P01 (undefined_table): 이미 삭제된 객체를 참조하려 할 때 발생하는 에러로, 2BP01 처리 후 의존 객체가 올바르게 재생성되지 않은 경우 연속적으로 발생할 수 있습니다.
- 55006 (object_in_use): 객체가 현재 사용 중인 상태에서 삭제를 시도할 때 발생하며, 2BP01과 함께 DROP 명령 실패의 주요 원인 중 하나입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.