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

20000
2026년 06월 03일 | DBMS Error 가이드

이 글에서 다루는 내용

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

20000 case not found 는?

PostgreSQL 에러 코드 20000case_not_found라는 이름으로, PL/pgSQL의 CASE 문에서 어떠한 조건도 일치하지 않았을 때 발생하는 런타임 에러입니다. 이 에러는 CASE 문에 ELSE 절이 없는 상태에서 입력값이 모든 WHEN 조건을 만족하지 못할 경우 PostgreSQL이 처리할 분기를 찾지 못해 예외를 던지는 상황에서 발생합니다. 주로 PL/pgSQL 함수나 프로시저 내부에서 CASE 표현식을 사용할 때 데이터의 범위나 유형을 충분히 고려하지 않은 경우 나타납니다.


주요 발생 원인

1. ELSE 절 없는 CASE 문에서 미처리 값 발생

PL/pgSQL에서 CASE 문을 작성할 때 ELSE 절을 생략하면, 어떤 WHEN 조건에도 해당하지 않는 값이 입력될 경우 즉시 case_not_found 예외가 발생합니다. 이는 SQL의 CASE WHEN 표현식이 NULL을 반환하는 것과 달리, PL/pgSQL의 CASE 문은 반드시 일치하는 분기가 있어야 정상 실행되기 때문입니다. 예를 들어 상태값이 ‘A’, ‘B’, ‘C’ 세 가지만 처리하도록 작성했는데 실제 데이터에 ‘D’가 존재한다면 이 에러가 발생합니다.

2. 데이터 변경으로 인한 예상치 못한 값 유입

애플리케이션이 처음 개발된 이후 비즈니스 요구사항이 변경되거나 데이터 마이그레이션이 수행되면서 기존 CASE 문이 처리하지 못하는 새로운 값이 데이터베이스에 추가될 수 있습니다. 이런 상황에서는 기존에 정상 동작하던 함수나 프로시저가 갑자기 에러를 던지기 시작해 운영 장애로 이어지는 경우가 많습니다. 특히 ENUM 타입이나 코드성 컬럼의 값이 늘어났을 때 이 문제가 빈번하게 발생합니다.

3. NULL 값 처리 미흡

CASE 문에서 NULL 값을 명시적으로 처리하지 않으면 예상치 못한 case_not_found 에러로 이어질 수 있습니다. PL/pgSQL의 CASE 표현식에서 NULL = NULLTRUE가 아니므로, 입력값이 NULL인 경우 어떤 WHEN 조건도 일치하지 않아 에러가 발생합니다. 컬럼이 NOT NULL 제약이 없는 경우 이 상황은 언제든지 발생할 수 있으므로 반드시 WHEN NULL 또는 ELSE 절로 방어해야 합니다.


해결 방법

원인 1 해결: ELSE 절 추가

가장 직접적인 해결 방법은 모든 CASE 문에 ELSE 절을 추가하는 것입니다. 처리되지 않은 값이 들어왔을 때 기본값을 반환하거나, 의도적으로 예외를 발생시켜 로그에 남기는 방식을 선택할 수 있습니다.

-- 문제가 되는 코드 (ELSE 절 없음)
CREATE OR REPLACE FUNCTION get_status_label(p_status TEXT)
RETURNS TEXT AS $$
BEGIN
    CASE p_status
        WHEN 'A' THEN RETURN '승인';
        WHEN 'B' THEN RETURN '대기';
        WHEN 'C' THEN RETURN '거절';
        -- 'D' 같은 값이 들어오면 20000 에러 발생!
    END CASE;
END;
$$ LANGUAGE plpgsql;

-- 해결된 코드 (ELSE 절 추가)
CREATE OR REPLACE FUNCTION get_status_label(p_status TEXT)
RETURNS TEXT AS $$
BEGIN
    CASE p_status
        WHEN 'A' THEN RETURN '승인';
        WHEN 'B' THEN RETURN '대기';
        WHEN 'C' THEN RETURN '거절';
        ELSE
            -- 알 수 없는 값에 대한 처리
            RAISE WARNING '알 수 없는 상태값: %', p_status;
            RETURN '알 수 없음';
    END CASE;
END;
$$ LANGUAGE plpgsql;

-- 테스트
SELECT get_status_label('A');  -- 승인
SELECT get_status_label('D');  -- 알 수 없음 (WARNING 로그 발생)

원인 2 해결: EXCEPTION 블록으로 에러 처리

기존 함수를 수정하기 어려운 운영 환경이라면, EXCEPTION 블록을 사용해 case_not_found 에러를 잡아 처리할 수 있습니다.

CREATE OR REPLACE FUNCTION get_status_label_safe(p_status TEXT)
RETURNS TEXT AS $$
DECLARE
    v_result TEXT;
BEGIN
    CASE p_status
        WHEN 'A' THEN v_result := '승인';
        WHEN 'B' THEN v_result := '대기';
        WHEN 'C' THEN v_result := '거절';
    END CASE;

    RETURN v_result;

EXCEPTION
    WHEN case_not_found THEN
        -- 에러 로그 테이블에 기록
        INSERT INTO error_log(func_name, input_value, error_time)
        VALUES ('get_status_label_safe', p_status, NOW());

        RAISE NOTICE 'CASE 처리 실패 - 입력값: %, 기본값 반환', p_status;
        RETURN '미정의 상태';
END;
$$ LANGUAGE plpgsql;

원인 3 해결: NULL 값 명시적 처리

NULL 값이 유입될 가능성이 있다면 반드시 명시적으로 처리해야 합니다.

CREATE OR REPLACE FUNCTION process_order_status(p_status TEXT)
RETURNS TEXT AS $$
BEGIN
    -- NULL 체크를 먼저 수행
    IF p_status IS NULL THEN
        RETURN '상태 정보 없음';
    END IF;

    CASE p_status
        WHEN 'PENDING'   THEN RETURN '주문 대기';
        WHEN 'APPROVED'  THEN RETURN '주문 승인';
        WHEN 'SHIPPED'   THEN RETURN '배송 중';
        WHEN 'DELIVERED' THEN RETURN '배송 완료';
        WHEN 'CANCELLED' THEN RETURN '주문 취소';
        ELSE
            RETURN FORMAT('미처리 상태: %s', p_status);
    END CASE;
END;
$$ LANGUAGE plpgsql;

-- Searched CASE 형태에서도 NULL 처리 가능
CREATE OR REPLACE FUNCTION classify_score(p_score NUMERIC)
RETURNS TEXT AS $$
BEGIN
    CASE
        WHEN p_score IS NULL      THEN RETURN '점수 없음';
        WHEN p_score >= 90        THEN RETURN 'A';
        WHEN p_score >= 80        THEN RETURN 'B';
        WHEN p_score >= 70        THEN RETURN 'C';
        WHEN p_score >= 60        THEN RETURN 'D';
        ELSE                           RETURN 'F';
    END CASE;
END;
$$ LANGUAGE plpgsql;

예방 방법

1. 코드 리뷰 시 CASE 문 ELSE 절 필수화 정책 수립

팀 내 코딩 컨벤션으로 PL/pgSQL의 모든 CASE 문에는 반드시 ELSE 절을 포함하도록 규칙을 정하고, CI/CD 파이프라인에서 이를 검사하는 린팅 도구를 적용하는 것이 좋습니다. 새로운 상태값이나 코드값이 추가될 때마다 관련된 함수와 프로시저 목록을 관리하고, 변경 전에 영향 범위를 분석하는 변경 관리 프로세스를 도입하면 운영 장애를 크게 줄일 수 있습니다.

2. 단위 테스트 및 경계값 테스트 자동화

pgTAP 같은 PostgreSQL 테스트 프레임워크를 활용하여 함수와 프로시저에 대한 단위 테스트를 작성하고, 정의된 값 이외의 경계값, NULL, 빈 문자열 등에 대한 테스트 케이스를 반드시 포함시키는 자동화된 테스트 체계를 구축해야 합니다. 데이터베이스 객체가 변경될 때마다 자동으로 테스트가 실행되도록 구성하면 배포 전에 문제를 사전에 차단할 수 있습니다.

-- pgTAP을 이용한 테스트 예시
SELECT plan(4);

SELECT is(get_status_label('A'), '승인', '상태 A 정상 처리');
SELECT is(get_status_label('B'), '대기', '상태 B 정상 처리');
SELECT is(get_status_label(NULL), '알 수 없음', 'NULL 값 처리');
SELECT is(get_status_label('Z'), '알 수 없음', '미정의 값 처리');

SELECT * FROM finish();

관련 에러

  • P0001 (raise_exception): PL/pgSQL에서 RAISE EXCEPTION으로 명시적으로 던진 에러로, case_not_foundELSE에서 의도적으로 예외로 전환할 때 사용됩니다.
  • P0002 (no_data_found): SELECT INTO에서 결과가 없을 때 발생하는 에러로, CASE 처리 전 데이터 조회 실패와 함께 발생하는 경우가 많습니다.
  • P0003 (too_many_rows): SELECT INTO에서 복수의 행이 반환될 때 발생하며, CASE 처리 로직과 함께 PL/pgSQL 예외 처리 맥락에서 자주 언급됩니다.
  • 42601 (syntax_error): CASE 문 문법 오류로 발생하며, case_not_found와 혼동될 수 있어 주의가 필요합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기