2026년 07월 10일 | DBMS Error 가이드
이 글에서 다루는 내용
42P05 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42P05 duplicate prepared statement 는?
PostgreSQL 에러 코드 42P05는 duplicate prepared statement 에러로, 이미 동일한 이름으로 준비된(Prepared) 구문이 현재 세션에 존재할 때 발생합니다. PREPARE 명령을 사용하여 이미 존재하는 이름과 동일한 이름으로 새로운 Prepared Statement를 생성하려 할 때 PostgreSQL은 이 에러를 반환합니다. 이 에러는 주로 애플리케이션 코드에서 커넥션 풀링 환경이나 반복적인 쿼리 준비 과정에서 자주 마주치게 됩니다.
주요 발생 원인
1. 동일한 세션에서 같은 이름으로 PREPARE를 중복 실행
가장 흔한 원인으로, 애플리케이션 로직에서 커넥션이 재사용될 때 이미 준비된 구문 이름을 해제(DEALLOCATE)하지 않고 다시 PREPARE를 호출하는 경우입니다. 특히 커넥션 풀링(PgBouncer, HikariCP 등)을 사용하는 환경에서는 커넥션이 재활용되므로, 이전 세션에서 생성된 Prepared Statement가 그대로 남아 충돌이 발생합니다. 애플리케이션이 재시작 없이 오래 운영될수록 이 문제가 더 자주 나타납니다.
2. 트랜잭션 내 예외 처리 미흡으로 인한 PREPARE 재시도
트랜잭션 중간에 에러가 발생하여 롤백되었음에도 불구하고, 예외 처리 코드가 Prepared Statement를 정리하지 않고 재시도하는 경우입니다. PREPARE는 트랜잭션 범위와 무관하게 세션 수준에서 유지되기 때문에, 트랜잭션이 롤백되어도 이미 등록된 Prepared Statement 이름은 그대로 살아남습니다. 이로 인해 재시도 시 동일한 이름으로 다시 PREPARE를 수행하면 42P05 에러가 발생합니다.
3. ORM 또는 드라이버의 자동 Prepared Statement 관리 충돌
Hibernate, SQLAlchemy, psycopg2, JDBC 등의 ORM 및 드라이버들은 내부적으로 Prepared Statement를 자동으로 생성하고 관리합니다. 드라이버 설정이 잘못되거나, 커넥션 풀과의 궁합이 맞지 않을 경우 드라이버가 이미 서버에 등록된 이름으로 다시 준비 명령을 내리면서 에러가 발생합니다. 특히 PgBouncer를 transaction 모드로 운영할 때 드라이버의 Prepared Statement 캐싱과 충돌하는 사례가 매우 빈번합니다.
해결 방법
원인 1 해결: DEALLOCATE 후 재생성
Prepared Statement를 새로 생성하기 전에 기존 것을 먼저 해제합니다.
-- 기존 Prepared Statement 해제
DEALLOCATE my_query;
-- 새롭게 Prepared Statement 생성
PREPARE my_query (int) AS
SELECT * FROM orders WHERE customer_id = $1;
-- 실행
EXECUTE my_query(42);
혹은, 존재 여부를 먼저 확인한 후 조건부로 처리하는 방식도 권장됩니다.
-- pg_prepared_statements 뷰를 통해 존재 여부 확인
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_prepared_statements WHERE name = 'my_query'
) THEN
DEALLOCATE my_query;
END IF;
END;
$$;
-- 안전하게 새로 등록
PREPARE my_query (int) AS
SELECT * FROM orders WHERE customer_id = $1;
원인 2 해결: 트랜잭션 예외 처리에서 DEALLOCATE 보장
트랜잭션 실패 시에도 반드시 Prepared Statement를 정리하도록 처리합니다.
BEGIN;
-- Prepared Statement 준비
PREPARE batch_insert (text, int) AS
INSERT INTO products (name, stock) VALUES ($1, $2);
-- 실행 시도
EXECUTE batch_insert('Widget A', 100);
-- 예외 발생을 가정한 ROLLBACK 시뮬레이션
-- 실제 환경에서는 애플리케이션 레이어에서 처리
ROLLBACK;
-- 롤백 이후에도 Prepared Statement는 세션에 남아 있으므로 반드시 해제
DEALLOCATE batch_insert;
원인 3 해결: 드라이버 및 커넥션 풀 설정 조정
psycopg2 (Python) 예시:
-- psycopg2에서 서버 사이드 prepared statement 비활성화
-- 연결 시 옵션으로 제어하거나, 쿼리마다 커서를 새로 사용
# Python psycopg2에서 Prepared Statement 이름 충돌 방지
import psycopg2
conn = psycopg2.connect("dbname=mydb user=postgres")
cur = conn.cursor()
# 이름 없는 prepared statement 사용 (매번 새로 준비됨)
cur.execute("SELECT * FROM orders WHERE customer_id = %s", (42,))
PgBouncer 환경에서 JDBC 설정 예시:
-- PgBouncer transaction 모드 사용 시
-- JDBC URL에 prepareThreshold=0 설정으로 서버 사이드 Prepared Statement 비활성화
-- jdbc:postgresql://host/db?prepareThreshold=0
전체 세션의 Prepared Statement를 한 번에 정리하려면:
-- 현재 세션의 모든 Prepared Statement 확인
SELECT name, statement, prepare_time
FROM pg_prepared_statements;
-- 모든 Prepared Statement 한 번에 해제
DEALLOCATE ALL;
예방 방법
1. Prepared Statement 생명주기를 명확히 관리하는 래퍼 함수 또는 유틸리티 사용
애플리케이션 레벨에서 Prepared Statement를 생성하기 전에 항상 pg_prepared_statements 뷰를 조회하여 중복 여부를 확인하고, 존재할 경우 DEALLOCATE 후 재생성하는 패턴을 공통 유틸리티로 추상화합니다. 커넥션 풀에서 커넥션을 반환할 때 DEALLOCATE ALL을 실행하는 훅(hook)을 등록하면 세션 오염을 사전에 방지할 수 있습니다.
-- 커넥션 풀 반환 시점에 실행하는 정리 쿼리 예시
DEALLOCATE ALL;
DISCARD ALL; -- 세션 상태 전체 초기화 (더 강력한 옵션)
2. PgBouncer 모드 선택과 드라이버 설정의 정합성 확보
PgBouncer를 session 모드로 운영하거나, transaction 모드 사용 시에는 반드시 드라이버의 서버 사이드 Prepared Statement 기능을 비활성화합니다. JDBC의 경우 prepareThreshold=0, psycopg2의 경우 named cursor 사용을 피하고, SQLAlchemy의 경우 use_native_hstore=False 및 statement cache 비활성화 옵션을 검토합니다. 운영 환경 배포 전 커넥션 풀 + 드라이버 조합을 반드시 통합 테스트로 검증하는 것을 표준 프로세스로 삼아야 합니다.
관련 에러
- 26000
invalid_sql_statement_name: 존재하지 않는 Prepared Statement 이름으로EXECUTE또는DEALLOCATE를 호출할 때 발생하는 에러로, 42P05와 반대 상황입니다. - 42P01
undefined_table: Prepared Statement 내 참조된 테이블이 없을 때 발생하며, 준비 단계에서 스키마 검증이 실패하는 경우 함께 나타날 수 있습니다. - 25P02
in_failed_sql_transaction: 트랜잭션 실패 상태에서 추가 쿼리를 시도할 때 발생하며, Prepared Statement 재시도 로직과 맞물려 42P05를 유발하는 선행 에러로 작용하는 경우가 많습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.