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

42P13
2026년 07월 13일 | DBMS Error 가이드

이 글에서 다루는 내용

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

42P13 invalid function definition 는?

PostgreSQL 에러 코드 42P13은 함수(Function) 또는 프로시저(Procedure)를 생성하거나 수정할 때 함수 정의 자체에 논리적 또는 구문적 오류가 있을 경우 발생하는 에러입니다. 단순한 SQL 문법 오류(42601)와는 달리, 이 에러는 함수의 반환 타입, 언어 선언, RETURNS 절, 또는 함수 본문 구조 자체가 PostgreSQL의 규칙에 맞지 않을 때 발생합니다. 실무에서는 PL/pgSQL, SQL, PL/Python 등 다양한 언어로 함수를 작성할 때 자주 마주치는 에러이며, 원인을 정확히 파악하지 못하면 디버깅에 많은 시간을 낭비할 수 있습니다.


주요 발생 원인

1. RETURNS 절과 실제 반환 값의 불일치

함수 정의에서 RETURNS 절에 선언한 타입과 함수 본문에서 실제로 반환하는 값의 타입이 맞지 않을 때 발생합니다. 예를 들어 RETURNS INTEGER로 선언했지만 본문에서 RETURN 'hello'처럼 문자열을 반환하거나, RETURNS TABLE을 선언했음에도 단순 스칼라 값을 반환하는 경우가 이에 해당합니다. PostgreSQL은 함수 생성 시점에 반환 타입의 유효성을 엄격히 검사하기 때문에 이 불일치는 즉시 에러로 이어집니다.

2. 함수 언어(LANGUAGE) 선언 오류 또는 지원되지 않는 언어 사용

LANGUAGE 절에 잘못된 언어 이름을 명시하거나, 해당 PostgreSQL 서버에 설치되지 않은 절차적 언어(Procedural Language)를 사용하려고 할 때 이 에러가 발생합니다. 예를 들어 plpgsql 대신 plpgsq처럼 오타를 내거나, PL/Python(plpython3u)이 서버에 설치되어 있지 않은 상태에서 해당 언어로 함수를 생성하려는 경우가 대표적입니다. SELECT * FROM pg_language; 쿼리로 현재 설치된 언어를 미리 확인하는 습관이 중요합니다.

3. RETURNS VOID 함수에서 잘못된 RETURN 문 사용 또는 SET RETURNING 함수의 구조 오류

RETURNS VOID로 선언된 함수 안에서 값을 반환하는 RETURN 문을 사용하거나, RETURNS SETOF / RETURNS TABLE 함수에서 RETURN NEXT 또는 RETURN QUERY 없이 단순 RETURN 문만 사용하는 경우에도 이 에러가 발생할 수 있습니다. 또한 OUT 파라미터와 RETURNS 절을 동시에 잘못 조합하는 경우도 빈번한 원인 중 하나입니다. 이러한 경우 함수의 반환 구조 전체를 다시 설계해야 할 수도 있습니다.


해결 방법

원인 1 해결: RETURNS 절과 반환 타입 일치시키기

아래는 잘못된 예시와 올바른 수정 예시입니다.

-- ❌ 잘못된 예: RETURNS INTEGER인데 TEXT를 반환
CREATE OR REPLACE FUNCTION get_user_name(p_user_id INT)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN 'John Doe';  -- TEXT를 반환하므로 에러 발생
END;
$$;

-- ✅ 올바른 예: 반환 타입을 TEXT로 수정
CREATE OR REPLACE FUNCTION get_user_name(p_user_id INT)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
    v_name TEXT;
BEGIN
    SELECT name INTO v_name FROM users WHERE user_id = p_user_id;
    RETURN v_name;
END;
$$;
-- ❌ 잘못된 예: RETURNS TABLE인데 스칼라 반환
CREATE OR REPLACE FUNCTION get_active_users()
RETURNS TABLE(user_id INT, username TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN 1;  -- 에러: 테이블 구조와 맞지 않음
END;
$$;

-- ✅ 올바른 예: RETURN QUERY 사용
CREATE OR REPLACE FUNCTION get_active_users()
RETURNS TABLE(user_id INT, username TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY
        SELECT u.user_id, u.username
        FROM users u
        WHERE u.is_active = TRUE;
END;
$$;

원인 2 해결: LANGUAGE 절 및 설치 언어 확인

-- 현재 서버에 설치된 언어 확인
SELECT lanname, lanpltrusted FROM pg_language;

-- PL/pgSQL 설치 (슈퍼유저 권한 필요)
CREATE EXTENSION IF NOT EXISTS plpgsql;

-- PL/Python3 설치 (슈퍼유저 권한 필요)
CREATE EXTENSION IF NOT EXISTS plpython3u;

-- ❌ 잘못된 예: 오타가 있는 언어 선언
CREATE OR REPLACE FUNCTION calculate_bonus(p_salary NUMERIC)
RETURNS NUMERIC
LANGUAGE plpgsq  -- 오타! 'plpgsql'이어야 함
AS $$
BEGIN
    RETURN p_salary * 0.1;
END;
$$;

-- ✅ 올바른 예
CREATE OR REPLACE FUNCTION calculate_bonus(p_salary NUMERIC)
RETURNS NUMERIC
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN p_salary * 0.1;
END;
$$;

원인 3 해결: VOID 함수 및 SET RETURNING 함수 구조 수정

-- ❌ 잘못된 예: RETURNS VOID인데 값을 반환
CREATE OR REPLACE FUNCTION log_action(p_action TEXT)
RETURNS VOID
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO action_log(action, created_at) VALUES (p_action, NOW());
    RETURN 'done';  -- 에러: VOID 함수는 값 반환 불가
END;
$$;

-- ✅ 올바른 예: RETURN; 또는 생략
CREATE OR REPLACE FUNCTION log_action(p_action TEXT)
RETURNS VOID
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO action_log(action, created_at) VALUES (p_action, NOW());
    RETURN;  -- 또는 이 줄 자체를 생략해도 됨
END;
$$;

-- ❌ 잘못된 예: OUT 파라미터와 RETURNS 혼용
CREATE OR REPLACE FUNCTION get_stats(
    IN p_dept_id INT,
    OUT o_count INT,
    OUT o_avg_salary NUMERIC
)
RETURNS INTEGER  -- 에러: OUT 파라미터가 있으면 RETURNS 불필요
LANGUAGE plpgsql
AS $$
BEGIN
    SELECT COUNT(*), AVG(salary)
    INTO o_count, o_avg_salary
    FROM employees
    WHERE dept_id = p_dept_id;
END;
$$;

-- ✅ 올바른 예: OUT 파라미터만 사용하거나 RETURNS RECORD 사용
CREATE OR REPLACE FUNCTION get_stats(
    IN p_dept_id INT,
    OUT o_count INT,
    OUT o_avg_salary NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
    SELECT COUNT(*), AVG(salary)
    INTO o_count, o_avg_salary
    FROM employees
    WHERE dept_id = p_dept_id;
END;
$$;

예방 방법

1. 함수 작성 전 반환 타입 및 파라미터 구조를 명세서로 문서화하기

함수를 작성하기 전에 반드시 입력 파라미터의 타입, 반환 타입(스칼라/테이블/VOID), 사용할 언어를 명세화하세요. 특히 팀 환경에서는 코드 리뷰 단계에서 함수 시그니처(Signature)만이라도 사전 검토하는 프로세스를 도입하면 42P13 에러를 대폭 줄일 수 있습니다. 개발 환경에서 충분히 테스트한 후 운영 환경에 배포하는 CI/CD 파이프라인을 구축하는 것도 강력히 권장합니다.

2. \df 명령어와 pg_proc 시스템 카탈로그를 활용한 함수 정의 사전 검증

psql의 \df+ 함수명 명령어나 pg_proc 카탈로그를 통해 기존 함수의 정의를 항상 확인하고, 새로 작성할 함수와 충돌이 없는지 미리 점검하세요. 또한 PostgreSQL의 DO 블록을 활용해 함수 본문 로직을 먼저 익명 블록으로 테스트한 후 정식 함수로 등록하는 방법을 사용하면 에러를 사전에 발견할 수 있습니다.

-- pg_proc으로 함수 정의 확인
SELECT proname, prorettype::regtype, prolang, prosrc
FROM pg_proc
WHERE proname = 'get_user_name';

-- DO 블록으로 로직 사전 테스트
DO $$
DECLARE
    v_result TEXT;
BEGIN
    SELECT name INTO v_result FROM users WHERE user_id = 1;
    RAISE NOTICE 'Result: %', v_result;
END;
$$;

관련 에러

  • 42601 (syntax_error): SQL 문법 자체의 오류로, 42P13과 함께 자주 발생합니다. 함수 본문의 세미콜론 누락이나 키워드 오타가 주요 원인입니다.
  • 42883 (undefined_function): 호출하려는 함수가 존재하지 않거나 해당 인자 타입의 함수가 없을 때 발생하며, 함수 정의 실패 후 호출 시 연달아 나타날 수 있습니다.
  • 0A000 (feature_not_supported): 특정 PostgreSQL 버전에서 지원하지 않는 함수 기능을 사용할 때 발생하며, 버전 호환성 문제와 연관됩니다.
  • 42P16 (invalid_table_definition): 함수가 아닌 테이블/뷰 정의 오류이지만, RETURNS TABLE 구조에서 컬럼 타입 정의가 잘못된 경우 연관되어 나타날 수 있습니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기