🎯 학습 목표
- EXCEPTION 섹션에서 사전정의 예외를 처리하는 패턴을 익힌다.
- WHEN OTHERS THEN과 SQLCODE/SQLERRM으로 예상 못 한 오류를 처리한다.
- RAISE로 예외를 다시 올리고, 사용자 정의 예외와 RAISE_APPLICATION_ERROR를 만든다.
📖 예외 처리의 흐름
PL/SQL 블록에서 오류가 발생하면 실행이 즉시 EXCEPTION 섹션으로 점프합니다. EXCEPTION 섹션에서 처리되지 않으면 오류가 호출자에게 전파(propagate)됩니다.
BEGIN
(정상 실행)
오류 발생 --------------------------->
EXCEPTION
WHEN 예외1 THEN ... <-- 처리됨 --> 블록 정상 종료
WHEN 예외2 THEN ...
WHEN OTHERS THEN ... <-- 나머지 모두
END; <-- 처리 안 되면 오류가 위로 전파
📖 사전정의 예외 (Named Exceptions)
DECLARE
v_sal emp.sal%TYPE;
BEGIN
SELECT sal INTO v_sal FROM emp WHERE empno = 9999; -- 없는 사원
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('[오류] 해당 사원 없음');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('[오류] 조회 결과가 두 건 이상');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('[오류] 0으로 나눌 수 없음');
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('[오류] 중복 키 입력');
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('[오류] 타입 또는 크기 오류');
END;
/
자주 쓰는 사전정의 예외: NO_DATA_FOUND, TOO_MANY_ROWS, ZERO_DIVIDE, DUP_VAL_ON_INDEX, VALUE_ERROR, CURSOR_ALREADY_OPEN, INVALID_NUMBER.
💻 WHEN OTHERS THEN — 포괄 처리
BEGIN
INSERT INTO some_table VALUES (1, '테스트');
COMMIT;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('중복: ' || SQLERRM);
ROLLBACK;
WHEN OTHERS THEN
-- SQLCODE: Oracle 오류 번호 (음수)
-- SQLERRM: 오류 메시지 문자열
DBMS_OUTPUT.PUT_LINE('예상치 못한 오류 [' || SQLCODE || ']: ' || SQLERRM);
ROLLBACK;
RAISE; -- 처리 후 오류를 다시 위로 전파
END;
/
WHEN OTHERS THEN은 반드시 마지막에 위치해야 합니다. 그 뒤에 다른 WHEN을 쓰면 컴파일 오류입니다.
💻 사용자 정의 예외와 RAISE
DECLARE
e_low_stock EXCEPTION; -- 사용자 정의 예외 선언
v_qty NUMBER := 3;
BEGIN
IF v_qty < 5 THEN
RAISE e_low_stock; -- 명시적으로 예외 발생
END IF;
DBMS_OUTPUT.PUT_LINE('재고 충분: ' || v_qty);
EXCEPTION
WHEN e_low_stock THEN
DBMS_OUTPUT.PUT_LINE('[경고] 재고 부족: ' || v_qty || '개');
END;
/
💻 RAISE_APPLICATION_ERROR — 의미 있는 오류 번호와 메시지
CREATE OR REPLACE PROCEDURE check_age(p_age IN NUMBER) AS
BEGIN
IF p_age < 0 THEN
-- 오류 번호는 -20000 ~ -20999 범위에서 자유롭게 지정
RAISE_APPLICATION_ERROR(-20001, '나이는 음수일 수 없습니다: ' || p_age);
END IF;
DBMS_OUTPUT.PUT_LINE('나이: ' || p_age);
END;
/
호출자(애플리케이션)에 특정 오류 번호와 메시지를 전달할 때 씁니다. 사용자 정의 예외는 블록 내부에서 처리하는 반면, RAISE_APPLICATION_ERROR는 외부 호출자에게도 의미 있는 오류를 전달합니다.
⚠️ 주의사항
- EXCEPTION 섹션에서 오류가 발생하면 그 오류는 같은 블록 안에서 처리되지 않고 바깥으로 전파됩니다. 중첩 블록 안에서 각각 처리하거나 RAISE로 위임하세요.
WHEN OTHERS THEN NULL;처럼 오류를 그냥 삼키는 코드는 매우 위험합니다. 최소한 로그를 남기거나 RAISE로 전파하세요.- EXCEPTION 섹션에서는 GOTO로 BEGIN 구역으로 돌아갈 수 없습니다. 재시도 로직이 필요하면 중첩 블록이나 반복문 구조로 설계하세요.
💡 팁
- 프로시저·함수에서 오류 로그 테이블에 INSERT한 뒤 RAISE하는 패턴이 실무에서 많이 쓰입니다. 오류 이력을 추적하면서도 호출자에게 오류를 알릴 수 있습니다.
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE()를 WHEN OTHERS 안에서 쓰면 오류가 발생한 정확한 줄 번호를 알 수 있어 디버깅에 유용합니다.