2026년 06월 03일 | DBMS Error 가이드
이 글에서 다루는 내용
0Z002 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
0Z002 stacked diagnostics accessed without active handler 는?
PostgreSQL 에러 코드 0Z002는 PL/pgSQL 코드 내에서 GET STACKED DIAGNOSTICS 구문을 예외 처리 핸들러(EXCEPTION 블록) 외부에서 호출했을 때 발생하는 에러입니다. GET STACKED DIAGNOSTICS는 오직 활성화된 예외 핸들러 안에서만 사용할 수 있으며, 예외 컨텍스트 정보(스택 트레이스, 에러 메시지 등)를 가져오기 위한 용도로 설계되어 있습니다. 즉, 예외가 실제로 발생하여 해당 EXCEPTION 블록이 활성 상태일 때만 의미 있는 진단 정보를 제공할 수 있기 때문에, 그 외의 영역에서 호출하면 PostgreSQL이 이 에러를 발생시킵니다.
주요 발생 원인
- EXCEPTION 블록 밖에서 GET STACKED DIAGNOSTICS 호출
가장 흔한 원인입니다. 개발자가 GET STACKED DIAGNOSTICS 구문을 일반 실행 흐름(BEGIN…END 블록)에 잘못 배치하는 경우입니다. 이 구문은 오직 예외가 발생하여 EXCEPTION 핸들러가 활성화된 상태에서만 호출되어야 하며, 그렇지 않으면 “active handler”가 없다는 에러가 발생합니다.
- 중첩 블록에서의 잘못된 범위(Scope) 사용
중첩된 BEGIN…END 블록을 사용할 때, 내부 블록에서 발생한 예외를 처리하는 핸들러가 아닌 외부 블록의 일반 실행 영역에서 GET STACKED DIAGNOSTICS를 호출하는 경우입니다. 블록 구조가 복잡해질수록 어느 블록의 예외 핸들러가 활성화되어 있는지 혼동하기 쉬워 이 에러가 발생합니다.
- 동적 SQL 또는 함수 호출 후 잘못된 진단 정보 수집 시도
EXECUTE 구문이나 다른 함수를 호출한 직후, 예외가 발생하지 않았음에도 불구하고 진단 정보를 수집하려는 시도에서 발생합니다. 개발자가 GET DIAGNOSTICS(현재 실행 컨텍스트용)와 GET STACKED DIAGNOSTICS(예외 핸들러 전용)를 혼동하여 잘못된 구문을 사용할 때 이 에러가 나타납니다.
해결 방법
원인 1 해결: GET STACKED DIAGNOSTICS를 반드시 EXCEPTION 블록 내부로 이동
잘못된 예시:
CREATE OR REPLACE FUNCTION bad_example()
RETURNS void AS $$
DECLARE
v_state TEXT;
v_message TEXT;
v_context TEXT;
BEGIN
-- 잘못된 위치: EXCEPTION 블록 밖에서 호출
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE NOTICE 'State: %, Message: %', v_state, v_message;
END;
$$ LANGUAGE plpgsql;
올바른 예시:
CREATE OR REPLACE FUNCTION good_example()
RETURNS void AS $$
DECLARE
v_state TEXT;
v_message TEXT;
v_context TEXT;
BEGIN
-- 여기서 예외가 발생한다고 가정
PERFORM 1 / 0;
EXCEPTION
WHEN OTHERS THEN
-- 올바른 위치: EXCEPTION 블록 내부에서 호출
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE NOTICE 'Error State: %', v_state;
RAISE NOTICE 'Error Message: %', v_message;
RAISE NOTICE 'Error Context: %', v_context;
END;
$$ LANGUAGE plpgsql;
원인 2 해결: 중첩 블록에서 올바른 범위 지정
CREATE OR REPLACE FUNCTION nested_block_example()
RETURNS void AS $$
DECLARE
v_message TEXT;
v_context TEXT;
BEGIN
-- 외부 블록의 일반 실행 영역
RAISE NOTICE '외부 블록 시작';
BEGIN
-- 내부 블록: 여기서 예외 발생
PERFORM 1 / 0;
EXCEPTION
WHEN division_by_zero THEN
-- 내부 블록의 EXCEPTION 핸들러: 올바른 위치
GET STACKED DIAGNOSTICS
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE NOTICE '내부 블록 에러 처리: %', v_message;
RAISE NOTICE '에러 컨텍스트: %', v_context;
END;
-- 외부 블록 계속 실행
RAISE NOTICE '외부 블록 계속 진행';
END;
$$ LANGUAGE plpgsql;
원인 3 해결: GET DIAGNOSTICS와 GET STACKED DIAGNOSTICS 구분
현재 실행 컨텍스트 정보가 필요하다면 GET DIAGNOSTICS를 사용하고, 예외 정보가 필요할 때만 GET STACKED DIAGNOSTICS를 사용합니다.
CREATE OR REPLACE FUNCTION diagnostics_difference_example()
RETURNS void AS $$
DECLARE
v_row_count BIGINT;
v_message TEXT;
v_context TEXT;
BEGIN
-- GET DIAGNOSTICS: 일반 실행 컨텍스트에서 사용 가능
UPDATE some_table SET col1 = 'value' WHERE id = 1;
GET DIAGNOSTICS v_row_count = ROW_COUNT;
RAISE NOTICE '영향받은 행 수: %', v_row_count;
EXCEPTION
WHEN OTHERS THEN
-- GET STACKED DIAGNOSTICS: 반드시 EXCEPTION 블록 안에서만 사용
GET STACKED DIAGNOSTICS
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE WARNING '예외 발생 - 메시지: %, 컨텍스트: %',
v_message, v_context;
END;
$$ LANGUAGE plpgsql;
실무에서 활용 가능한 에러 로깅 패턴
아래는 실무에서 자주 사용되는 에러 로깅 함수 패턴입니다. 이 패턴을 공통 유틸리티 함수로 만들어 재사용하면 효과적입니다.
-- 에러 로그 테이블 생성
CREATE TABLE IF NOT EXISTS error_log (
id SERIAL PRIMARY KEY,
func_name TEXT,
sql_state TEXT,
err_message TEXT,
err_context TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 에러 로깅 함수
CREATE OR REPLACE FUNCTION log_and_handle_error(p_func_name TEXT)
RETURNS void AS $$
DECLARE
v_state TEXT;
v_message TEXT;
v_context TEXT;
BEGIN
-- 비즈니스 로직 실행 (예시)
PERFORM some_risky_operation();
EXCEPTION
WHEN OTHERS THEN
-- 스택 진단 정보 수집 (EXCEPTION 블록 내부)
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
-- 에러 로그 테이블에 기록
INSERT INTO error_log (func_name, sql_state, err_message, err_context)
VALUES (p_func_name, v_state, v_message, v_context);
-- 필요 시 예외 재발생
RAISE EXCEPTION 'Function % failed: [%] %',
p_func_name, v_state, v_message
USING ERRCODE = v_state;
END;
$$ LANGUAGE plpgsql;
예방 방법
- 코드 리뷰 시 GET STACKED DIAGNOSTICS 위치를 반드시 검증하라
팀 내 코드 리뷰 프로세스에서 GET STACKED DIAGNOSTICS 구문이 항상 EXCEPTION 블록 내부에 위치하는지 체크리스트 항목으로 추가하는 것을 권장합니다. IDE나 정적 분석 도구(예: plpgsql_check 익스텐션)를 활용하면 배포 전에 이 류의 오류를 사전에 탐지할 수 있습니다. plpgsql_check는 CREATE EXTENSION plpgsql_check; 명령으로 설치 후 SELECT plpgsql_check_function('your_function_name()');로 점검할 수 있습니다.
“`sql
— plpgsql_check 익스텐션을 통한 사전 검증
CREATE EXTENSION IF NOT EXISTS plpgsql_check;
— 함수 문법 및 로직 검사
SELECT * FROM plpgsql_check_function(‘good_example()’);
“`
- 표준화된 예외 처리 템플릿을 팀 전체에 공유하라
모든 PL/pgSQL 함수 작성 시 아래와 같은 표준 템플릿을 사용하도록 팀 가이드라인을 수립하면, GET STACKED DIAGNOSTICS 오용을 구조적으로 방지할 수 있습니다. 템플릿을 코드 스니펫 형태로 IDE에 등록해 두면 실수를 줄이는 데 효과적입니다.
“`sql
— 팀 표준 PL/pgSQL 함수 템플릿
CREATE OR REPLACE FUNCTION template_function(/ 파라미터 /)
RETURNS void AS $$
DECLARE
v_sql_state TEXT;
v_message TEXT;
v_context TEXT;
BEGIN
/*
* 비즈니스 로직을 여기에 작성
* GET STACKED DIAGNOSTICS는 절대 여기에 쓰지 말 것
*/
EXCEPTION
WHEN OTHERS THEN
— 항상 이 블록 안에서만 GET STACKED DIAGNOSTICS 사용
GET STACKED DIAGNOSTICS
v_sql_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE WARNING ‘[%] %: %’, v_sql_state, v_message, v_context;
RAISE; — 필요 시 예외 재발생
END;
$$ LANGUAGE plpgsql;
“`
관련 에러
0Z000(diagnostics_exception):0Z002의 상위 범주 에러로, 진단 정보 수집과 관련된 일반적인 예외 클래스입니다.P0000(plpgsql_error): PL/pgSQL 런타임 전반에서 발생하는 일반 에러로, 잘못된 블록 구조나 변수 참조 오류와 함께 나타날 수 있습니다.P0001(raise_exception):RAISE EXCEPTION구문으로 명시적으로 발생시킨 예외이며,GET STACKED DIAGNOSTICS로 해당 예외의 세부 정보를 수집할 때 자주 함께 등장합니다.P0004(assert_failure):ASSERT구문 실패 시 발생하며, 예외 핸들러 구조 설계 시 함께 고려해야 할 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.