PostgreSQL 42P02 오류 원인과 해결 방법 완벽 가이드

42P02
2026년 07월 09일 | DBMS Error 가이드

이 글에서 다루는 내용

42P02 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

42P02 undefined parameter 는?

PostgreSQL 에러 코드 42P02는 “undefined parameter”로, SQL 문이나 함수 내에서 참조된 파라미터($1, $2 등)가 정의되지 않았거나, 실제로 바인딩된 파라미터 수보다 더 많은 파라미터를 참조하려 할 때 발생합니다. 주로 prepared statement, PL/pgSQL 함수, 동적 SQL 실행 시 파라미터 바인딩이 올바르게 이루어지지 않았을 때 나타나는 에러입니다. 이 에러는 쿼리 실행 전 파싱 단계에서 감지되므로, 실제 데이터에 접근하기 전에 미리 차단된다는 특징이 있습니다.


주요 발생 원인

1. Prepared Statement에서 파라미터 수 불일치

Prepared statement를 작성할 때 SQL 문에 선언한 파라미터($1, $2, …)의 수와 실제 실행 시 전달한 값의 수가 맞지 않으면 이 에러가 발생합니다. 예를 들어 쿼리에 $3까지 사용했는데 EXECUTE 시 2개의 값만 넘기거나, 반대로 SQL 문에는 $1만 있는데 존재하지 않는 $2를 참조하는 경우입니다. 이는 개발 초기 단계나 코드 리팩토링 과정에서 SQL을 수정하면서 파라미터 번호를 잘못 조정했을 때 가장 많이 발생합니다.

2. PL/pgSQL 동적 SQL(EXECUTE)에서의 파라미터 참조 오류

PL/pgSQL에서 EXECUTE 구문으로 동적 SQL을 실행할 때, USING 절에 전달한 변수 수보다 더 많은 $N 참조가 SQL 문자열 내에 포함된 경우 이 에러가 발생합니다. 동적 SQL은 컴파일 타임에 완전히 검증되지 않기 때문에, 런타임에 파라미터 참조 번호가 실제 전달된 인자 수를 초과하면 42P02 에러가 발생하게 됩니다. 복잡한 동적 쿼리를 생성하는 함수에서 특히 주의가 필요합니다.

3. PREPARE 구문 사용 시 파라미터 타입 미지정 또는 초과 참조

PREPARE 구문을 사용해 쿼리를 미리 준비할 때, 파라미터 타입을 명시하는 리스트보다 더 높은 번호의 파라미터를 SQL 내에서 참조하거나, 아예 타입 선언 없이 존재하지 않는 파라미터 번호를 사용할 경우 발생합니다. 또한 PREPARE 없이 단순 쿼리에서 $1 형식의 파라미터 플레이스홀더를 그대로 사용하려는 실수도 이 에러를 유발합니다.


해결 방법

원인 1 해결: Prepared Statement 파라미터 수 일치시키기

파라미터 수가 맞는지 확인하고 수정합니다.

-- 잘못된 예: $3를 참조하지만 EXECUTE 시 값 2개만 전달
PREPARE my_query (int, text) AS
  SELECT * FROM users WHERE id = $1 AND name = $2 AND status = $3;
-- 에러: ERROR: there is no parameter $3

-- 올바른 예: 파라미터 타입 선언과 사용 수를 일치
PREPARE my_query (int, text, boolean) AS
  SELECT * FROM users WHERE id = $1 AND name = $2 AND is_active = $3;

-- 실행
EXECUTE my_query(1, 'Alice', true);

파라미터를 줄여야 하는 경우라면 SQL 내의 $N 참조 번호도 함께 수정해야 합니다.

-- 파라미터를 2개로 줄인 올바른 예
PREPARE my_query (int, text) AS
  SELECT * FROM users WHERE id = $1 AND name = $2;

EXECUTE my_query(1, 'Alice');

원인 2 해결: PL/pgSQL 동적 SQL USING 절 파라미터 정확히 전달하기

동적 SQL의 USING 절에 전달하는 인자 수와 SQL 문자열 내 $N 참조 수를 반드시 맞추어야 합니다.

-- 잘못된 예: SQL 문에 $3 참조가 있지만 USING에 2개 값만 전달
CREATE OR REPLACE FUNCTION find_user(p_id int, p_name text)
RETURNS TABLE(id int, name text, dept text) AS $$
DECLARE
  v_sql text;
BEGIN
  v_sql := 'SELECT id, name, dept FROM users WHERE id = $1 AND name = $2 AND dept = $3';
  RETURN QUERY EXECUTE v_sql USING p_id, p_name;
  -- 에러: ERROR:  there is no parameter $3
END;
$$ LANGUAGE plpgsql;

-- 올바른 예: USING 절 인자 수와 $N 참조 수를 맞춤
CREATE OR REPLACE FUNCTION find_user(p_id int, p_name text, p_dept text)
RETURNS TABLE(id int, name text, dept text) AS $$
DECLARE
  v_sql text;
BEGIN
  v_sql := 'SELECT id, name, dept FROM users WHERE id = $1 AND name = $2 AND dept = $3';
  RETURN QUERY EXECUTE v_sql USING p_id, p_name, p_dept;
END;
$$ LANGUAGE plpgsql;

-- 함수 호출
SELECT * FROM find_user(1, 'Alice', 'Engineering');

동적 SQL이 복잡할 경우 실행 전 RAISE NOTICE로 SQL 문자열을 먼저 출력해 검증하는 것이 좋습니다.

-- 디버깅 팁: SQL 문자열 확인
CREATE OR REPLACE FUNCTION debug_dynamic_sql(p_id int, p_name text)
RETURNS void AS $$
DECLARE
  v_sql text;
BEGIN
  v_sql := format('SELECT * FROM users WHERE id = %L AND name = %L', p_id, p_name);
  RAISE NOTICE 'Executing SQL: %', v_sql;
  EXECUTE v_sql;
END;
$$ LANGUAGE plpgsql;

원인 3 해결: PREPARE 구문 파라미터 타입 선언 정확히 하기

-- 잘못된 예: 타입 선언은 1개인데 $2 참조
PREPARE bad_stmt (int) AS
  SELECT * FROM orders WHERE user_id = $1 AND product_id = $2;
-- 에러: ERROR:  there is no parameter $2

-- 올바른 예: 파라미터 타입 모두 선언
PREPARE good_stmt (int, int) AS
  SELECT * FROM orders WHERE user_id = $1 AND product_id = $2;

EXECUTE good_stmt(100, 200);

-- 사용 후 정리
DEALLOCATE good_stmt;

일반 쿼리에서 실수로 $1 플레이스홀더를 사용하는 경우, 반드시 PREPARE/EXECUTE 패턴을 사용하거나 실제 값으로 대체해야 합니다.

-- 잘못된 예: 일반 SELECT에서 플레이스홀더 사용 시도
SELECT * FROM users WHERE id = $1;
-- 에러: ERROR:  there is no parameter $1

-- 올바른 예 1: 직접 값 사용
SELECT * FROM users WHERE id = 42;

-- 올바른 예 2: Prepared statement 활용
PREPARE get_user (int) AS SELECT * FROM users WHERE id = $1;
EXECUTE get_user(42);
DEALLOCATE get_user;

예방 방법

1. SQL 파라미터 번호와 타입을 항상 명시적으로 관리하라

Prepared statement나 동적 SQL을 작성할 때, 파라미터 번호($1, $2, …)를 순차적으로 빠짐없이 사용하고, 선언된 타입 목록과 반드시 1:1로 대응되도록 습관화해야 합니다. 쿼리가 수정될 때마다 파라미터 번호와 USING 절(또는 EXECUTE 인자)이 동기화되어 있는지 코드 리뷰 체크리스트에 포함시키는 것이 좋습니다. 특히 동적 SQL을 생성하는 함수는 단위 테스트를 통해 다양한 입력 조합으로 파라미터 바인딩을 검증해야 합니다.

2. 동적 SQL 생성 시 format() 함수와 로깅을 적극 활용하라

PL/pgSQL에서 동적 SQL을 생성할 때는 문자열 연결(||) 대신 PostgreSQL의 format() 함수를 사용하면 파라미터 자리수 관리와 SQL 인젝션 방지를 동시에 해결할 수 있습니다. 또한 개발 및 스테이징 환경에서는 RAISE NOTICE 또는 RAISE LOG를 통해 실행 직전의 동적 SQL 문자열을 로그에 남겨두면, 파라미터 불일치 문제를 조기에 발견할 수 있습니다. 운영 환경에서는 log_min_messages 설정을 적절히 조정해 필요한 로그가 캡처되도록 관리하세요.


관련 에러

  • 42601 (syntax_error): SQL 문법 오류로, 파라미터 플레이스홀더를 잘못된 위치에 사용했을 때 함께 발생하는 경우가 있습니다.
  • 42P01 (undefined_table): 동적 SQL에서 테이블명과 파라미터를 혼동하여 잘못 참조할 때 연관되어 나타날 수 있습니다.
  • 08P01 (protocol_violation): 클라이언트 라이브러리에서 prepared statement에 전달한 파라미터 수가 서버 측 선언과 맞지 않을 때 발생하며, 42P02와 유사한 맥락에서 나타납니다.
  • 22023 (invalid_parameter_value): 파라미터 자체는 존재하지만 전달된 값의 타입이나 범위가 잘못되었을 때 발생하는 에러로, 42P02와 함께 파라미터 관련 에러 그룹으로 묶어 관리하는 것이 좋습니다.
DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기