2026년 09월 13일 | DBMS Error 가이드
이 글에서 다루는 내용
42P05 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42P05 duplicate prepared statement 는?
PostgreSQL 에러 코드 42P05는 동일한 이름의 Prepared Statement가 이미 존재할 때 발생하는 에러입니다. PREPARE 명령을 통해 이미 등록된 이름으로 다시 PREPARE를 시도하면, PostgreSQL은 이를 중복으로 간주하고 해당 에러를 발생시킵니다. 주로 연결 풀링 환경, 장기 실행 세션, 또는 애플리케이션 재시작 없이 반복적으로 쿼리를 준비(Prepare)하는 코드에서 자주 목격됩니다.
주요 발생 원인
1. 동일 세션에서 같은 이름으로 중복 PREPARE 실행
가장 흔한 원인입니다. 애플리케이션 코드가 매 요청마다 또는 매 함수 호출마다 PREPARE 구문을 실행하는데, 세션이 유지된 상태(커넥션 풀 등)에서 이미 같은 이름의 Prepared Statement가 등록되어 있을 때 발생합니다. 특히 PgBouncer나 pgpool-II 같은 커넥션 풀러를 사용할 때, 동일한 백엔드 연결이 재사용되므로 이전 세션에서 준비된 구문이 그대로 남아 충돌을 일으킵니다.
2. 트랜잭션 롤백 후 재실행 시 정리되지 않은 Prepared Statement
트랜잭션 내에서 PREPARE를 실행한 후 해당 트랜잭션이 롤백되어도, PostgreSQL에서 Prepared Statement는 트랜잭션 범위 밖에서 관리됩니다. 즉, 롤백되어도 이미 등록된 Prepared Statement는 삭제되지 않고 세션에 남아 있습니다. 이 상태에서 동일한 이름으로 다시 PREPARE를 시도하면 42P05 에러가 발생합니다.
3. ORM 또는 드라이버의 자동 Prepared Statement 관리 미흡
Hibernate, SQLAlchemy, psycopg2, JDBC 등 다양한 ORM 및 데이터베이스 드라이버는 내부적으로 Prepared Statement를 자동 생성하고 캐싱합니다. 드라이버나 ORM의 설정이 잘못되거나, 연결이 재사용될 때 이전에 준비된 구문을 제대로 정리하지 않으면 동일한 이름의 Prepared Statement가 충돌합니다. 특히 Java의 JDBC 드라이버에서 prepareThreshold 설정이나 Python의 psycopg2에서 autocommit 모드 설정이 이런 문제를 유발할 수 있습니다.
해결 방법
원인 1 해결: DEALLOCATE 후 재등록
이미 존재하는 Prepared Statement를 먼저 해제(DEALLOCATE)하고 다시 준비(PREPARE)합니다.
-- 기존 Prepared Statement 확인
SELECT name, statement, prepare_time
FROM pg_prepared_statements;
-- 특정 Prepared Statement 해제
DEALLOCATE my_query;
-- 또는 모든 Prepared Statement 해제
DEALLOCATE ALL;
-- 다시 준비
PREPARE my_query (int) AS
SELECT * FROM orders WHERE customer_id = $1;
-- 실행
EXECUTE my_query(42);
안전하게 처리하려면 존재 여부를 확인한 후 조건부로 해제하는 방법도 권장됩니다.
-- 존재 여부 확인 후 조건부 해제 (PL/pgSQL 내에서 활용)
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_prepared_statements WHERE name = 'my_query'
) THEN
DEALLOCATE my_query;
END IF;
END;
$$;
-- 안전하게 다시 PREPARE
PREPARE my_query (int) AS
SELECT * FROM orders WHERE customer_id = $1;
원인 2 해결: 트랜잭션 구조 개선 및 명시적 DEALLOCATE
트랜잭션 롤백 후에도 Prepared Statement가 남아 있음을 인지하고, 예외 처리 블록에서 명시적으로 해제합니다.
-- PL/pgSQL에서 예외 처리와 함께 사용하는 패턴
DO $$
BEGIN
-- 기존 것이 있으면 먼저 해제
DEALLOCATE ALL;
PREPARE fetch_user (int) AS
SELECT id, username, email FROM users WHERE id = $1;
-- 실행
EXECUTE fetch_user(100);
EXCEPTION
WHEN duplicate_prepared_statement THEN
RAISE NOTICE '이미 존재하는 Prepared Statement입니다. 해제 후 재시도합니다.';
DEALLOCATE fetch_user;
PREPARE fetch_user (int) AS
SELECT id, username, email FROM users WHERE id = $1;
END;
$$;
원인 3 해결: 드라이버/ORM 설정 조정
psycopg2를 사용하는 경우, autocommit 모드를 활성화하거나 Prepared Statement 캐시를 비활성화합니다.
-- psycopg2 (Python) 예시 - SQL 레벨에서의 처리
-- 연결 초기화 시 기존 구문 정리
DEALLOCATE ALL;
-- 이후 정상적으로 PREPARE 사용
PREPARE product_search (text) AS
SELECT product_id, name, price
FROM products
WHERE name ILIKE '%' || $1 || '%'
ORDER BY price ASC;
EXECUTE product_search('노트북');
JDBC를 사용하는 Java 환경에서는 연결 URL에 prepareThreshold=0을 설정하여 자동 Prepared Statement 변환을 비활성화하거나, 명시적으로 Statement를 닫아주는 코드를 추가합니다.
예방 방법
1. 애플리케이션 레벨에서 Prepared Statement 생명주기 관리
Prepared Statement는 세션 단위로 관리되므로, 커넥션 풀에서 연결을 반환하기 전에 반드시 DEALLOCATE ALL을 실행하거나, 연결을 재사용하기 전에 초기화 쿼리를 통해 이전 상태를 정리하는 습관을 들여야 합니다. 연결 풀 설정에서 “connection initialization query”나 “reset query” 옵션이 있다면 반드시 활용하세요.
-- PgBouncer의 server_reset_query 설정에 추가 권장
-- pgbouncer.ini 예시:
-- server_reset_query = DISCARD ALL;
-- DISCARD ALL은 아래를 모두 포함합니다:
DISCARD ALL;
-- 위 명령은 DEALLOCATE ALL, RESET ALL, UNLISTEN * 등을 포함
2. pg_prepared_statements 뷰를 통한 모니터링 체계 구축
운영 환경에서 주기적으로 pg_prepared_statements 뷰를 조회하여 비정상적으로 많은 Prepared Statement가 쌓이고 있는지 모니터링합니다. 임계값을 초과하면 알림을 발송하는 자동화 스크립트를 구축해두면 사전에 문제를 예방할 수 있습니다.
-- 현재 세션의 Prepared Statement 전체 현황 조회
SELECT
name,
statement,
prepare_time,
from_sql,
parameter_types
FROM pg_prepared_statements
ORDER BY prepare_time DESC;
-- 세션별 Prepared Statement 수 집계 (pg_stat_activity 활용)
SELECT
pid,
usename,
application_name,
state,
COUNT(*) OVER() AS total_prepared
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY pid;
관련 에러
26000– invalid_sql_statement_name:EXECUTE또는DEALLOCATE시 존재하지 않는 Prepared Statement 이름을 참조할 때 발생합니다.42P05와 반대 상황으로, 이름이 없을 때 발생하는 에러입니다.55000– object_not_in_prerequisite_state: Prepared Statement 실행 전 필요한 선행 조건이 충족되지 않았을 때 발생할 수 있습니다.53300– too_many_connections: 커넥션 풀 관련 설정 미흡으로 연결이 과도하게 생성될 때 발생하며,42P05와 함께 커넥션 풀 환경에서 자주 동반 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.