Python을 배워보자

오라클 저장 프로시저 내부 오류 캡처 및 Python oracledb로 애플리케이션 레벨 오류 처리 방법

_Blue_Sky_ 2025. 6. 15. 21:08
728x90
오라클 데이터베이스의 저장 프로시저(Stored Procedure, SP)에서 발생하는 내부 오류를 상세히 파악하고, Python의 oracledb 모듈을 사용해 애플리케이션 레벨에서 이를 효과적으로 캡처하는 방법을 설명합니다. 이 글은 오라클 PL/SQL과 Python 개발자를 위해 실용적인 예제와 함께 오류 처리 기법을 다룹니다.

1. 오라클 저장 프로시저에서 내부 오류 캡처
오라클 저장 프로시저에서 오류를 캡처하려면 PL/SQL의 **예외 처리 블록(EXCEPTION)**을 사용합니다. 이를 통해 오류 코드(SQLCODE)와 오류 메시지(SQLERRM)를 기록하거나 호출한 애플리케이션으로 전달할 수 있습니다.
예제: 오류 로깅 테이블에 기록 
CREATE TABLE ERROR_LOG (
    ERROR_CODE VARCHAR2(20),
    ERROR_DESC VARCHAR2(400),
    ERROR_DATE TIMESTAMP DEFAULT SYSTIMESTAMP
);

CREATE OR REPLACE PROCEDURE MY_PROC AS
    V_SQLCODE VARCHAR2(20);
    V_SQLERRM VARCHAR2(400);
BEGIN
    -- 예외를 유발하는 코드 (예: 0으로 나누기)
    DECLARE
        V_NUM NUMBER := 1 / 0;
    BEGIN
        NULL;
    END;
EXCEPTION
    WHEN OTHERS THEN
        V_SQLCODE := SQLCODE;
        V_SQLERRM := SQLERRM;
        -- 오류 로깅 테이블에 삽입
        INSERT INTO ERROR_LOG (ERROR_CODE, ERROR_DESC)
        VALUES (V_SQLCODE, V_SQLERRM);
        COMMIT;
        -- 호출자에게 오류 전파
        RAISE_APPLICATION_ERROR(-20001, 'Error: ' || V_SQLERRM);
END;
/
  • 설명:
    • SQLCODE: 오류 코드 (예: ORA-01476은 0으로 나누기 오류).
    • SQLERRM: 오류 메시지 (최대 512바이트).
    • RAISE_APPLICATION_ERROR: 사용자 정의 오류를 호출자에게 전달.
    • 오류를 ERROR_LOG 테이블에 저장해 추후 분석 가능.
팁:
  • WHEN OTHERS는 모든 예외를 포괄하지만, 특정 예외(예: NO_DATA_FOUND, ZERO_DIVIDE)를 명시적으로 처리하는 것이 좋습니다.
  • DBMS_UTILITY.FORMAT_ERROR_STACK 또는 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE를 사용하면 더 자세한 오류 스택 정보를 얻을 수 있습니다.

 
728x90
 
2. Python oracledb로 오류 캡처
Python의 oracledb 모듈은 오라클 데이터베이스와 상호작용하며, 저장 프로시저 호출 시 발생하는 오류를 체계적으로 처리할 수 있습니다. 모든 oracledb 예외는 oracledb.Error에서 상속되며, 오류 객체에서 상세 정보를 추출할 수 있습니다.
예제: 저장 프로시저 호출 및 오류 처리 
 
import oracledb
import logging

# 로깅 설정
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def call_stored_procedure():
    try:
        # 데이터베이스 연결
        connection = oracledb.connect(user="your_user", password="your_password", dsn="localhost:1521/orclpdb")
        cursor = connection.cursor()

        # 저장 프로시저 호출
        cursor.callproc("MY_PROC")

        # 커밋 (필요한 경우)
        connection.commit()
        logging.info("Stored procedure executed successfully.")

    except oracledb.DatabaseError as e:
        error_obj, = e.args
        logging.error(f"Database error occurred: Code={error_obj.code}, Message={error_obj.message}")
        # 추가 정보 출력
        print(f"Full Code: {error_obj.full_code}")
        print(f"Context: {error_obj.context}")

    except oracledb.Error as e:
        error_obj, = e.args
        logging.error(f"oracledb error occurred: {error_obj.message}")

    except Exception as e:
        logging.critical(f"Unexpected error: {e}")

    finally:
        # 자원 정리
        if 'cursor' in locals():
            cursor.close()
        if 'connection' in locals():
            connection.close()
        logging.info("Database resources closed.")

if __name__ == "__main__":
    call_stored_procedure()
  • 설명:
    • oracledb.connect: 데이터베이스 연결 설정.
    • cursor.callproc: 저장 프로시저 호출.
    • oracledb.DatabaseError: 데이터베이스 관련 오류 (예: ORA-20001).
    • error_obj: 오류 객체로, code, message, full_code, context 속성을 제공.
    • logging: 오류를 로그 파일에 기록해 디버깅 용이.
    • finally: 연결 및 커서 자원 정리.
오류 객체 속성:
  • code: 오류 코드 (예: -20001).
  • message: 오류 메시지.
  • full_code: 전체 오류 코드 (예: ORA-20001).
  • context: 오류가 발생한 컨텍스트 정보.
Thin vs Thick 모드:
  • oracledb는 Thin(기본)과 Thick 모드를 지원합니다. Thin 모드는 Python 자체 네트워킹을 사용하며, Thick 모드는 Oracle 클라이언트 라이브러리를 사용합니다.
  • Thin 모드에서는 DPY-XXXX 접두사의 오류가 발생할 수 있으며, Thick 모드에서는 ORA-XXXX 또는 DPI-XXXX 오류가 발생할 수 있습니다.

3. 고급 오류 처리 기법
  1. 오류 스택 추적:
    • PL/SQL에서 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE를 사용해 오류 발생 위치를 추적. 
     
    EXCEPTION
        WHEN OTHERS THEN
            INSERT INTO ERROR_LOG (ERROR_CODE, ERROR_DESC)
            VALUES (SQLCODE, SQLERRM || ' Backtrace: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
            COMMIT;
            RAISE;
  2. 사용자 정의 예외:
    • Python에서 특정 오류 코드를 기반으로 커스텀 처리 로직 추가. 
     
    if error_obj.code == -20001:
        logging.warning("Custom application error detected. Retrying...")
        # 재시도 로직
  3. 비동기 처리:
    • oracledb의 비동기 메서드(AsyncCursor.execute, AsyncConnection)를 사용해 대규모 작업에서 오류 처리 최적화.
  4. 연결 풀링:
    • 다중 사용자 환경에서는 oracledb.create_pool을 사용해 연결 풀을 생성, 오류 발생 시 풀에서 새 연결을 획득하도록 설계.

 
 
4. 디버깅 팁
  • 오라클 클라이언트 로그 활성화:
    • Thick 모드에서 OCI 추적 활성화: 
       
      export EVENT_10842=15
      $HOME/instantclient/log/diag/clients 디렉토리에 로그 생성.
  • DPI 디버깅:
    • oracledb Thin 모드에서 DPI 디버깅 활성화: 
       
      oracledb.defaults.debug_dpi = True
  • SQL*Plus로 테스트:
    • 저장 프로시저를 SQL*Plus에서 실행해 예상 오류 확인: 
      EXEC MY_PROC;
      SELECT * FROM ERROR_LOG;

5. 결론
오라클 저장 프로시저에서 내부 오류를 캡처하려면 PL/SQL 예외 처리와 로깅 테이블을 활용하고, RAISE_APPLICATION_ERROR로 애플리케이션에 오류를 전달합니다. Python oracledb에서는 try-except 블록과 oracledb.Error를 사용해 오류를 체계적으로 처리하며, 로깅과 자원 정리를 철저히 수행해야 합니다. 추가로 오류 스택 추적, 사용자 정의 예외, 연결 풀링 등을 활용하면 더 robust한 애플리케이션을 구축할 수 있습니다.

 
728x90