2026년 09월 30일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-20005 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-20005 user defined error -20005 는?
ORA-20005는 Oracle에서 사용자가 직접 정의한 애플리케이션 레벨의 오류로, RAISE_APPLICATION_ERROR 프로시저를 통해 개발자가 의도적으로 발생시키는 에러입니다. Oracle은 사용자 정의 에러를 위해 -20000부터 -20999까지의 에러 번호 범위를 예약해 두고 있으며, ORA-20005는 그 중 하나입니다. 즉, 데이터베이스 엔진 자체의 문제가 아니라, 비즈니스 로직이나 데이터 유효성 검사 실패 시 개발자가 명시적으로 예외를 던지도록 설계된 메커니즘입니다.
주요 발생 원인
1. 비즈니스 로직 위반 (Business Rule Violation)
가장 흔한 원인으로, 특정 비즈니스 규칙을 위반했을 때 PL/SQL 코드 내부에서 RAISE_APPLICATION_ERROR(-20005, '메시지')를 명시적으로 호출하여 발생합니다. 예를 들어, 재고 수량이 0 이하인 상태에서 출고 처리를 시도하거나, 특정 상태값이 아닌 데이터를 변경하려 할 때 트리거 또는 저장 프로시저가 이 에러를 발생시킵니다. 이 경우 에러는 의도된 것이며, 에러 메시지와 함께 발생 컨텍스트를 파악하는 것이 핵심입니다.
2. 데이터 유효성 검사 실패 (Data Validation Failure)
입력 데이터가 허용된 범위나 형식을 벗어났을 때 유효성 검사 로직이 이 에러를 발생시킵니다. 예를 들어, 특정 컬럼에 NULL 값이 허용되지 않는 비즈니스 규칙이 있음에도 NULL이 입력되었거나, 날짜 범위가 유효하지 않은 경우가 해당됩니다. 트리거(Trigger)나 저장 프로시저(Stored Procedure)의 유효성 검사 블록에서 주로 발생하며, 애플리케이션 레이어의 검증을 우회하여 DB에 직접 데이터를 입력할 때 자주 나타납니다.
3. 트리거 내부 조건 충족 (Trigger Condition Met)
DML(INSERT, UPDATE, DELETE) 작업 수행 시 해당 테이블에 정의된 트리거가 특정 조건을 만족하여 RAISE_APPLICATION_ERROR를 호출하는 경우입니다. 예를 들어, 특정 기간에만 데이터 수정을 허용하는 정책이 있을 때, 허용 기간 외에 UPDATE를 시도하면 트리거가 이 에러를 발생시킵니다. 트리거의 존재 자체를 모르고 작업하는 신규 개발자나 DBA가 당황하는 경우가 많습니다.
해결 방법
원인 1: 비즈니스 로직 위반 해결
먼저 에러 메시지를 정확히 확인하고, 어느 프로시저 또는 패키지에서 발생했는지 추적합니다.
-- 에러 발생 위치 확인: DBMS_UTILITY를 활용한 콜 스택 출력
CREATE OR REPLACE PROCEDURE check_stock_and_ship(
p_item_id IN NUMBER,
p_quantity IN NUMBER
) AS
v_stock NUMBER;
BEGIN
SELECT stock_qty INTO v_stock
FROM inventory
WHERE item_id = p_item_id;
IF v_stock < p_quantity THEN
-- ORA-20005 의도적 발생 지점
RAISE_APPLICATION_ERROR(-20005,
'재고 부족: 요청 수량(' || p_quantity ||
')이 현재 재고(' || v_stock || ')를 초과합니다.');
END IF;
-- 출고 처리 로직
UPDATE inventory
SET stock_qty = stock_qty - p_quantity
WHERE item_id = p_item_id;
COMMIT;
END;
/
-- 호출 시 예외 처리 포함
BEGIN
check_stock_and_ship(101, 500);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error Code : ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('Error Message: ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('Call Stack : ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
ROLLBACK;
END;
/
원인 2: 데이터 유효성 검사 실패 해결
유효성 검사 로직을 확인하고, 올바른 데이터를 입력하거나 유효성 검사 조건을 검토합니다.
-- 유효성 검사 프로시저 예시 및 예외 처리
CREATE OR REPLACE PROCEDURE insert_employee(
p_emp_id IN NUMBER,
p_emp_name IN VARCHAR2,
p_hire_date IN DATE,
p_salary IN NUMBER
) AS
BEGIN
-- 데이터 유효성 검사
IF p_emp_name IS NULL OR LENGTH(TRIM(p_emp_name)) = 0 THEN
RAISE_APPLICATION_ERROR(-20005, '직원 이름은 필수 입력 값입니다.');
END IF;
IF p_hire_date > SYSDATE THEN
RAISE_APPLICATION_ERROR(-20005, '입사일은 오늘 이후 날짜를 입력할 수 없습니다.');
END IF;
IF p_salary <= 0 THEN
RAISE_APPLICATION_ERROR(-20005, '급여는 0보다 커야 합니다. 입력값: ' || p_salary);
END IF;
INSERT INTO employees(emp_id, emp_name, hire_date, salary)
VALUES (p_emp_id, p_emp_name, p_hire_date, p_salary);
COMMIT;
END;
/
-- 올바른 데이터로 재시도
BEGIN
insert_employee(1001, '홍길동', SYSDATE - 30, 5000000);
DBMS_OUTPUT.PUT_LINE('정상 처리 완료');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
ROLLBACK;
END;
/
원인 3: 트리거 내부 조건 – 트리거 확인 및 임시 비활성화
-- 특정 테이블의 트리거 목록 조회
SELECT trigger_name, trigger_type, triggering_event, status
FROM user_triggers
WHERE table_name = 'ORDERS'
ORDER BY trigger_name;
-- 트리거 소스 코드 확인 (어떤 조건에서 ORA-20005를 발생시키는지 분석)
SELECT text
FROM user_source
WHERE name = 'TRG_ORDERS_VALIDATION'
AND type = 'TRIGGER'
ORDER BY line;
-- 분석이 완료된 후 DBA 승인 하에 임시 비활성화 (운영 환경 주의)
ALTER TRIGGER TRG_ORDERS_VALIDATION DISABLE;
-- 작업 완료 후 반드시 재활성화
ALTER TRIGGER TRG_ORDERS_VALIDATION ENABLE;
-- 에러 발생 시점의 정확한 트리거 내용 예시
CREATE OR REPLACE TRIGGER TRG_ORDERS_VALIDATION
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW
DECLARE
v_allowed_start DATE := TRUNC(SYSDATE, 'MM'); -- 월 시작일
v_allowed_end DATE := LAST_DAY(SYSDATE); -- 월 말일
BEGIN
IF SYSDATE NOT BETWEEN v_allowed_start AND v_allowed_end + 1 THEN
RAISE_APPLICATION_ERROR(-20005,
'주문 데이터는 당월(' ||
TO_CHAR(v_allowed_start, 'YYYY-MM') ||
')에만 수정 가능합니다.');
END IF;
END;
/
예방 방법
1. 표준화된 에러 코드 및 메시지 관리 체계 수립
팀 또는 조직 내에서 -20000 ~ -20999 범위의 에러 번호를 체계적으로 관리하는 에러 코드 테이블을 운영하고, 각 번호의 사용 용도와 담당 모듈을 명확히 문서화해야 합니다. 아래와 같이 에러 코드를 중앙화된 패키지로 관리하면 유지보수성이 크게 향상됩니다.
-- 에러 코드 중앙 관리 패키지 예시
CREATE OR REPLACE PACKAGE pkg_error_codes AS
-- 재고 관련 에러
C_ERR_STOCK_INSUFFICIENT CONSTANT NUMBER := -20005;
C_MSG_STOCK_INSUFFICIENT CONSTANT VARCHAR2(200) := '재고 수량이 부족합니다.';
-- 데이터 유효성 에러
C_ERR_INVALID_DATA CONSTANT NUMBER := -20010;
C_MSG_INVALID_DATA CONSTANT VARCHAR2(200) := '입력 데이터가 유효하지 않습니다.';
-- 공통 에러 발생 프로시저
PROCEDURE raise_error(p_code IN NUMBER, p_detail IN VARCHAR2 DEFAULT NULL);
END pkg_error_codes;
/
CREATE OR REPLACE PACKAGE BODY pkg_error_codes AS
PROCEDURE raise_error(p_code IN NUMBER, p_detail IN VARCHAR2 DEFAULT NULL) IS
v_msg VARCHAR2(4000);
BEGIN
CASE p_code
WHEN C_ERR_STOCK_INSUFFICIENT THEN v_msg := C_MSG_STOCK_INSUFFICIENT;
WHEN C_ERR_INVALID_DATA THEN v_msg := C_MSG_INVALID_DATA;
ELSE v_msg := '정의되지 않은 에러입니다.';
END CASE;
IF p_detail IS NOT NULL THEN
v_msg := v_msg || ' [상세: ' || p_detail || ']';
END IF;
RAISE_APPLICATION_ERROR(p_code, v_msg);
END raise_error;
END pkg_error_codes;
/
2. 전역 예외 핸들러 및 에러 로깅 구현
모든 PL/SQL 코드에서 예외가 발생했을 때 에러 정보를 별도의 로그 테이블에 저장하는 공통 에러 핸들러를 구현하면, ORA-20005 발생 시 원인 추적이 훨씬 용이해집니다. 이를 통해 반복적으로 발생하는 비즈니스 로직 위반 패턴을 파악하고 근본 원인을 제거할 수 있습니다.
-- 에러 로그 테이블 생성
CREATE TABLE app_error_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
error_code NUMBER,
error_message VARCHAR2(4000),
program_name VARCHAR2(100),
user_name VARCHAR2(100),
log_timestamp TIMESTAMP DEFAULT SYSTIMESTAMP,
call_stack CLOB
);
-- 공통 에러 로깅 프로시저
CREATE OR REPLACE PROCEDURE log_error(p_program_name IN VARCHAR2) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO app_error_log (error_code, error_message, program_name, user_name, call_stack)
VALUES (SQLCODE, SQLERRM, p_program_name, USER, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
COMMIT;
END;
/
관련 에러
- ORA-20000 ~ ORA-20999: 모두 사용자 정의 에러 범위로, ORA-20005와 동일한 메커니즘으로 발생합니다. 각 번호는 애플리케이션의 설계에 따라 다른 의미를 가집니다.
- ORA-06512:
RAISE_APPLICATION_ERROR호출 시 스택 트레이스에 함께 나타나는 에러로, 에러가 발생한 라인 번호와 프로그램 단위(패키지, 프로시저 등)를 알려줍니다. ORA-20005 분석 시 반드시 함께 확인해야 합니다. - ORA-01403 (NO_DATA_FOUND) 및 ORA-06502 (VALUE_ERROR): 유효성 검사 로직 내부에서 이들 에러가 처리되지 않은 채 ORA-20005를 발생시키는 흐름의 선행 원인이 되는 경우가 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.