예외 처리 (Exception Handling)

오라클 PL/SQL 예외 처리: 안정적인 데이터베이스 애플리케이션 구축

오라클 데이터베이스에서 PL/SQL을 사용하여 애플리케이션을 개발할 때, 예외 처리는 코드의 안정성과 신뢰성을 확보하는 데 매우 중요합니다. 예외 처리를 통해 예상치 못한 오류 발생 시 애플리케이션이 비정상적으로 종료되는 것을 방지하고, 오류 상황에 적절히 대응하여 데이터의 무결성을 유지할 수 있습니다.

예외 처리의 중요성

  • 안정성 확보: 예외 처리는 런타임 오류 발생 시 애플리케이션이 중단되지 않고 정상적으로 실행될 수 있도록 합니다.
  • 데이터 무결성 유지: 오류 발생 시 데이터베이스의 상태를 일관성 있게 유지하여 데이터 손실이나 불일치를 방지합니다.
  • 유지보수 용이성 증대: 명확하고 체계적인 예외 처리 코드는 오류 발생 원인 파악 및 수정 작업을 간소화합니다.

기본적인 예외 처리 구문

PL/SQL에서 예외 처리는 BEGIN, EXCEPTION, END 블록을 사용하여 수행됩니다.


DECLARE
  -- 변수 선언
  v_salary NUMBER;
BEGIN
  -- SQL 문 또는 PL/SQL 코드
  SELECT salary INTO v_salary FROM employees WHERE employee_id = 100;

  -- 예외 처리 블록
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    -- 예외 처리 코드: employee_id가 100인 직원이 없을 경우 처리
    DBMS_OUTPUT.PUT_LINE('해당 직원이 없습니다.');
  WHEN OTHERS THEN
    -- 예외 처리 코드: 그 외 모든 예외 처리
    DBMS_OUTPUT.PUT_LINE('알 수 없는 오류 발생: ' || SQLERRM);
END;
/

설명:

  • DECLARE: 변수 선언 영역입니다.
  • BEGIN: 실행 가능한 SQL 문 또는 PL/SQL 코드 블록이 시작됩니다.
  • EXCEPTION: 예외 처리 블록이 시작됩니다.
  • WHEN: 특정 예외가 발생했을 때 실행할 코드를 지정합니다.
    • NO_DATA_FOUND: SELECT 문으로 데이터를 찾지 못했을 때 발생하는 예외입니다.
    • OTHERS: 사전에 정의되지 않은 모든 예외를 처리합니다. SQLERRM 함수를 사용하여 오류 메시지를 얻을 수 있습니다.
  • END: PL/SQL 블록이 종료됩니다.

자주 사용되는 예외 유형

PL/SQL에서 자주 사용되는 예외 유형은 다음과 같습니다.

  • NO_DATA_FOUND: SELECT 문이 행을 반환하지 않을 때 발생합니다.
  • TOO_MANY_ROWS: SELECT INTO 문이 둘 이상의 행을 반환할 때 발생합니다.
  • INVALID_NUMBER: 숫자 형식으로 변환할 수 없는 문자열을 변환하려고 할 때 발생합니다.
  • ZERO_DIVIDE: 0으로 나누기를 시도할 때 발생합니다.
  • DUP_VAL_ON_INDEX: 고유 인덱스에 중복된 값을 삽입하려고 할 때 발생합니다.
  • VALUE_ERROR: 산술 오류, 문자열 조작 오류, 크기 제한 초과 등 다양한 값 관련 오류 발생 시 발생합니다.

사용자 정의 예외

애플리케이션의 특정 요구 사항에 맞는 사용자 정의 예외를 정의하고 사용할 수 있습니다.


DECLARE
  -- 사용자 정의 예외 선언
  insufficient_funds EXCEPTION;
  v_balance NUMBER := 100;
  v_withdrawal NUMBER := 200;
BEGIN
  -- 잔액 확인
  IF v_withdrawal > v_balance THEN
    -- 예외 발생
    RAISE insufficient_funds;
  END IF;

  -- 출금 처리
  v_balance := v_balance - v_withdrawal;
  DBMS_OUTPUT.PUT_LINE('출금 성공. 잔액: ' || v_balance);

EXCEPTION
  WHEN insufficient_funds THEN
    -- 예외 처리 코드
    DBMS_OUTPUT.PUT_LINE('잔액 부족!');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('알 수 없는 오류 발생: ' || SQLERRM);
END;
/

설명:

  • EXCEPTION 키워드 다음에 사용자 정의 예외 이름을 선언합니다.
  • RAISE 문을 사용하여 사용자 정의 예외를 발생시킵니다.

예외 전파

PL/SQL 블록 내에서 처리되지 않은 예외는 호출 스택을 따라 전파됩니다. 예외가 최상위 블록까지 전파되어도 처리되지 않으면 애플리케이션이 비정상적으로 종료됩니다.


CREATE OR REPLACE PROCEDURE inner_proc IS
BEGIN
  RAISE VALUE_ERROR; -- 예외 발생
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('inner_proc에서 예외 처리');
    RAISE; -- 예외 다시 발생
END;
/

CREATE OR REPLACE PROCEDURE outer_proc IS
BEGIN
  inner_proc;
EXCEPTION
  WHEN VALUE_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('outer_proc에서 VALUE_ERROR 예외 처리');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('outer_proc에서 다른 예외 처리: ' || SQLERRM);
END;
/

BEGIN
  outer_proc;
END;
/

RAISE 문과 RAISE APPLICATION_ERROR 프로시저

예외를 명시적으로 발생시키는 방법은 RAISE 문과 RAISE APPLICATION_ERROR 프로시저를 사용하는 것입니다.

RAISE Statement

RAISE 문은 미리 정의된 예외 또는 사용자 정의 예외를 발생시킬 때 사용합니다. 사용자 정의 예외를 사용하기 전에 먼저 DECLARE 섹션에서 예외를 정의해야 합니다.


DECLARE
  my_exception EXCEPTION; -- 사용자 정의 예외 정의
BEGIN
  -- 조건에 따라 예외 발생
  IF (condition) THEN
    RAISE my_exception;
  END IF;
EXCEPTION
  WHEN my_exception THEN
    -- 예외 처리 로직
    DBMS_OUTPUT.PUT_LINE('사용자 정의 예외 발생!');
END;
/

RAISE APPLICATION_ERROR 프로시저

RAISE APPLICATION_ERROR 프로시저는 미리 정의된 범위(예: -20000 ~ -20999) 내의 오류 코드를 사용하여 사용자 정의 오류 메시지를 반환할 때 사용됩니다. 이 방법은 특히 애플리케이션 로직과 관련된 특정 오류 조건을 나타내는 데 유용합니다.


BEGIN
  -- 조건에 따라 애플리케이션 에러 발생
  IF (condition) THEN
    RAISE_APPLICATION_ERROR(-20001, '유효하지 않은 데이터입니다.');
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    -- 예외 처리 로직 (선택적)
    DBMS_OUTPUT.PUT_LINE('애플리케이션 에러 발생!');
END;
/

SQLCODESQLERRM 사용

오라클은 예외 처리 시 유용한 정보를 제공하는 내장 함수 SQLCODESQLERRM 을 제공합니다.

  • SQLCODE: 가장 최근에 실행된 SQL 문의 오류 코드를 반환합니다. 성공적인 실행 시 0을 반환하고, 오류 발생 시 0이 아닌 값을 반환합니다.
  • SQLERRM: 가장 최근에 발생한 오류와 관련된 오류 메시지를 반환합니다.

BEGIN
  -- ... 코드 ...
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('오류 코드: ' || SQLCODE);
    DBMS_OUTPUT.PUT_LINE('오류 메시지: ' || SQLERRM);
END;
/

예외 처리 모범 사례

  • 구체적인 예외 처리: OTHERS 절을 남용하지 않고 가능한 한 구체적인 예외를 처리합니다.
  • 로깅: 예외 정보를 로그에 기록하여 디버깅 및 감사에 활용합니다.
  • 예외 전파 방지: 불필요한 예외 전파를 방지하고, 예외를 적절한 수준에서 처리합니다.
  • 정리 작업: 예외 발생 시 롤백, 자원 해제 등 필요한 정리 작업을 수행합니다.
  • 커밋 전 예외 처리: 커밋 전에 발생한 예외는 롤백하여 데이터 무결성을 유지합니다.

다양한 예외 처리 예시

이제 실제 시나리오에서 예외 처리가 어떻게 적용될 수 있는지 다양한 예시를 살펴보겠습니다.

1. 데이터 삽입 시 예외 처리

데이터 삽입 시 발생하는 제약 조건 위반 예외를 처리하는 예시입니다.


DECLARE
  dup_val_on_index EXCEPTION;
  PRAGMA EXCEPTION_INIT(dup_val_on_index, -00001); -- ORA-00001: unique constraint violated
BEGIN
  INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, job_id, salary)
  VALUES (100, 'John', 'Doe', 'john.doe@example.com', SYSDATE, 'IT_PROG', 6000);
  COMMIT;

EXCEPTION
  WHEN dup_val_on_index THEN
    DBMS_OUTPUT.PUT_LINE('Error: Unique constraint violated. Employee ID already exists.');
END;
/

2. 데이터 수정 시 예외 처리

데이터 수정 시 존재하지 않는 데이터에 접근하는 경우 발생하는 예외를 처리하는 예시입니다.


DECLARE
  no_data_found EXCEPTION;
  PRAGMA EXCEPTION_INIT(no_data_found, -01403); -- ORA-01403: no data found
BEGIN
  UPDATE employees SET salary = 7000 WHERE employee_id = 999;

  IF SQL%NOTFOUND THEN -- 업데이트된 행이 없는 경우
    RAISE no_data_found;
  ELSE
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('Salary updated successfully.');
  END IF;

EXCEPTION
  WHEN no_data_found THEN
    DBMS_OUTPUT.PUT_LINE('Error: Employee with ID 999 not found.');
END;
/

3. 함수 내에서 예외 처리

함수 내에서 예외를 처리하고, 발생한 예외를 호출자에게 다시 전달하는 예시입니다.


CREATE OR REPLACE FUNCTION get_employee_salary (p_employee_id NUMBER) RETURN NUMBER IS
  v_salary NUMBER;
BEGIN
  SELECT salary INTO v_salary FROM employees WHERE employee_id = p_employee_id;
  RETURN v_salary;

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('해당 직원이 없습니다.');
    RETURN NULL; -- 혹은 특정 오류 코드를 반환
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('알 수 없는 오류 발생: ' || SQLERRM);
    RETURN NULL;
END;
/

DECLARE
  emp_salary NUMBER;
BEGIN
  emp_salary := get_employee_salary(1000); -- 존재하지 않는 직원 ID

  IF emp_salary IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('직원 급여 정보를 가져오는데 실패했습니다.');
  ELSE
    DBMS_OUTPUT.PUT_LINE('직원 급여: ' || emp_salary);
  END IF;
END;
/

4. 저장 프로시저에서 예외 처리

저장 프로시저 내에서 데이터베이스 작업을 수행하고, 예외 발생 시 롤백하는 예시입니다.


CREATE OR REPLACE PROCEDURE transfer_funds (
  p_from_account NUMBER,
  p_to_account NUMBER,
  p_amount NUMBER
) IS
  insufficient_funds EXCEPTION;
  PRAGMA EXCEPTION_INIT(insufficient_funds, -20002); -- 사용자 정의 에러 코드 정의
  v_from_balance NUMBER;
BEGIN
  -- 출금 계좌 잔액 확인
  SELECT balance INTO v_from_balance FROM accounts WHERE account_id = p_from_account;
  IF v_from_balance < p_amount THEN
    RAISE insufficient_funds;
  END IF;

  -- 출금 처리
  UPDATE accounts SET balance = balance - p_amount WHERE account_id = p_from_account;

  -- 입금 처리
  UPDATE accounts SET balance = balance + p_amount WHERE account_id = p_to_account;

  COMMIT;
  DBMS_OUTPUT.PUT_LINE('계좌 이체 성공!');

EXCEPTION
  WHEN insufficient_funds THEN
    ROLLBACK;
    DBMS_OUTPUT.PUT_LINE('잔액 부족으로 이체 실패!');
  WHEN OTHERS THEN
    ROLLBACK;
    DBMS_OUTPUT.PUT_LINE('알 수 없는 오류 발생: ' || SQLERRM);
END;
/

결론

오라클 PL/SQL에서 예외 처리는 견고하고 신뢰할 수 있는 애플리케이션을 구축하는 데 필수적인 요소입니다. 체계적인 예외 처리 전략을 통해 런타임 오류를 효과적으로 관리하고, 데이터 무결성을 유지하며, 애플리케이션의 유지보수성을 향상시킬 수 있습니다. 제시된 예시들을 바탕으로 자신의 환경에 맞는 예외 처리 코드를 구현하여 안정적인 데이터베이스 애플리케이션을 개발하시기 바랍니다.

위로 스크롤