2026년 07월 11일 | DBMS Error 가이드
이 글에서 다루는 내용
42P07 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42P07 duplicate table 란?
PostgreSQL 에러 코드 42P07은 이미 존재하는 테이블과 동일한 이름으로 새 테이블을 생성하려 할 때 발생하는 오류입니다. 이 에러는 CREATE TABLE 구문을 실행할 때, 동일한 스키마 내에 같은 이름의 테이블이 이미 존재하는 경우 트리거됩니다. 데이터베이스 마이그레이션 스크립트, 반복 실행되는 배포 스크립트, 또는 개발 환경에서 테이블을 수동으로 생성하는 과정에서 특히 자주 마주치는 에러입니다.
주요 발생 원인
1. 마이그레이션 또는 배포 스크립트 중복 실행
CI/CD 파이프라인이나 데이터베이스 마이그레이션 도구를 사용할 때, 동일한 CREATE TABLE 스크립트가 두 번 이상 실행되는 상황이 발생할 수 있습니다. 특히 멱등성(idempotency)을 고려하지 않고 작성된 스크립트는 처음에는 정상 동작하지만, 재실행 시 반드시 이 에러를 발생시킵니다. 운영 환경에서 배포 롤백 후 재배포하는 과정에서도 동일한 문제가 빈번히 나타납니다.
2. 동일 스키마 내 테이블 명명 충돌
여러 개발자가 동시에 작업하는 환경에서 서로 다른 기능 브랜치에서 같은 이름의 테이블을 생성하는 경우가 있습니다. 또한 레거시 테이블이 삭제되었다고 가정하고 새 테이블을 만들려 했지만, 실제로는 해당 테이블이 여전히 데이터베이스에 남아 있는 경우에도 발생합니다. 이는 팀 간 커뮤니케이션 부재 또는 데이터베이스 형상 관리 부재에서 비롯되는 경우가 많습니다.
3. ORM 또는 프레임워크의 자동 테이블 생성 로직 충돌
Django, Hibernate, SQLAlchemy 등의 ORM 프레임워크는 애플리케이션 시작 시 자동으로 테이블을 생성하려 시도하는 옵션을 제공합니다. create 또는 create-drop 같은 DDL 자동 생성 옵션이 활성화된 상태에서 이미 테이블이 존재하면 이 에러가 발생합니다. 프로덕션 환경에서 해당 설정을 검토하지 않고 애플리케이션을 재시작하는 경우 예상치 못한 에러로 이어질 수 있습니다.
해결 방법
해결책 1: IF NOT EXISTS 절 사용 (가장 권장)
CREATE TABLE 구문에 IF NOT EXISTS를 추가하면 테이블이 이미 존재하더라도 에러 없이 구문이 통과됩니다. 이 방법은 멱등성 있는 스크립트 작성의 핵심입니다.
-- 에러 발생 코드 (테이블이 이미 존재할 경우 42P07 발생)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 해결: IF NOT EXISTS 사용
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
해결책 2: 테이블 존재 여부 사전 확인 후 처리
실행 전에 테이블 존재 여부를 명시적으로 확인하고, 조건에 따라 다른 처리를 하고 싶을 때 사용합니다. information_schema 또는 pg_catalog를 활용하는 방법입니다.
-- information_schema를 이용한 사전 확인
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'users'
);
-- pg_catalog를 이용한 확인 (더 빠름)
SELECT EXISTS (
SELECT FROM pg_catalog.pg_tables
WHERE schemaname = 'public'
AND tablename = 'users'
);
-- DO 블록을 활용한 조건부 생성
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_catalog.pg_tables
WHERE schemaname = 'public'
AND tablename = 'users'
) THEN
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
RAISE NOTICE 'users 테이블이 생성되었습니다.';
ELSE
RAISE NOTICE 'users 테이블이 이미 존재합니다. 생성을 건너뜁니다.';
END IF;
END
$$;
해결책 3: 기존 테이블 삭제 후 재생성 (개발 환경 전용)
개발 또는 테스트 환경에서 테이블 구조를 완전히 초기화하고 싶은 경우에 사용합니다. 절대 운영 환경에서는 사용하지 마세요. 데이터 손실이 발생합니다.
-- 개발 환경 전용: 기존 테이블 제거 후 재생성
DROP TABLE IF EXISTS users CASCADE;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 여러 테이블을 한 번에 초기화할 경우
DROP TABLE IF EXISTS orders, order_items, users CASCADE;
해결책 4: 트랜잭션과 예외 처리 활용
애플리케이션 레벨에서 에러를 안전하게 처리하고 싶을 때, PL/pgSQL의 예외 처리를 활용할 수 있습니다.
-- 예외 처리를 통한 안전한 테이블 생성
DO $$
BEGIN
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
RAISE NOTICE 'users 테이블 생성 완료';
EXCEPTION
WHEN duplicate_table THEN
RAISE NOTICE '테이블이 이미 존재합니다: %', SQLERRM;
-- 필요한 경우 추가 처리 로직 삽입
END
$$;
예방 방법
1. 모든 DDL 스크립트에 멱등성(Idempotency) 보장
배포 스크립트나 마이그레이션 파일을 작성할 때는 반드시 몇 번을 실행하더라도 동일한 결과를 보장하는 멱등성 원칙을 따라야 합니다. CREATE TABLE IF NOT EXISTS, DROP TABLE IF EXISTS, CREATE INDEX IF NOT EXISTS 등 PostgreSQL이 제공하는 조건부 DDL 구문을 습관적으로 사용하세요. Flyway, Liquibase와 같은 데이터베이스 마이그레이션 도구를 도입하면 각 마이그레이션 파일의 실행 이력을 관리하여 중복 실행을 시스템 차원에서 방지할 수 있습니다.
-- 좋은 예: 멱등성이 보장된 마이그레이션 스크립트 패턴
CREATE TABLE IF NOT EXISTS audit_log (
id BIGSERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL,
changed_at TIMESTAMPTZ DEFAULT NOW(),
changed_by TEXT DEFAULT CURRENT_USER
);
CREATE INDEX IF NOT EXISTS idx_audit_log_table_name
ON audit_log(table_name);
CREATE INDEX IF NOT EXISTS idx_audit_log_changed_at
ON audit_log(changed_at);
2. 데이터베이스 형상 관리 도구 도입 및 네이밍 컨벤션 수립
팀 전체가 공유하는 데이터베이스 스키마 버전 관리 체계를 구축해야 합니다. Git과 연동된 Flyway나 Liquibase를 사용하면 어떤 마이그레이션이 언제 실행되었는지 추적할 수 있으며, 스키마 충돌을 사전에 감지할 수 있습니다. 또한 테이블 네이밍 컨벤션(예: 도메인 접두사 사용 payment_transactions, auth_users)을 팀 차원에서 합의하고 문서화하면 이름 충돌 가능성을 크게 줄일 수 있습니다.
관련 에러
- 42P16 (
invalid_table_definition): 테이블 정의 자체가 잘못된 경우 발생하며, 42P07과 함께 DDL 작업 시 자주 마주칩니다. - 42701 (
duplicate_column): 동일 테이블 내에 중복된 컬럼 이름을 정의하려 할 때 발생합니다. - 42P01 (
undefined_table): 존재하지 않는 테이블을 참조하려 할 때 발생하며, 42P07과 반대 상황입니다.DROP TABLE이후 참조 시 자주 나타납니다. - 23505 (
unique_violation): 테이블 자체는 아니지만 유니크 제약 조건 위반으로, DDL 및 데이터 삽입 시 함께 처리해야 할 에러입니다. - 42P06 (
duplicate_schema): 스키마 레벨에서 동일한 문제가 발생할 경우의 에러 코드로,CREATE SCHEMA IF NOT EXISTS로 예방할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.