2026년 08월 06일 | DBMS Error 가이드
이 글에서 다루는 내용
0Z000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
0Z000 diagnostics exception 는?
PostgreSQL 에러 코드 0Z000은 diagnostics_exception으로 분류되며, PL/pgSQL 또는 PL/SQL 호환 블록 내에서 GET DIAGNOSTICS 구문을 잘못 사용하거나, 진단 정보를 가져오는 컨텍스트가 올바르지 않을 때 발생합니다. 이 에러는 주로 저장 프로시저(Stored Procedure), 함수(Function), 또는 트리거(Trigger) 내부에서 예외 처리 블록이나 진단 구문을 부적절하게 호출할 때 나타납니다. 일반적으로 SQL 표준의 진단 영역(Diagnostics Area)과 관련된 예외 상황을 다루며, 실무에서는 복잡한 예외 처리 로직을 작성할 때 자주 마주치게 됩니다.
주요 발생 원인
GET STACKED DIAGNOSTICS를 EXCEPTION 블록 외부에서 호출
GET STACKED DIAGNOSTICS는 반드시 EXCEPTION 블록 내부에서만 사용할 수 있습니다. 이 구문은 현재 처리 중인 예외에 대한 스택 진단 정보를 가져오는 목적으로 설계되어 있으며, 예외 컨텍스트 밖에서 호출하면 PostgreSQL은 참조할 수 있는 스택 정보가 없기 때문에 0Z000 에러를 발생시킵니다. 개발자가 일반 블록과 예외 블록의 구조적 차이를 혼동할 때 이 실수가 자주 발생합니다.
- 진단 변수 타입 불일치
GET DIAGNOSTICS 또는 GET STACKED DIAGNOSTICS 구문에서 반환되는 진단 항목(예: RETURNED_SQLSTATE, MESSAGE_TEXT, PG_EXCEPTION_DETAIL 등)을 담을 변수의 데이터 타입이 맞지 않을 경우 에러가 발생할 수 있습니다. 예를 들어, ROW_COUNT를 TEXT 타입 변수에 담으려 하거나, 문자열 진단 정보를 INTEGER 타입으로 받으려 할 때 내부적으로 변환 오류와 함께 진단 예외가 트리거됩니다. 타입 매핑을 정확히 이해하고 선언하는 것이 중요합니다.
- 중첩된 예외 처리 블록에서의 진단 컨텍스트 혼용
중첩된 PL/pgSQL 블록에서 내부 블록의 예외를 외부 블록에서 처리하면서 진단 정보를 참조하려 할 때, 컨텍스트 경계가 모호해져 0Z000 에러가 발생할 수 있습니다. PostgreSQL의 진단 스택은 현재 활성화된 예외 핸들러의 스코프 안에서만 유효하며, 블록을 벗어난 후에는 해당 진단 정보에 접근할 수 없습니다. 복잡한 예외 처리 구조를 설계할 때는 각 블록의 스코프를 명확히 구분하는 것이 필수입니다.
해결 방법
원인 1: GET STACKED DIAGNOSTICS를 올바른 위치에서 사용하기
잘못된 예제 (에러 발생):
DO $$
DECLARE
v_state TEXT;
v_message TEXT;
BEGIN
-- 일반 블록에서 STACKED DIAGNOSTICS 호출 (잘못된 사용)
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT;
RAISE NOTICE 'State: %, Message: %', v_state, v_message;
END;
$$;
-- ERROR: 0Z000: GET STACKED DIAGNOSTICS cannot be used outside an exception handler
올바른 예제 (수정 후):
DO $$
DECLARE
v_state TEXT;
v_message TEXT;
v_detail TEXT;
v_hint TEXT;
v_context TEXT;
BEGIN
-- 의도적으로 예외를 발생시키는 구문
PERFORM 1 / 0;
EXCEPTION
WHEN OTHERS THEN
-- EXCEPTION 블록 내부에서만 GET STACKED DIAGNOSTICS 사용 가능
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_detail = PG_EXCEPTION_DETAIL,
v_hint = PG_EXCEPTION_HINT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE NOTICE '에러 코드: %', v_state;
RAISE NOTICE '에러 메시지: %', v_message;
RAISE NOTICE '상세 정보: %', v_detail;
RAISE NOTICE '힌트: %', v_hint;
RAISE NOTICE '컨텍스트: %', v_context;
END;
$$;
원인 2: 진단 변수 타입을 올바르게 선언하기
CREATE OR REPLACE FUNCTION safe_diagnostics_example()
RETURNS VOID AS $$
DECLARE
-- 올바른 타입 선언
v_sqlstate TEXT; -- RETURNED_SQLSTATE → TEXT
v_message TEXT; -- MESSAGE_TEXT → TEXT
v_detail TEXT; -- PG_EXCEPTION_DETAIL → TEXT
v_row_count BIGINT; -- ROW_COUNT → BIGINT
v_hint TEXT; -- PG_EXCEPTION_HINT → TEXT
BEGIN
-- ROW_COUNT는 EXCEPTION 블록 밖에서도 사용 가능 (GET DIAGNOSTICS)
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;
GET DIAGNOSTICS v_row_count = ROW_COUNT;
RAISE NOTICE '업데이트된 행 수: %', v_row_count;
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS
v_sqlstate = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_detail = PG_EXCEPTION_DETAIL,
v_hint = PG_EXCEPTION_HINT;
-- 에러 로그 테이블에 기록
INSERT INTO error_log (error_code, error_message, error_detail, error_hint, logged_at)
VALUES (v_sqlstate, v_message, v_detail, v_hint, NOW());
RAISE EXCEPTION '처리 중 에러 발생: [%] %', v_sqlstate, v_message;
END;
$$ LANGUAGE plpgsql;
원인 3: 중첩 블록에서의 진단 정보 올바르게 처리하기
CREATE OR REPLACE FUNCTION nested_exception_handler()
RETURNS VOID AS $$
DECLARE
v_outer_state TEXT;
v_outer_message TEXT;
BEGIN
-- 외부 블록
BEGIN
-- 내부 블록: 자체적으로 예외 처리
DECLARE
v_inner_state TEXT;
v_inner_message TEXT;
BEGIN
PERFORM * FROM non_existent_table; -- 에러 유발
EXCEPTION
WHEN OTHERS THEN
-- 내부 블록의 EXCEPTION에서 진단 정보 수집
GET STACKED DIAGNOSTICS
v_inner_state = RETURNED_SQLSTATE,
v_inner_message = MESSAGE_TEXT;
RAISE NOTICE '[내부 블록] 에러 처리: [%] %', v_inner_state, v_inner_message;
-- 필요시 외부로 재발생
-- RAISE;
END;
-- 외부 블록의 다른 작업
PERFORM pg_sleep(0);
EXCEPTION
WHEN OTHERS THEN
-- 외부 블록의 EXCEPTION에서 별도 진단 정보 수집
GET STACKED DIAGNOSTICS
v_outer_state = RETURNED_SQLSTATE,
v_outer_message = MESSAGE_TEXT;
RAISE NOTICE '[외부 블록] 에러 처리: [%] %', v_outer_state, v_outer_message;
END;
END;
$$ LANGUAGE plpgsql;
예방 방법
- 에러 로깅 전용 함수를 표준화하여 일관되게 사용하기
진단 정보를 수집하고 로깅하는 공통 함수를 미리 작성해 두고, 모든 저장 프로시저와 함수에서 이를 재사용하면 GET STACKED DIAGNOSTICS의 잘못된 사용을 사전에 방지할 수 있습니다. 아래와 같이 표준 에러 핸들러 함수를 정의해 두는 것을 권장합니다.
“`sql
— 에러 로그 테이블 생성
CREATE TABLE IF NOT EXISTS error_log (
id BIGSERIAL PRIMARY KEY,
proc_name TEXT,
error_code TEXT,
error_message TEXT,
error_detail TEXT,
error_hint TEXT,
error_context TEXT,
logged_at TIMESTAMPTZ DEFAULT NOW()
);
— 표준 에러 핸들링 프로시저
CREATE OR REPLACE PROCEDURE log_error(
p_proc_name TEXT
) AS $$
DECLARE
v_state TEXT;
v_message TEXT;
v_detail TEXT;
v_hint TEXT;
v_context TEXT;
BEGIN
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_detail = PG_EXCEPTION_DETAIL,
v_hint = PG_EXCEPTION_HINT,
v_context = PG_EXCEPTION_CONTEXT;
INSERT INTO error_log (proc_name, error_code, error_message, error_detail, error_hint, error_context)
VALUES (p_proc_name, v_state, v_message, v_detail, v_hint, v_context);
END;
$$ LANGUAGE plpgsql;
— 사용 예시
CREATE OR REPLACE PROCEDURE business_logic()
AS $$
BEGIN
— 비즈니스 로직
INSERT INTO orders (customer_id, amount) VALUES (NULL, 100); — NOT NULL 위반 유발
EXCEPTION
WHEN OTHERS THEN
CALL log_error(‘business_logic’);
RAISE;
END;
$$ LANGUAGE plpgsql;
“`
- 코드 리뷰 체크리스트에 PL/pgSQL 진단 구문 규칙 포함하기
팀 내 코드 리뷰 프로세스에 PL/pgSQL 예외 처리 가이드라인을 포함하여, GET STACKED DIAGNOSTICS는 항상 EXCEPTION WHEN 블록 내에서만 사용하고, GET DIAGNOSTICS는 일반 실행 블록에서 DML 결과 확인용으로만 사용하도록 규칙을 명문화해야 합니다. 또한 CI/CD 파이프라인에 pg_prove나 pgTAP을 활용한 자동화 테스트를 포함하면 배포 전에 잘못된 진단 구문 사용을 사전에 감지할 수 있습니다.
관련 에러
P0000(plpgsql_error): PL/pgSQL 전반적인 런타임 에러로, 진단 예외와 함께 발생하는 경우가 많습니다.P0001(raise_exception):RAISE EXCEPTION구문으로 명시적으로 발생시킨 에러이며,GET STACKED DIAGNOSTICS로 정보를 캡처할 수 있습니다.39000(external_routine_exception): 외부 루틴 호출 시 발생하는 예외로, PL/pgSQL 진단 예외와 유사한 맥락에서 발생합니다.38000(external_routine_invocation_exception): 외부 프로시저 호출 예외로, 진단 정보 수집 시0Z000과 함께 나타날 수 있습니다.XX000(internal_error): PostgreSQL 내부 오류로, 진단 컨텍스트가 손상된 경우0Z000과 연계되어 발생하기도 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.