2026년 06월 26일 | DBMS Error 가이드
이 글에서 다루는 내용
2B000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2B000 dependent privilege descriptors still exist 는?
PostgreSQL 에러 코드 2B000은 dependent privilege descriptors still exist라는 메시지와 함께 발생하며, 특정 객체(사용자, 역할, 스키마 등)를 삭제하려 할 때 해당 객체에 종속된 권한 디스크립터가 아직 존재하는 경우 나타납니다. 쉽게 말해, 삭제하려는 대상이 다른 객체와 권한 관계로 얽혀 있어 PostgreSQL이 안전을 위해 삭제를 거부하는 상황입니다. 이 에러는 주로 DROP ROLE, DROP USER, DROP SCHEMA, REVOKE 등의 명령 실행 시 발생하며, 권한 의존성을 먼저 해소하지 않으면 해결되지 않습니다.
주요 발생 원인
- 역할(Role) 또는 사용자(User)에게 부여된 권한이 아직 남아 있는 경우
가장 흔한 원인입니다. 특정 역할을 삭제하려 할 때, 그 역할이 다른 데이터베이스 객체(테이블, 시퀀스, 함수, 스키마 등)에 대한 권한을 아직 보유하고 있으면 PostgreSQL은 삭제를 허용하지 않습니다. GRANT 명령으로 부여된 모든 권한을 REVOKE로 먼저 회수해야만 해당 역할을 정상적으로 삭제할 수 있습니다.
- 역할이 다른 역할의 멤버로 소속되어 있는 경우
PostgreSQL에서는 역할 간 계층 구조(Role Membership)가 가능합니다. 삭제 대상 역할이 다른 역할에 소속되어 있거나, 반대로 다른 역할이 삭제 대상 역할 내에 멤버로 포함된 경우에도 이 에러가 발생합니다. 이 경우 REVOKE role FROM other_role 또는 REVOKE other_role FROM role 구문으로 멤버십 관계를 먼저 해제해야 합니다.
- 객체의 소유자(Owner)가 삭제 대상 역할로 설정되어 있는 경우
삭제하려는 역할이 특정 데이터베이스, 테이블, 스키마, 함수 등의 소유자로 지정되어 있는 경우, 소유권 이전 없이는 삭제가 불가능합니다. 소유자는 해당 객체에 대한 묵시적 권한 디스크립터를 보유하므로, ALTER TABLE ... OWNER TO, REASSIGN OWNED BY 등을 통해 소유권을 다른 역할로 먼저 이전해야 합니다.
해결 방법
원인 1: 부여된 권한 회수
먼저 해당 역할이 보유한 권한 목록을 조회합니다.
-- 특정 역할에 부여된 테이블 권한 조회
SELECT grantee, table_schema, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'target_role';
-- 특정 역할에 부여된 스키마 권한 조회
SELECT grantor, grantee, object_schema, object_name, object_type, privilege_type
FROM information_schema.role_usage_grants
WHERE grantee = 'target_role';
권한 조회 후 아래와 같이 회수합니다.
-- 특정 테이블에 대한 권한 회수
REVOKE ALL PRIVILEGES ON TABLE public.orders FROM target_role;
-- 특정 스키마에 대한 권한 회수
REVOKE ALL PRIVILEGES ON SCHEMA sales FROM target_role;
-- 데이터베이스 전체에 대한 권한 회수
REVOKE ALL PRIVILEGES ON DATABASE mydb FROM target_role;
-- 모든 테이블에 대한 권한 일괄 회수
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM target_role;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM target_role;
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM target_role;
원인 2: 역할 멤버십 해제
-- target_role이 속해 있는 상위 역할 확인
SELECT rolname, member::regrole
FROM pg_auth_members
JOIN pg_roles ON pg_roles.oid = roleid
WHERE member = (SELECT oid FROM pg_roles WHERE rolname = 'target_role');
-- 멤버십 관계 해제
REVOKE target_role FROM parent_role;
-- 반대 방향 (다른 역할이 target_role 안에 포함된 경우)
REVOKE child_role FROM target_role;
원인 3: 소유권 이전 후 삭제
-- target_role이 소유한 모든 객체를 new_owner에게 일괄 이전
REASSIGN OWNED BY target_role TO new_owner;
-- target_role이 소유하거나 권한이 부여된 객체를 모두 정리 (삭제 포함)
DROP OWNED BY target_role;
-- 이후 역할 삭제
DROP ROLE target_role;
종합 해결 순서 (권장)
-- Step 1: 소유 객체 이전
REASSIGN OWNED BY target_role TO postgres;
-- Step 2: 남은 권한 및 의존 객체 정리
DROP OWNED BY target_role;
-- Step 3: 역할 삭제
DROP ROLE IF EXISTS target_role;
> 주의: DROP OWNED BY는 해당 역할이 소유한 객체를 실제로 삭제하므로, 실행 전 반드시 REASSIGN OWNED BY로 소유권을 이전하거나, 삭제해도 무방한 객체인지 확인하세요.
예방 방법
- 역할 삭제 전 의존성 점검 스크립트 운영화
운영 환경에서 역할을 삭제하기 전에 아래 쿼리를 표준 점검 절차로 활용하세요. 이 쿼리를 배포 스크립트나 DBA 체크리스트에 포함시키면 실수로 인한 에러 발생을 크게 줄일 수 있습니다.
“`sql
— 역할 삭제 전 의존성 종합 점검
SELECT ‘TABLE GRANT’ AS type, grantee, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = ‘target_role’
UNION ALL
SELECT ‘MEMBERSHIP’, r.rolname, m.member::regrole::text, ‘MEMBER’
FROM pg_auth_members m
JOIN pg_roles r ON r.oid = m.roleid
WHERE m.member = (SELECT oid FROM pg_roles WHERE rolname = ‘target_role’);
“`
- 권한 관리 정책 문서화 및 역할 설계 표준화
처음부터 권한 구조를 계층적으로 설계하고 문서화해두면 나중에 역할을 삭제하거나 변경할 때 의존성 파악이 훨씬 쉬워집니다. 예를 들어, 직접 사용자에게 권한을 부여하기보다는 역할 그룹(readonly_role, readwrite_role 등)을 만들어 사용자는 해당 역할 그룹에만 소속되도록 표준화하면, 개별 사용자 삭제 시 권한 의존성 문제가 발생할 가능성이 현저히 줄어듭니다.
관련 에러
42501(insufficient_privilege): 권한이 없는 작업을 시도할 때 발생하며, 권한 관련 에러 중 가장 빈번하게 접하는 에러입니다.2B000과 함께 권한 관리 구조를 점검할 때 자주 연관됩니다.2BP01(dependent_objects_still_exist):DROP명령 시 종속 객체가 존재할 때 발생합니다.2B000과 매우 유사한 상황이며,CASCADE옵션 사용 여부와 함께 검토해야 합니다.55006(object_in_use): 삭제하려는 객체가 현재 다른 세션에서 사용 중일 때 발생하며, 권한 에러와 복합적으로 나타나는 경우도 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.