오라클 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;
/
SQLCODE
와 SQLERRM
사용
오라클은 예외 처리 시 유용한 정보를 제공하는 내장 함수 SQLCODE
와 SQLERRM
을 제공합니다.
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에서 예외 처리는 견고하고 신뢰할 수 있는 애플리케이션을 구축하는 데 필수적인 요소입니다. 체계적인 예외 처리 전략을 통해 런타임 오류를 효과적으로 관리하고, 데이터 무결성을 유지하며, 애플리케이션의 유지보수성을 향상시킬 수 있습니다. 제시된 예시들을 바탕으로 자신의 환경에 맞는 예외 처리 코드를 구현하여 안정적인 데이터베이스 애플리케이션을 개발하시기 바랍니다.