Oracle ORA-06545 오류 원인과 해결 방법 완벽 가이드

ORA-06545
2026년 09월 01일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-06545 PL/SQL: unhandled exception 는?

ORA-06545는 PL/SQL 블록 또는 서브프로그램(프로시저, 함수, 패키지 등) 내에서 발생한 예외(Exception)가 적절한 예외 처리 핸들러(EXCEPTION 블록)에 의해 처리되지 않고 호출자(Caller)에게 전파될 때 발생하는 에러입니다. 쉽게 말해, 런타임 도중 발생한 오류를 코드 내에서 잡아주는 로직이 없을 때 Oracle 엔진이 이 에러를 반환합니다. 이 에러는 단독으로 나타나기보다는 ORA-06512(at line N)와 함께 스택 트레이스 형태로 출력되어 에러가 발생한 위치를 추적하는 데 도움을 줍니다.


주요 발생 원인

1. EXCEPTION 블록 누락 또는 불완전한 예외 처리

가장 흔한 원인으로, PL/SQL 블록을 작성할 때 EXCEPTION 섹션 자체를 작성하지 않거나, 특정 예외만 처리하고 나머지는 무시한 경우입니다. 예를 들어, NO_DATA_FOUND나 TOO_MANY_ROWS 같은 특정 예외는 처리했지만 OTHERS 핸들러를 두지 않으면, 예상치 못한 다른 예외가 발생했을 때 ORA-06545가 발생합니다. 또한 중첩된 블록(Nested Block)에서 내부 블록의 예외가 외부 블록으로 전파되면서 발생하기도 합니다.

2. 사용자 정의 예외(User-Defined Exception) RAISE 후 미처리

개발자가 RAISE 또는 RAISE_APPLICATION_ERROR를 사용하여 명시적으로 예외를 발생시켰지만, 해당 예외를 처리하는 WHEN 절이 없는 경우입니다. 특히 패키지 내부에서 정의된 사용자 예외를 외부 호출 코드에서 CATCH하지 못할 때 이 문제가 자주 발생합니다. 이는 패키지 명세(Package Spec)와 패키지 바디(Package Body) 사이의 예외 가시성(Visibility) 문제와도 연관되어 있습니다.

3. 동적 SQL(Dynamic SQL) 또는 자율 트랜잭션(Autonomous Transaction) 내부의 미처리 예외

EXECUTE IMMEDIATE나 DBMS_SQL을 사용하는 동적 SQL 블록, 또는 PRAGMA AUTONOMOUS_TRANSACTION으로 선언된 자율 트랜잭션 내부에서 예외가 발생했을 때, 해당 블록에 EXCEPTION 핸들러가 없으면 예외가 그대로 호출자에게 전파됩니다. 자율 트랜잭션의 경우, 처리되지 않은 예외는 트랜잭션을 강제 롤백하고 에러를 호출 세션으로 반환하기 때문에 더욱 주의가 필요합니다.


해결 방법

원인 1 해결: EXCEPTION 블록 추가 및 OTHERS 핸들러 적용

모든 PL/SQL 블록에는 반드시 OTHERS 핸들러를 포함한 EXCEPTION 섹션을 추가해야 합니다.

-- 잘못된 예 (EXCEPTION 블록 없음)
CREATE OR REPLACE PROCEDURE bad_proc AS
  v_name VARCHAR2(100);
BEGIN
  SELECT ename INTO v_name FROM emp WHERE empno = 9999; -- 데이터 없으면 에러
  DBMS_OUTPUT.PUT_LINE('Name: ' || v_name);
END;
/

-- 올바른 예 (EXCEPTION 블록 포함)
CREATE OR REPLACE PROCEDURE good_proc AS
  v_name VARCHAR2(100);
BEGIN
  SELECT ename INTO v_name FROM emp WHERE empno = 9999;
  DBMS_OUTPUT.PUT_LINE('Name: ' || v_name);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('해당 사번의 직원이 존재하지 않습니다.');
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('복수의 레코드가 존재합니다.');
  WHEN OTHERS THEN
    -- SQLERRM과 SQLCODE로 에러 정보 기록
    DBMS_OUTPUT.PUT_LINE('예상치 못한 에러 발생: ' || SQLERRM);
    -- 필요 시 에러 로그 테이블에 INSERT
    INSERT INTO error_log (err_code, err_msg, err_date)
    VALUES (SQLCODE, SQLERRM, SYSDATE);
    COMMIT;
    RAISE; -- 필요에 따라 상위로 재전파
END;
/

원인 2 해결: 사용자 정의 예외의 올바른 선언과 처리

패키지 명세에 예외를 선언하고, 호출하는 쪽에서 패키지명을 붙여 예외를 처리해야 합니다.

-- 패키지 명세에 사용자 정의 예외 선언
CREATE OR REPLACE PACKAGE emp_pkg AS
  e_invalid_dept EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_invalid_dept, -20001);
  
  PROCEDURE hire_employee(p_empno NUMBER, p_deptno NUMBER);
END emp_pkg;
/

-- 패키지 바디 구현
CREATE OR REPLACE PACKAGE BODY emp_pkg AS
  PROCEDURE hire_employee(p_empno NUMBER, p_deptno NUMBER) AS
    v_cnt NUMBER;
  BEGIN
    SELECT COUNT(*) INTO v_cnt FROM dept WHERE deptno = p_deptno;
    IF v_cnt = 0 THEN
      RAISE_APPLICATION_ERROR(-20001, '유효하지 않은 부서 번호입니다: ' || p_deptno);
    END IF;
    INSERT INTO emp (empno, deptno) VALUES (p_empno, p_deptno);
    COMMIT;
  EXCEPTION
    WHEN OTHERS THEN
      ROLLBACK;
      RAISE;
  END hire_employee;
END emp_pkg;
/

-- 호출부에서 패키지 예외를 명시적으로 처리
BEGIN
  emp_pkg.hire_employee(1234, 99);
EXCEPTION
  WHEN emp_pkg.e_invalid_dept THEN
    DBMS_OUTPUT.PUT_LINE('부서 오류: ' || SQLERRM);
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('기타 오류: ' || SQLERRM);
END;
/

원인 3 해결: 동적 SQL 및 자율 트랜잭션의 예외 처리

-- 동적 SQL에서의 예외 처리
CREATE OR REPLACE PROCEDURE run_dynamic_sql(p_table_name VARCHAR2) AS
  v_count NUMBER;
  v_sql   VARCHAR2(500);
BEGIN
  v_sql := 'SELECT COUNT(*) FROM ' || DBMS_ASSERT.SIMPLE_SQL_NAME(p_table_name);
  EXECUTE IMMEDIATE v_sql INTO v_count;
  DBMS_OUTPUT.PUT_LINE('Row count: ' || v_count);
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('동적 SQL 실행 오류: ' || SQLERRM);
    RAISE;
END;
/

-- 자율 트랜잭션에서의 예외 처리
CREATE OR REPLACE PROCEDURE log_error_autonomous(
  p_proc_name VARCHAR2,
  p_err_msg   VARCHAR2
) AS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO error_log (proc_name, err_msg, log_date)
  VALUES (p_proc_name, p_err_msg, SYSDATE);
  COMMIT; -- 자율 트랜잭션은 반드시 COMMIT 또는 ROLLBACK 필요
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK; -- 처리 안 하면 ORA-06545 발생
    DBMS_OUTPUT.PUT_LINE('로그 저장 실패: ' || SQLERRM);
END;
/

예방 방법

1. 표준화된 예외 처리 템플릿 사용 및 코드 리뷰 의무화

모든 PL/SQL 개발 시 아래와 같은 표준 템플릿을 팀 내에 공유하고, 코드 리뷰 단계에서 EXCEPTION 블록 누락 여부를 반드시 확인하도록 프로세스를 수립해야 합니다. WHEN OTHERS 핸들러 내에서는 SQLERRM, SQLCODE, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE를 활용하여 에러 정보를 로그 테이블에 저장하는 습관을 들이면 장애 분석 시간을 크게 단축할 수 있습니다.

-- 표준 예외 처리 템플릿
CREATE OR REPLACE PROCEDURE template_proc AS
  c_proc_name CONSTANT VARCHAR2(100) := 'TEMPLATE_PROC';
BEGIN
  -- 비즈니스 로직
  NULL;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    log_error_autonomous(c_proc_name, 'NO_DATA_FOUND: ' || SQLERRM);
    RAISE;
  WHEN OTHERS THEN
    log_error_autonomous(
      c_proc_name,
      SQLERRM || CHR(10) || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
    );
    RAISE;
END;
/

2. 정적 분석 도구 및 컴파일 경고 활성화

Oracle의 PLSQL_WARNINGS 파라미터를 활성화하여 컴파일 시점에 잠재적 문제를 사전에 발견하세요. PL/SQL Analyzer, SQL Developer의 Code Analysis 기능, 또는 서드파티 툴(Toad Code Analysis 등)을 CI/CD 파이프라인에 통합하면 예외 처리 누락을 배포 전에 자동으로 감지할 수 있습니다.

-- 세션 레벨에서 컴파일 경고 활성화
ALTER SESSION SET PLSQL_WARNINGS = 'ENABLE:ALL';

-- 특정 프로시저 재컴파일 후 경고 확인
ALTER PROCEDURE good_proc COMPILE;

-- 컴파일 에러 및 경고 확인
SELECT line, position, text, attribute
FROM   user_errors
WHERE  name = 'GOOD_PROC'
ORDER  BY sequence;

관련 에러

  • ORA-06512: “at \, line N” 형태로 ORA-06545와 함께 출력되며, 예외가 발생한 정확한 소스 위치(파일명 및 라인 번호)를 스택 형태로 보여줍니다. 대부분의 경우 ORA-06545 바로 아래에 ORA-06512가 연달아 출력됩니다.
  • ORA-06500: PL/SQL storage error로, PGA 메모리 부족 시 발생하며 예외 처리가 없을 경우 ORA-06545로 이어질 수 있습니다.
  • ORA-01403: NO_DATA_FOUND. SELECT INTO 구문에서 결과가 없을 때 발생하며, EXCEPTION 블록이 없으면 ORA-06545를 유발하는 가장 흔한 원인 중 하나입니다.
  • ORA-01422: TOO_MANY_ROWS. SELECT INTO에서 복수 행 반환 시 발생하며, 마찬가지로 미처리 시 ORA-06545로 이어집니다.
  • ORA-20000 ~ ORA-20999: RAISE_APPLICATION_ERROR로 발생시키는 사용자 정의 에러 범위로, 호출부에서 처리하지 않으면 ORA-06545를 동반합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기