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

42P04
2026년 07월 10일 | DBMS Error 가이드

이 글에서 다루는 내용

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

42P04 duplicate database 는?

PostgreSQL 에러 코드 42P04는 duplicate_database라는 이름으로 정의되어 있으며, 이미 존재하는 데이터베이스와 동일한 이름으로 새 데이터베이스를 생성하려 할 때 발생합니다. 예를 들어 CREATE DATABASE mydb; 명령을 실행했을 때 PostgreSQL 클러스터 내에 mydb라는 데이터베이스가 이미 존재한다면 즉시 이 에러가 반환됩니다. 이 에러는 데이터 손실을 방지하는 안전장치로 작동하지만, 자동화 스크립트나 배포 파이프라인에서 예상치 못하게 발생하면 전체 프로세스를 중단시킬 수 있어 사전에 철저히 대비해야 합니다.


주요 발생 원인

1. 자동화 스크립트 또는 CI/CD 파이프라인의 중복 실행

가장 흔한 원인은 데이터베이스 초기화 스크립트가 멱등성(idempotency) 없이 작성된 경우입니다. 배포 파이프라인에서 동일한 스크립트가 두 번 이상 실행되거나, 실패 후 재시도 로직이 작동할 때 이미 생성된 데이터베이스에 대해 CREATE DATABASE 명령이 다시 호출되어 에러가 발생합니다. 특히 Kubernetes 환경이나 Terraform 같은 IaC 도구와 PostgreSQL을 함께 사용할 때 이 문제가 자주 나타납니다.

2. 데이터베이스 이름 충돌 (대소문자 및 인코딩 문제 포함)

PostgreSQL에서 데이터베이스 이름은 기본적으로 대소문자를 구분하지 않고 소문자로 저장됩니다. MyDB, mydb, MYDB는 모두 동일한 이름으로 인식되어, 개발자가 서로 다른 이름이라고 착각하고 생성 시도 시 42P04 에러가 발생합니다. 또한 팀 내 네이밍 컨벤션이 명확하지 않으면 서로 다른 팀원이 비슷한 이름의 데이터베이스를 생성하려다 충돌이 생기는 경우도 있습니다.

3. 백업 복원 또는 마이그레이션 작업 중 발생

데이터베이스를 다른 서버로 마이그레이션하거나 pg_dump / pg_restore를 사용한 복원 작업 시, 타겟 서버에 동일한 이름의 데이터베이스가 이미 존재하면 이 에러가 발생합니다. 특히 createdb 명령이나 pg_restore -C 옵션을 사용할 때 타겟 환경을 충분히 확인하지 않으면 복원 작업 전체가 실패로 끝날 수 있습니다.


해결 방법

원인 1: 스크립트 중복 실행 방지 — IF NOT EXISTS 사용

PostgreSQL 9.3 이상부터는 CREATE DATABASE 문에서 직접 IF NOT EXISTS를 지원하지 않습니다. 대신 DO 블록 또는 쉘 스크립트 레벨에서 존재 여부를 확인하고 조건부로 실행해야 합니다.

-- pg_database 시스템 카탈로그를 활용한 조건부 생성
DO $$
BEGIN
  IF NOT EXISTS (
    SELECT FROM pg_database WHERE datname = 'mydb'
  ) THEN
    PERFORM dblink_exec('dbname=postgres', 'CREATE DATABASE mydb');
  END IF;
END
$$;

혹은 쉘 스크립트에서 psql을 활용하는 방식도 널리 사용됩니다:

-- 데이터베이스가 없을 때만 생성하는 쉘 친화적인 방법
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (
  SELECT FROM pg_database WHERE datname = 'mydb'
)\gexec

위 방법은 \gexec 메타 커맨드를 활용해 SELECT 결과를 SQL로 실행하는 실무에서 자주 쓰이는 패턴입니다.


원인 2: 이름 충돌 확인 및 정리

먼저 현재 클러스터에 존재하는 데이터베이스 목록을 확인합니다.

-- 현재 클러스터의 모든 데이터베이스 조회
SELECT datname, datcollate, datctype, pg_encoding_to_char(encoding) AS encoding
FROM pg_database
WHERE datistemplate = false
ORDER BY datname;

불필요하게 중복된 데이터베이스가 있다면 정리 후 원하는 이름으로 재생성합니다.

-- 기존 데이터베이스 삭제 (연결 종료 후 실행)
-- 주의: 실행 전 반드시 백업 확인!
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'old_mydb' AND pid <> pg_backend_pid();

DROP DATABASE IF EXISTS old_mydb;

CREATE DATABASE mydb
  WITH OWNER = myuser
  ENCODING = 'UTF8'
  LC_COLLATE = 'ko_KR.UTF-8'
  LC_CTYPE = 'ko_KR.UTF-8'
  TEMPLATE = template0;

원인 3: 백업 복원 시 처리 방법

pg_restore를 사용할 때는 -C 옵션 사용 전에 타겟 데이터베이스 존재 여부를 반드시 확인해야 합니다.

-- 복원 전 타겟 데이터베이스 존재 여부 확인
SELECT datname FROM pg_database WHERE datname = 'restore_target_db';

존재한다면 기존 DB를 삭제하거나 이름을 변경한 후 복원합니다.

-- 기존 데이터베이스 이름 변경 후 복원하는 안전한 방법
ALTER DATABASE restore_target_db RENAME TO restore_target_db_backup_20240101;

-- 이후 pg_restore 실행 (쉘 명령)
-- pg_restore -U postgres -d postgres -C backup.dump

예방 방법

1. 모든 데이터베이스 생성 스크립트에 멱등성 보장 로직 추가

실무에서는 데이터베이스 초기화 스크립트를 작성할 때 항상 pg_database 카탈로그를 조회하여 존재 여부를 확인하는 로직을 표준으로 삼아야 합니다. Ansible, Terraform의 postgresql_db 리소스, 또는 Flyway 같은 마이그레이션 도구를 활용하면 이러한 멱등성을 프레임워크 수준에서 보장할 수 있습니다. CI/CD 파이프라인에서는 데이터베이스 생성 단계를 별도의 조건부 스텝으로 분리하고, 실행 전 항상 현황을 점검하는 프리플라이트 체크를 습관화하세요.

2. 데이터베이스 이름 네이밍 컨벤션과 중앙 레지스트리 관리

팀 또는 조직 전체에서 데이터베이스 이름에 대한 명확한 네이밍 컨벤션(예: {환경}_{서비스명}_{용도} 형태)을 정의하고, Confluence나 내부 위키에 현재 운영 중인 데이터베이스 목록을 중앙에서 관리하는 레지스트리를 유지하세요. 새 데이터베이스 생성 요청은 반드시 DBA 또는 인프라 팀의 승인을 거치도록 프로세스를 수립하면 충돌을 사전에 방지할 수 있습니다. PostgreSQL의 pg_database 카탈로그를 주기적으로 스캔하여 레지스트리와 비교하는 감사 스크립트를 자동화하면 더욱 효과적입니다.


관련 에러

  • 42P07 duplicate_table: 동일 스키마 내에서 이미 존재하는 테이블 이름으로 CREATE TABLE을 시도할 때 발생합니다. CREATE TABLE IF NOT EXISTS 구문으로 예방 가능합니다.
  • 42P06 duplicate_schema: 동일한 이름의 스키마를 중복 생성할 때 발생하며, CREATE SCHEMA IF NOT EXISTS로 대응합니다.
  • 23505 unique_violation: 유니크 제약 조건 위반으로, 데이터 레벨에서의 중복을 의미하며 42P04와 개념적으로 유사한 중복 에러 계열입니다.
  • 42P05 duplicate_cursor: 동일 트랜잭션 내에서 같은 이름의 커서를 중복 선언할 때 발생합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기