2026년 07월 11일 | DBMS Error 가이드
이 글에서 다루는 내용
42710 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42710 duplicate object 는?
PostgreSQL 에러 코드 42710은 duplicate_object 에러로, 이미 데이터베이스 내에 동일한 이름의 객체(테이블, 인덱스, 시퀀스, 뷰, 함수, 역할 등)가 존재할 때 발생합니다. 쉽게 말해, 새로운 객체를 생성하려고 했지만 같은 이름이 이미 사용 중인 경우에 PostgreSQL이 이 에러를 반환합니다. 마이그레이션 스크립트 실행, 배포 자동화, 개발 환경 초기화 등 다양한 상황에서 자주 마주치는 에러입니다.
주요 발생 원인
- 마이그레이션 스크립트 중복 실행
마이그레이션 도구(Flyway, Liquibase, Alembic 등)를 사용하지 않고 수동으로 SQL 스크립트를 실행할 때 동일한 스크립트를 두 번 이상 실행하면 발생합니다. 특히 CI/CD 파이프라인에서 배포 단계가 반복 실행될 경우, CREATE TABLE, CREATE INDEX, CREATE SEQUENCE 등의 구문이 이미 존재하는 객체를 다시 만들려 시도하면서 이 에러가 빈번하게 발생합니다. 실무에서 가장 많이 마주치는 원인으로, 개발 초기에 체계적인 마이그레이션 관리 전략이 없을 때 특히 위험합니다.
- 동일 데이터베이스 내 객체 이름 충돌
서로 다른 팀원이 같은 이름의 인덱스나 제약 조건을 동시에 생성하거나, 테이블 이름을 명확히 규정하지 않은 채 개발을 진행하면 객체 이름이 충돌합니다. PostgreSQL에서 인덱스 이름은 스키마 범위 내에서 유일해야 하므로, 여러 테이블에 걸쳐 동일한 이름의 인덱스를 생성하려 하면 에러가 발생합니다. 대규모 팀 프로젝트에서 네이밍 컨벤션이 없을 때 자주 발생하는 시나리오입니다.
- 역할(Role) 또는 데이터베이스 재생성 시도
운영 환경 복구나 개발 환경 셋업 과정에서 CREATE ROLE, CREATE DATABASE, CREATE SCHEMA 등의 구문을 멱등성 없이 실행하면 발생합니다. 특히 도커 컨테이너 초기화 스크립트나 Ansible/Terraform 같은 인프라 자동화 도구에서 PostgreSQL 초기 설정을 매번 실행할 때, 이미 존재하는 역할이나 데이터베이스를 다시 생성하려 해서 이 에러가 트리거됩니다. 이 경우 자동화 스크립트 전체가 실패할 수 있어 특히 주의가 필요합니다.
해결 방법
1. IF NOT EXISTS 구문 사용 (가장 권장하는 방법)
PostgreSQL 9.1 이상부터 대부분의 CREATE 구문에 IF NOT EXISTS 옵션이 지원됩니다. 이미 객체가 존재하더라도 에러 없이 건너뛰도록 처리할 수 있습니다.
-- 테이블 생성 시
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 인덱스 생성 시
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
-- 시퀀스 생성 시
CREATE SEQUENCE IF NOT EXISTS order_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
-- 스키마 생성 시
CREATE SCHEMA IF NOT EXISTS analytics;
-- 역할 생성 시
CREATE ROLE IF NOT EXISTS readonly_user LOGIN PASSWORD 'securepassword';
2. 존재 여부 확인 후 생성 (DO 블록 활용)
IF NOT EXISTS를 지원하지 않는 구문(예: CREATE TYPE, CREATE EXTENSION 일부)이나 더 세밀한 제어가 필요한 경우, DO 블록을 활용하여 먼저 존재 여부를 확인하고 생성할 수 있습니다.
-- 타입(ENUM) 중복 생성 방지
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_type WHERE typname = 'order_status'
) THEN
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'completed', 'cancelled');
END IF;
END
$$;
-- 함수 중복 생성 방지 (CREATE OR REPLACE 활용)
CREATE OR REPLACE FUNCTION get_user_by_email(p_email VARCHAR)
RETURNS TABLE(id INT, username VARCHAR, email VARCHAR) AS $$
BEGIN
RETURN QUERY
SELECT u.id, u.username, u.email
FROM users u
WHERE u.email = p_email;
END;
$$ LANGUAGE plpgsql;
-- 제약 조건 중복 추가 방지
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'uq_users_username'
AND table_name = 'users'
) THEN
ALTER TABLE users ADD CONSTRAINT uq_users_username UNIQUE (username);
END IF;
END
$$;
3. 기존 객체 삭제 후 재생성 (DROP … IF EXISTS 활용)
마이그레이션이나 테스트 환경 초기화 시, 기존 객체를 완전히 새로 만들어야 하는 경우에는 DROP IF EXISTS를 사용하여 안전하게 삭제 후 재생성합니다. 단, 운영 환경에서 데이터 손실 위험이 있으므로 반드시 백업 후 사용하세요.
-- 안전한 테이블 재생성 패턴
DROP TABLE IF EXISTS temp_staging_data CASCADE;
CREATE TABLE temp_staging_data (
id BIGSERIAL PRIMARY KEY,
raw_data JSONB,
processed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 인덱스 재생성
DROP INDEX IF EXISTS idx_orders_created_at;
CREATE INDEX idx_orders_created_at ON orders(created_at DESC);
-- 역할 재생성
DROP ROLE IF EXISTS reporting_user;
CREATE ROLE reporting_user LOGIN PASSWORD 'newpassword' CONNECTION LIMIT 10;
4. 현재 존재하는 객체 확인 쿼리
에러 발생 시 어떤 객체가 이미 존재하는지 확인하는 진단 쿼리입니다.
-- 현재 스키마의 모든 테이블 확인
SELECT schemaname, tablename, tableowner
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY schemaname, tablename;
-- 특정 이름의 인덱스 존재 여부 확인
SELECT indexname, tablename, indexdef
FROM pg_indexes
WHERE schemaname = 'public'
AND indexname = 'idx_users_email';
-- 역할 존재 여부 확인
SELECT rolname, rolcanlogin, rolcreatedb
FROM pg_roles
WHERE rolname = 'readonly_user';
-- 시퀀스 존재 여부 확인
SELECT sequence_schema, sequence_name
FROM information_schema.sequences
WHERE sequence_name = 'order_id_seq';
-- 타입 존재 여부 확인
SELECT typname, typtype
FROM pg_type
WHERE typname = 'order_status'
AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public');
예방 방법
- 모든 DDL 스크립트에
IF NOT EXISTS/CREATE OR REPLACE패턴 적용 의무화
팀 내 코딩 표준으로 모든 DDL(Data Definition Language) 구문에 멱등성(Idempotency)을 보장하는 패턴을 적용하도록 규정하세요. CREATE TABLE, CREATE INDEX, CREATE SEQUENCE는 항상 IF NOT EXISTS를, 함수와 뷰는 CREATE OR REPLACE를 사용하는 것을 팀 규칙으로 정립하면 마이그레이션 스크립트를 몇 번을 실행하더라도 안전합니다. Flyway나 Liquibase 같은 마이그레이션 도구를 도입하면 버전 관리와 중복 실행 방지가 자동으로 처리되어 더욱 효과적입니다.
- 객체 네이밍 컨벤션 수립 및 자동화된 검증 도입
PostgreSQL에서 인덱스, 제약 조건, 시퀀스 이름은 스키마 내에서 전역적으로 유일해야 합니다. 테이블명_컬럼명_타입 형식(예: idx_users_email, uq_orders_order_number, fk_orders_user_id)과 같이 명확한 네이밍 컨벤션을 문서화하고, PR(Pull Request) 리뷰 과정에서 이름 충돌 여부를 확인하는 체크리스트를 운영하세요. 가능하다면 CI 파이프라인에 스키마 린팅 도구를 도입하여 배포 전 자동으로 이름 충돌을 감지하는 것을 권장합니다.
관련 에러
- 42701
duplicate_column: 테이블에 이미 존재하는 컬럼을ALTER TABLE ... ADD COLUMN으로 추가하려 할 때 발생합니다.IF NOT EXISTS로 해결 가능합니다 (PostgreSQL 9.6+). - 42P07
duplicate_table:CREATE TABLE시 동일 스키마 내에 같은 이름의 테이블이 이미 존재할 때 발생하는 더 구체적인 에러입니다. - 23505
unique_violation: 유니크 제약 조건이 걸린 컬럼에 중복 데이터를 삽입하려 할 때 발생하는 에러로, 42710과 혼동하지 않도록 주의하세요. 이쪽은 DDL이 아닌 DML(INSERT/UPDATE) 과정에서 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.