2026년 10월 10일 | DBMS Error 가이드
이 글에서 다루는 내용
0P000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
0P000 invalid role specification 는?
PostgreSQL 에러 코드 0P000은 invalid role specification, 즉 잘못된 역할(Role) 지정 오류를 의미합니다. 이 에러는 GRANT, REVOKE, SET ROLE, ALTER TABLE OWNER TO, CREATE DATABASE ... OWNER 등의 SQL 구문에서 존재하지 않거나 잘못 표기된 역할(Role) 이름을 참조할 때 발생합니다. 특히 대소문자 혼용, 오타, 또는 이미 삭제된 역할을 참조하는 경우에 자주 마주치게 되며, 권한 관리 자동화 스크립트나 배포 파이프라인에서 흔히 발생하는 문제입니다.
주요 발생 원인
1. 존재하지 않는 역할(Role) 이름 참조
가장 빈번한 원인으로, 오타나 잘못된 이름으로 역할을 지정하는 경우입니다. PostgreSQL은 역할 이름을 정확하게 일치시켜야 하며, 해당 역할이 데이터베이스 클러스터에 존재하지 않으면 즉시 이 에러를 발생시킵니다. 배포 스크립트에서 역할 생성 단계를 누락하거나, 개발 환경과 운영 환경의 역할 구성이 다를 때 특히 자주 발생합니다.
2. 대소문자 처리 오류로 인한 역할 불일치
PostgreSQL에서 역할 이름은 기본적으로 소문자로 저장됩니다. 따옴표 없이 생성된 역할 MyAdmin은 내부적으로 myadmin으로 저장되는데, 나중에 큰따옴표를 사용하여 "MyAdmin"으로 참조하면 해당 역할을 찾지 못합니다. 반대로 큰따옴표로 생성한 "MyAdmin"은 정확히 "MyAdmin"으로만 참조해야 합니다. 이 차이를 이해하지 못하면 동일해 보이는 이름인데도 역할을 찾지 못하는 혼란스러운 상황이 발생합니다.
3. 역할 삭제 후 잔존하는 참조
운영 중에 특정 역할을 DROP ROLE로 삭제했지만, 해당 역할을 참조하는 스크립트, 뷰 정의의 SECURITY DEFINER, 또는 애플리케이션 설정이 남아 있는 경우 이 에러가 발생합니다. 특히 여러 데이터베이스에 걸쳐 권한이 부여된 역할을 삭제할 때 모든 참조를 정리하지 않으면, 추후 재연결이나 권한 재설정 시점에 에러가 표면화됩니다.
해결 방법
원인 1: 존재하지 않는 역할 확인 및 생성
먼저 현재 클러스터에 존재하는 역할 목록을 조회합니다.
-- 클러스터의 모든 역할 목록 조회
SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolcanlogin
FROM pg_catalog.pg_roles
ORDER BY rolname;
-- 특정 역할이 존재하는지 확인
SELECT EXISTS (
SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = 'app_user'
) AS role_exists;
역할이 없다면 생성 후 권한을 부여합니다.
-- 역할 생성 (로그인 가능한 사용자 역할)
CREATE ROLE app_user WITH LOGIN PASSWORD 'secure_password123';
-- 역할 생성 (그룹 역할, 로그인 불가)
CREATE ROLE readonly_group NOLOGIN;
-- 이제 권한 부여 가능
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_user;
원인 2: 대소문자 문제 해결
-- 잘못된 예시 (따옴표 없이 생성된 역할을 큰따옴표로 참조)
CREATE ROLE MyAdmin WITH LOGIN PASSWORD 'pass'; -- 실제 저장: myadmin
-- 아래는 에러 발생: role "MyAdmin" does not exist
GRANT ALL PRIVILEGES ON DATABASE mydb TO "MyAdmin"; -- 0P000 에러!
-- 올바른 참조 방법 (소문자로 저장되었으므로)
GRANT ALL PRIVILEGES ON DATABASE mydb TO myadmin;
-- 또는 처음부터 큰따옴표로 대소문자를 명시적으로 보존하여 생성
CREATE ROLE "MyAdmin" WITH LOGIN PASSWORD 'pass'; -- 정확히 "MyAdmin"으로 저장
-- 큰따옴표로 생성한 경우 반드시 큰따옴표로 참조
GRANT ALL PRIVILEGES ON DATABASE mydb TO "MyAdmin";
-- 현재 정확한 역할 이름 확인 (대소문자 포함)
SELECT rolname FROM pg_roles WHERE rolname ILIKE '%admin%';
원인 3: 삭제된 역할 참조 정리
-- 특정 역할에 부여된 권한 전체 조회 (삭제 전 참조 확인)
SELECT grantee, table_catalog, table_schema, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'old_role';
-- 역할을 삭제하기 전에 해당 역할이 소유한 객체 재할당
REASSIGN OWNED BY old_role TO postgres;
-- 해당 역할에 부여된 모든 권한 회수
DROP OWNED BY old_role;
-- 이제 안전하게 역할 삭제 가능
DROP ROLE old_role;
-- IF EXISTS를 사용하면 역할이 없어도 에러 방지 (스크립트에서 유용)
DROP ROLE IF EXISTS old_role;
-- 역할 삭제 후 관련 GRANT 구문도 업데이트
-- 새로운 역할로 권한 이전
CREATE ROLE new_role WITH LOGIN PASSWORD 'newpassword';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO new_role;
실전 안전 패턴: 역할 존재 확인 후 작업
-- DO 블록을 활용한 안전한 역할 생성 패턴
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_readonly'
) THEN
CREATE ROLE app_readonly WITH NOLOGIN;
RAISE NOTICE 'Role app_readonly created successfully.';
ELSE
RAISE NOTICE 'Role app_readonly already exists. Skipping creation.';
END IF;
END
$$;
-- 역할 생성 후 권한 부여
GRANT USAGE ON SCHEMA public TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO app_readonly;
예방 방법
1. 역할 관리 스크립트에 IF NOT EXISTS / IF EXISTS 패턴 적용
모든 배포 스크립트나 마이그레이션 파일에서 역할을 생성하거나 삭제할 때 반드시 존재 여부를 확인하는 패턴을 사용하세요. PostgreSQL 9.x 이하에서는 CREATE ROLE IF NOT EXISTS 문법을 지원하지 않으므로, 위의 DO $$ ... $$ 블록 패턴을 표준으로 채택하는 것이 좋습니다. PostgreSQL 16부터는 CREATE ROLE IF NOT EXISTS가 공식 지원되므로 버전에 맞게 활용하세요. 이 습관만으로도 환경 간 차이나 중복 실행으로 인한 배포 실패를 대부분 방지할 수 있습니다.
2. 역할 이름 명명 규칙(Naming Convention) 통일 및 문서화
팀 내에서 역할 이름을 항상 소문자와 언더스코어만 사용하도록 컨벤션을 정하면 대소문자 혼용 문제를 근본적으로 차단할 수 있습니다. 예: app_user, readonly_group, data_admin 형태로 통일합니다. 또한 pg_roles 뷰를 주기적으로 조회하여 역할 현황을 문서화하고, IaC(Infrastructure as Code) 도구(Terraform, Ansible 등)로 역할 상태를 코드로 관리하면 환경 간 불일치를 사전에 방지할 수 있습니다.
관련 에러
42501insufficient_privilege: 역할은 존재하지만 해당 역할에 필요한 권한이 없을 때 발생합니다.0P000과 함께 권한 관리 문제를 진단할 때 자주 함께 등장합니다.42939reserved_name: 시스템 예약어를 역할 이름으로 사용하려 할 때 발생하며, 역할 생성 시 이름 충돌 문제와 관련됩니다.2BP01dependent_objects_still_exist:DROP ROLE시 해당 역할이 소유한 객체나 부여된 권한이 남아 있을 때 발생합니다.REASSIGN OWNED와DROP OWNED실행 없이 역할을 삭제하려 할 때0P000의 후속 작업 중 만나게 되는 에러입니다.28000invalid_authorization_specification: 연결 시 지정한 역할로 인증에 실패했을 때 발생하며, 역할 관련 에러 중 가장 접속 단계에서 자주 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.