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

P0002
2026년 07월 29일 | DBMS Error 가이드

이 글에서 다루는 내용

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

P0002 no data found 는?

PostgreSQL 에러 코드 P0002는 PL/pgSQL 함수나 프로시저 내에서 SELECT INTO 또는 FETCH 구문을 실행했을 때 결과 행이 하나도 반환되지 않은 경우 발생하는 예외(Exception)입니다. 이 에러는 단순한 SELECT 쿼리가 아니라, 반드시 하나의 행을 반환해야 한다는 암묵적 기대가 있는 PL/pgSQL 컨텍스트에서 주로 발생합니다. 실무에서는 데이터 정합성 문제, 잘못된 파라미터 전달, 또는 예상치 못한 데이터 삭제 이후에 자주 마주치는 에러입니다.


주요 발생 원인

  • PL/pgSQL 함수 내 SELECT INTO에서 STRICT 옵션 사용

PL/pgSQL에서 SELECT INTO STRICT를 사용하면, 쿼리 결과가 정확히 한 행이어야 합니다. 결과가 0건이면 NO_DATA_FOUND 예외가 발생하고, 결과가 2건 이상이면 TOO_MANY_ROWS 예외가 발생합니다. 실무에서는 단건 조회를 보장하려는 의도로 STRICT를 사용하지만, 데이터가 없는 경우를 처리하지 않으면 함수 전체가 예외로 중단됩니다.

  • CURSOR FETCH 이후 데이터 유무 확인 없이 변수 참조

커서(CURSOR)를 열고 FETCH로 데이터를 가져올 때, 커서에 더 이상 데이터가 없는 상태에서 FETCH를 시도하면 NO_DATA_FOUND가 발생할 수 있습니다. 특히 루프 외부에서 커서를 수동으로 관리하거나, FOUND 변수를 확인하지 않고 바로 데이터를 사용하는 경우 이 에러를 만나게 됩니다.

  • 외래 키 참조 또는 조인 대상 데이터 부재

비즈니스 로직상 반드시 존재해야 하는 참조 데이터(예: 회원 정보, 상품 정보 등)가 삭제되거나 마이그레이션 과정에서 누락된 경우, 이를 조회하는 함수에서 P0002 에러가 발생합니다. 이는 데이터 정합성 관리 부재에서 비롯되며, 특히 소프트 딜리트(soft delete)와 하드 딜리트(hard delete)가 혼용되는 환경에서 자주 발생합니다.


해결 방법

원인 1 해결: STRICT 대신 예외 처리 블록 사용

SELECT INTO STRICT 사용 시 반드시 EXCEPTION 블록으로 NO_DATA_FOUND를 처리해야 합니다.

CREATE OR REPLACE FUNCTION get_user_by_id(p_user_id INT)
RETURNS users AS $$
DECLARE
    v_user users%ROWTYPE;
BEGIN
    -- STRICT 옵션으로 정확히 1건을 기대
    SELECT * INTO STRICT v_user
    FROM users
    WHERE user_id = p_user_id;

    RETURN v_user;

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        -- 데이터가 없을 경우 명시적으로 처리
        RAISE NOTICE 'user_id %에 해당하는 사용자가 없습니다.', p_user_id;
        RETURN NULL;
    WHEN TOO_MANY_ROWS THEN
        RAISE EXCEPTION 'user_id %에 해당하는 사용자가 여러 명입니다.', p_user_id;
END;
$$ LANGUAGE plpgsql;

STRICT 없이 FOUND 변수를 활용하는 방법도 안전합니다.

CREATE OR REPLACE FUNCTION get_user_safe(p_user_id INT)
RETURNS users AS $$
DECLARE
    v_user users%ROWTYPE;
BEGIN
    SELECT * INTO v_user
    FROM users
    WHERE user_id = p_user_id;

    -- FOUND 변수로 결과 존재 여부 확인
    IF NOT FOUND THEN
        RAISE NOTICE '해당 사용자가 존재하지 않습니다: %', p_user_id;
        RETURN NULL;
    END IF;

    RETURN v_user;
END;
$$ LANGUAGE plpgsql;

원인 2 해결: 커서 FETCH 이후 반드시 FOUND 확인

CREATE OR REPLACE FUNCTION process_orders()
RETURNS VOID AS $$
DECLARE
    v_order orders%ROWTYPE;
    cur_orders CURSOR FOR
        SELECT * FROM orders WHERE status = 'PENDING';
BEGIN
    OPEN cur_orders;

    LOOP
        FETCH cur_orders INTO v_order;

        -- FETCH 직후 반드시 FOUND 확인
        EXIT WHEN NOT FOUND;

        -- 데이터 처리 로직
        RAISE NOTICE '주문 처리 중: order_id = %', v_order.order_id;
        UPDATE orders SET status = 'PROCESSING'
        WHERE order_id = v_order.order_id;
    END LOOP;

    CLOSE cur_orders;

    RAISE NOTICE '모든 PENDING 주문 처리 완료';
END;
$$ LANGUAGE plpgsql;

원인 3 해결: 조회 전 데이터 존재 여부 사전 검증

CREATE OR REPLACE FUNCTION get_product_with_validation(p_product_id INT)
RETURNS products AS $$
DECLARE
    v_product products%ROWTYPE;
    v_count   INT;
BEGIN
    -- 먼저 데이터 존재 여부 확인
    SELECT COUNT(*) INTO v_count
    FROM products
    WHERE product_id = p_product_id
      AND is_deleted = FALSE; -- 소프트 딜리트 조건 포함

    IF v_count = 0 THEN
        RAISE EXCEPTION 'P0002: product_id %에 해당하는 활성 상품이 없습니다.', p_product_id
            USING ERRCODE = 'P0002';
    END IF;

    SELECT * INTO v_product
    FROM products
    WHERE product_id = p_product_id
      AND is_deleted = FALSE;

    RETURN v_product;
END;
$$ LANGUAGE plpgsql;

-- 호출 예시 및 에러 처리
DO $$
BEGIN
    PERFORM get_product_with_validation(9999);
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        RAISE NOTICE '상품 데이터를 찾을 수 없습니다.';
END;
$$;

예방 방법

  • 모든 PL/pgSQL 함수에 표준 예외 처리 템플릿 적용

팀 내 코딩 컨벤션으로 PL/pgSQL 함수 작성 시 반드시 NO_DATA_FOUNDTOO_MANY_ROWS 예외 처리를 포함하는 템플릿을 강제하세요. CI/CD 파이프라인에 PL/pgSQL 정적 분석 도구(예: plpgsql_check 확장)를 연동하면 코드 리뷰 단계에서 누락된 예외 처리를 자동으로 탐지할 수 있습니다.

“`sql

— plpgsql_check 확장 활성화 (예방을 위한 정적 분석)

CREATE EXTENSION IF NOT EXISTS plpgsql_check;

— 함수 검증 예시

SELECT * FROM plpgsql_check_function(‘get_user_by_id(int)’);

“`

  • 데이터 정합성 보장을 위한 FK 제약 조건 및 감사 로그 활성화

참조 무결성을 데이터베이스 레벨에서 보장하기 위해 외래 키(Foreign Key) 제약 조건을 반드시 설정하고, 중요 테이블에는 감사 로그(Audit Log) 트리거를 적용하세요. 이를 통해 예상치 못한 데이터 삭제나 누락을 조기에 발견하고, P0002 에러의 근본 원인을 사전에 차단할 수 있습니다.


관련 에러

  • P0001 (raise_exception): PL/pgSQL에서 RAISE EXCEPTION으로 명시적으로 발생시키는 에러로, P0002와 함께 PL/pgSQL 예외 처리의 핵심 코드입니다.
  • P0003 (too_many_rows): SELECT INTO STRICT에서 결과가 2건 이상일 때 발생하며, P0002와 쌍으로 처리해야 하는 에러입니다. 두 에러 모두 STRICT 옵션 사용 시 EXCEPTION 블록에서 함께 핸들링하는 것이 모범 사례입니다.
  • 23502 (not_null_violation): 데이터가 없어 NULL이 할당된 변수를 NOT NULL 컬럼에 삽입할 때 연쇄적으로 발생할 수 있어 P0002와 간접적으로 연관됩니다.
  • 02000 (no_data): SQL 표준 레벨의 “no data” 상태 코드로, P0002는 이 상태를 PL/pgSQL 예외로 격상시킨 것입니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기