2026년 07월 09일 | DBMS Error 가이드
이 글에서 다루는 내용
42701 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42701 duplicate column 는?
PostgreSQL 에러 코드 42701 (duplicate_column) 은 테이블 생성(CREATE TABLE), 컬럼 추가(ALTER TABLE ... ADD COLUMN), 또는 쿼리의 결과 집합에서 동일한 컬럼 이름이 두 번 이상 정의될 때 발생합니다. 데이터베이스 엔진은 하나의 테이블이나 결과 집합 내에서 컬럼 이름의 유일성을 보장해야 하므로, 중복된 이름이 감지되는 즉시 작업을 중단하고 이 에러를 반환합니다. 주로 대규모 DDL 스크립트 작업, 마이그레이션, 또는 여러 테이블을 JOIN할 때 실수로 같은 컬럼명을 지정하는 상황에서 자주 목격됩니다.
주요 발생 원인
1. ALTER TABLE로 이미 존재하는 컬럼을 다시 추가하려는 경우
가장 빈번하게 발생하는 원인입니다. 마이그레이션 스크립트를 반복 실행하거나, 다른 개발자가 이미 추가한 컬럼을 모르고 동일 컬럼 추가 구문을 작성할 때 발생합니다. 특히 CI/CD 파이프라인에서 멱등성(idempotency)을 고려하지 않고 마이그레이션 스크립트를 작성하면 운영 환경에서 치명적인 장애로 이어질 수 있습니다.
2. CREATE TABLE 구문에서 동일한 컬럼명을 중복 정의한 경우
테이블 정의 DDL을 직접 작성하거나 ORM이 생성한 스크립트를 수동으로 편집할 때, 실수로 같은 이름의 컬럼을 두 번 선언하는 경우입니다. 컬럼 수가 많은 복잡한 테이블일수록 육안으로 검토하기 어렵기 때문에 발생 빈도가 높아집니다. 자동 생성된 DDL 스크립트를 그대로 복붙하거나 복사·수정 과정에서 발생하는 실수가 대표적입니다.
3. CREATE TABLE AS 또는 서브쿼리에서 결과 컬럼 이름이 충돌하는 경우
두 개 이상의 테이블을 JOIN하거나 UNION 등으로 결과를 조합할 때, 서로 다른 테이블에 동일한 이름의 컬럼이 존재하면 결과 집합에서 컬럼 이름이 충돌합니다. CREATE TABLE new_table AS SELECT ... 형태로 새 테이블을 만들 때 이 문제가 표면으로 드러나며, 특히 SELECT *를 사용하면서 두 테이블 모두 id, created_at 같은 공통 컬럼을 가질 경우 바로 이 에러가 발생합니다.
해결 방법
원인 1 해결: IF NOT EXISTS 조건 사용
PostgreSQL 9.6 이상부터는 ADD COLUMN IF NOT EXISTS 구문을 지원합니다. 마이그레이션 스크립트의 멱등성을 보장하는 가장 간단하고 안전한 방법입니다.
-- 문제 발생 코드
ALTER TABLE users ADD COLUMN email VARCHAR(255);
-- 이미 email 컬럼이 존재한다면 42701 에러 발생
-- 해결 방법: IF NOT EXISTS 사용
ALTER TABLE users ADD COLUMN IF NOT EXISTS email VARCHAR(255);
-- 실행 전 컬럼 존재 여부를 직접 확인하는 방법
SELECT column_name
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users'
AND column_name = 'email';
-- DO 블록으로 동적 처리하는 방법 (구버전 호환)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users'
AND column_name = 'email'
) THEN
ALTER TABLE users ADD COLUMN email VARCHAR(255);
END IF;
END;
$$;
원인 2 해결: CREATE TABLE DDL 중복 컬럼 제거
-- 문제 발생 코드: created_at이 두 번 정의됨
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
total_price NUMERIC(12, 2),
created_at TIMESTAMPTZ DEFAULT NOW(),
status VARCHAR(50),
created_at TIMESTAMPTZ DEFAULT NOW() -- 중복! 42701 에러
);
-- 해결 방법: 중복 컬럼 제거
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
total_price NUMERIC(12, 2),
status VARCHAR(50),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 생성 전 테이블 존재 여부도 함께 체크하는 모범 사례
CREATE TABLE IF NOT EXISTS orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
total_price NUMERIC(12, 2),
status VARCHAR(50),
created_at TIMESTAMPTZ DEFAULT NOW()
);
원인 3 해결: 컬럼 별칭(alias)으로 이름 충돌 해소
-- 문제 발생 코드: 두 테이블 모두 id, created_at 보유
CREATE TABLE user_orders AS
SELECT *
FROM users u
JOIN orders o ON u.id = o.user_id;
-- 42701: column "id" specified more than once
-- 해결 방법 1: 명시적 컬럼 선택 + 별칭 부여
CREATE TABLE user_orders AS
SELECT
u.id AS user_id,
u.email AS user_email,
u.created_at AS user_created_at,
o.id AS order_id,
o.total_price,
o.status,
o.created_at AS order_created_at
FROM users u
JOIN orders o ON u.id = o.user_id;
-- 해결 방법 2: USING 절로 공통 컬럼 중복 제거 (JOIN시)
SELECT
u.id,
u.email,
o.total_price,
o.status
FROM users u
JOIN orders o USING (id);
-- USING을 사용하면 공통 컬럼이 결과에 한 번만 포함됨
-- 해결 방법 3: 서브쿼리로 컬럼 정리 후 CTAS
CREATE TABLE user_orders AS
SELECT
sub.user_id,
sub.user_email,
sub.order_id,
sub.total_price,
sub.order_status
FROM (
SELECT
u.id AS user_id,
u.email AS user_email,
o.id AS order_id,
o.total_price,
o.status AS order_status
FROM users u
JOIN orders o ON u.id = o.user_id
) sub;
예방 방법
1. 마이그레이션 도구 도입 및 멱등성 보장
Flyway, Liquibase, Sqitch 같은 데이터베이스 마이그레이션 도구를 도입하면 각 마이그레이션 스크립트의 실행 이력을 관리하므로, 동일 스크립트가 중복 실행되는 것을 원천적으로 방지할 수 있습니다. 불가피하게 순수 SQL 스크립트로 마이그레이션을 관리해야 한다면, 모든 ALTER TABLE ADD COLUMN 구문에는 반드시 IF NOT EXISTS를, CREATE TABLE 구문에는 IF NOT EXISTS를 습관적으로 붙여 스크립트 자체가 멱등성을 갖도록 작성하세요.
2. DDL 변경 전 information_schema 또는 pg_catalog 조회 습관화
운영 환경에서 DDL을 실행하기 전, 아래와 같이 information_schema.columns 또는 pg_attribute를 조회하여 대상 컬럼의 존재 여부를 반드시 확인하는 절차를 팀 내 표준으로 정착시키세요. 코드 리뷰 단계에서 DDL 스크립트에 중복 컬럼 여부를 점검하는 체크리스트를 추가하는 것도 효과적인 예방책입니다.
-- 특정 테이블의 모든 컬럼 목록 확인
SELECT
column_name,
data_type,
is_nullable,
column_default
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'users'
ORDER BY ordinal_position;
-- pg_attribute를 이용한 저수준 조회 (더 상세한 정보)
SELECT
a.attname AS column_name,
t.typname AS data_type,
a.attnotnull AS not_null
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_type t ON a.atttypid = t.oid
WHERE c.relname = 'users'
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum;
관련 에러
- 42P07 (duplicate_table): 이미 존재하는 테이블을
CREATE TABLE로 다시 생성하려 할 때 발생하는 에러로, 42701과 유사한 맥락에서 자주 함께 마주칩니다.CREATE TABLE IF NOT EXISTS로 예방할 수 있습니다. - 42712 (duplicate_alias):
SELECT쿼리의 결과 컬럼에 동일한 별칭(alias)을 두 번 사용할 때 발생합니다. 42701과 마찬가지로 컬럼 이름 충돌 계열의 에러입니다. - 42710 (duplicate_object): 인덱스, 제약 조건, 시퀀스 등 데이터베이스 객체를 중복 생성할 때 발생하며, DDL 스크립트 작성 시 42701과 함께 자주 나타납니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.