2026년 09월 29일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-20001 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-20001 user defined error -20001 는?
ORA-20001은 Oracle 데이터베이스에서 개발자 또는 DBA가 PL/SQL 코드 내에서 RAISE_APPLICATION_ERROR 프로시저를 사용하여 의도적으로 발생시키는 사용자 정의 에러입니다. Oracle은 -20000부터 -20999까지의 에러 번호 범위를 사용자 정의 에러 전용으로 예약해 두었으며, 그 중 -20001은 가장 흔하게 사용되는 번호입니다. 이 에러는 비즈니스 로직 위반, 데이터 무결성 검사 실패, 또는 특정 조건에 부합하지 않는 상황에서 애플리케이션이 명시적으로 트랜잭션을 중단시키기 위해 발생시킵니다.
주요 발생 원인
1. RAISE_APPLICATION_ERROR를 이용한 비즈니스 규칙 위반 처리
가장 일반적인 원인으로, 개발자가 특정 비즈니스 규칙을 위반했을 때 애플리케이션 레이어로 명확한 오류 메시지를 전달하기 위해 RAISE_APPLICATION_ERROR(-20001, '메시지')를 명시적으로 호출합니다. 예를 들어, 재고 수량이 부족하거나 주문 금액이 허용 한도를 초과할 때 이 방식을 사용하여 트랜잭션을 롤백하고 에러를 상위 레이어로 전파합니다.
2. 트리거(Trigger) 내부에서의 데이터 무결성 검증 실패
데이터베이스 트리거 안에서 특정 컬럼 값의 유효성을 검사할 때 조건을 만족하지 못하면 ORA-20001이 발생합니다. 예를 들어 입력된 날짜가 과거 날짜이거나, 특정 상태 코드가 허용되지 않는 값일 경우 트리거가 자동으로 이 에러를 발생시켜 잘못된 데이터가 테이블에 저장되는 것을 방지합니다. 이 경우 에러 메시지를 보고 어떤 트리거가 활성화되었는지 추적하는 것이 중요합니다.
3. 저장 프로시저(Stored Procedure) 또는 패키지(Package) 내 예외 처리 로직
복잡한 저장 프로시저나 패키지 내부에서 예외 처리 블록(EXCEPTION WHEN OTHERS THEN)이 내부 에러를 잡아 ORA-20001로 변환하여 재발생시키는 경우가 있습니다. 이런 패턴은 내부 오류의 세부 정보를 숨기거나, 애플리케이션 표준 에러 코드로 통일하기 위해 사용되며, 원본 에러를 파악하기 위해 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 함수를 활용해야 합니다.
해결 방법
원인 1 해결: RAISE_APPLICATION_ERROR 호출 위치 파악 및 비즈니스 로직 수정
에러 메시지를 꼼꼼히 읽고 어떤 비즈니스 규칙이 위반되었는지 확인합니다. 아래 예제는 재고 부족 시 에러를 발생시키는 프로시저와, 이를 호출하는 측에서 예외를 처리하는 방법입니다.
-- 비즈니스 규칙 검사가 포함된 프로시저 예시
CREATE OR REPLACE PROCEDURE update_inventory(
p_product_id IN NUMBER,
p_quantity IN NUMBER
) AS
v_current_stock NUMBER;
BEGIN
SELECT stock_qty INTO v_current_stock
FROM products
WHERE product_id = p_product_id
FOR UPDATE;
IF v_current_stock < p_quantity THEN
RAISE_APPLICATION_ERROR(
-20001,
'재고 부족: 현재 재고(' || v_current_stock || ')가 요청 수량(' || p_quantity || ')보다 적습니다.'
);
END IF;
UPDATE products
SET stock_qty = stock_qty - p_quantity
WHERE product_id = p_product_id;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE; -- 에러를 상위 레이어로 전파
END update_inventory;
/
-- 호출 측에서 ORA-20001 처리 예시
BEGIN
update_inventory(101, 500);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -20001 THEN
DBMS_OUTPUT.PUT_LINE('사용자 정의 에러 발생: ' || SQLERRM);
-- 필요한 경우 대체 로직 수행
ELSE
RAISE;
END IF;
END;
/
원인 2 해결: 트리거 내부 로직 확인 및 검토
어떤 테이블에 대한 DML 작업 중 에러가 발생했는지 파악한 뒤, 해당 테이블에 연결된 트리거를 조회합니다.
-- 특정 테이블에 연결된 트리거 조회
SELECT trigger_name,
trigger_type,
triggering_event,
status
FROM user_triggers
WHERE table_name = 'ORDERS' -- 에러 발생 테이블명으로 변경
ORDER BY trigger_name;
-- 트리거 소스 코드 확인
SELECT text
FROM user_source
WHERE name = 'TRG_ORDERS_VALIDATE' -- 해당 트리거 이름
AND type = 'TRIGGER'
ORDER BY line;
-- 문제가 있는 트리거 수정 예시
CREATE OR REPLACE TRIGGER trg_orders_validate
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW
BEGIN
-- 주문 날짜 유효성 검사
IF :NEW.order_date < TRUNC(SYSDATE) THEN
RAISE_APPLICATION_ERROR(
-20001,
'주문 날짜는 오늘(' || TO_CHAR(SYSDATE, 'YYYY-MM-DD') || ') 이후여야 합니다.'
);
END IF;
-- 주문 금액 한도 검사
IF :NEW.order_amount > 10000000 THEN
RAISE_APPLICATION_ERROR(
-20002, -- 에러 구분을 위해 다른 번호 사용 권장
'단일 주문 한도(10,000,000원)를 초과했습니다.'
);
END IF;
END trg_orders_validate;
/
원인 3 해결: 원본 에러 추적 및 백트레이스 활용
EXCEPTION 블록에서 에러가 래핑되어 ORA-20001로 변환된 경우, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE를 활용해 원본 에러의 발생 위치를 추적합니다.
-- 에러 백트레이스를 활용한 디버깅 프로시저 예시
CREATE OR REPLACE PROCEDURE complex_process(p_id IN NUMBER) AS
v_error_msg VARCHAR2(4000);
v_backtrace VARCHAR2(4000);
BEGIN
-- 복잡한 비즈니스 로직 수행
process_step1(p_id);
process_step2(p_id);
process_step3(p_id);
EXCEPTION
WHEN OTHERS THEN
v_error_msg := SQLERRM;
v_backtrace := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
-- 에러 로그 테이블에 기록
INSERT INTO error_log (
log_date, error_code, error_message, backtrace, process_name
) VALUES (
SYSDATE,
SQLCODE,
v_error_msg,
v_backtrace,
'complex_process'
);
COMMIT;
-- 표준 에러 코드로 변환하여 재발생
RAISE_APPLICATION_ERROR(
-20001,
'처리 중 오류 발생. 로그를 확인하세요. 원본 에러: ' || v_error_msg
);
END complex_process;
/
-- 에러 로그 테이블 생성 (사전에 필요)
CREATE TABLE error_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
log_date DATE,
error_code NUMBER,
error_message VARCHAR2(4000),
backtrace VARCHAR2(4000),
process_name VARCHAR2(100)
);
예방 방법
1. 사용자 정의 에러 코드 표준화 및 문서화
팀 내에서 -20001부터 -20999 범위의 에러 코드를 체계적으로 관리하는 표준을 수립해야 합니다. 각 에러 코드에 대한 의미, 발생 조건, 처리 방법을 문서화하고, 에러 코드 관리 테이블을 데이터베이스에 유지하면 운영 중 혼란을 크게 줄일 수 있습니다. 무분별하게 -20001만 사용하는 것을 지양하고, 비즈니스 도메인별로 번호 범위를 분리하여 사용하는 것이 좋습니다.
-- 에러 코드 관리 테이블 예시
CREATE TABLE app_error_codes (
error_code NUMBER PRIMARY KEY,
error_name VARCHAR2(100) NOT NULL,
description VARCHAR2(500),
domain VARCHAR2(50),
created_date DATE DEFAULT SYSDATE
);
INSERT INTO app_error_codes VALUES (-20001, 'ERR_INVENTORY_SHORTAGE', '재고 부족 오류', 'INVENTORY', SYSDATE);
INSERT INTO app_error_codes VALUES (-20002, 'ERR_ORDER_LIMIT_EXCEEDED', '주문 한도 초과 오류', 'ORDER', SYSDATE);
INSERT INTO app_error_codes VALUES (-20010, 'ERR_INVALID_DATE', '유효하지 않은 날짜 오류', 'COMMON', SYSDATE);
COMMIT;
2. 공통 에러 처리 패키지 구현
애플리케이션 전반에서 일관된 방식으로 에러를 처리하고 로깅하기 위한 공통 패키지를 구현하면 유지보수성이 크게 향상됩니다. 에러 발생 시 자동으로 로그를 남기고 표준화된 메시지를 반환하는 패키지를 만들어 모든 프로시저와 트리거에서 재사용하면, 에러 추적과 디버깅이 훨씬 쉬워집니다.
-- 공통 에러 처리 패키지 예시
CREATE OR REPLACE PACKAGE pkg_error_handler AS
PROCEDURE raise_error(
p_error_code IN NUMBER,
p_message IN VARCHAR2 DEFAULT NULL
);
PROCEDURE log_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_process_name IN VARCHAR2
);
END pkg_error_handler;
/
CREATE OR REPLACE PACKAGE BODY pkg_error_handler AS
PROCEDURE raise_error(
p_error_code IN NUMBER,
p_message IN VARCHAR2 DEFAULT NULL
) AS
v_base_msg VARCHAR2(500);
BEGIN
SELECT NVL(p_message, description)
INTO v_base_msg
FROM app_error_codes
WHERE error_code = p_error_code;
log_error(p_error_code, v_base_msg, 'SYSTEM');
RAISE_APPLICATION_ERROR(p_error_code, v_base_msg);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20001, NVL(p_message, '알 수 없는 오류가 발생했습니다.'));
END raise_error;
PROCEDURE log_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_process_name IN VARCHAR2
) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO error_log (log_date, error_code, error_message, process_name)
VALUES (SYSDATE, p_error_code, p_error_msg, p_process_name);
COMMIT;
END log_error;
END pkg_error_handler;
/
관련 에러
- ORA-20000 ~ ORA-20999: Oracle이 사용자 정의 에러 전용으로 예약한 번호 범위로, ORA-20001과 동일한 메커니즘으로 동작합니다. 실무에서는 -20001을 범용적으로 사용하는 경우가 많지만, 에러 코드를 세분화하여 사용하는 것을 강력히 권장합니다.
- ORA-06512: 스택 트레이스 정보를 포함하는 에러로, ORA-20001 발생 시 함께 출력되어 에러가 발생한 PL/SQL 라인 번호를 알려줍니다. 디버깅 시 반드시 함께 확인해야 합니다.
- ORA-04088: 트리거 실행 중 에러가 발생했을 때 나타나는 에러로, 트리거 내부에서 ORA-20001이 발생한 경우 함께 출력될 수 있습니다.
- ORA-01403 (NO_DATA_FOUND): 저장 프로시저 내에서 SELECT INTO 구문이 결과를 반환하지 못할 때 발생하며, 이를 EXCEPTION 블록에서 ORA-20001로 변환하는 패턴이 자주 사용됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.