2026년 09월 29일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-20000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-20000 user defined error via raise_application_error 는?
ORA-20000은 Oracle에서 개발자가 의도적으로 발생시키는 사용자 정의 에러로, RAISE_APPLICATION_ERROR 프로시저를 통해 애플리케이션 레벨의 예외를 명시적으로 던질 때 나타납니다. 에러 번호 범위는 -20000부터 -20999까지이며, 개발자가 비즈니스 로직 위반, 데이터 유효성 검사 실패, 또는 특정 조건 불충족 시 사용자에게 의미 있는 메시지를 전달하기 위해 활용합니다. 단순한 시스템 에러가 아닌 만큼, 에러 메시지 자체에 실패 원인이 담겨 있어 이를 분석하는 것이 문제 해결의 핵심입니다.
주요 발생 원인
1. 비즈니스 규칙 위반 (Business Rule Violation)
가장 흔한 원인으로, 개발자가 PL/SQL 트리거, 프로시저, 함수 내에서 비즈니스 로직을 강제하기 위해 RAISE_APPLICATION_ERROR를 명시적으로 호출하는 경우입니다. 예를 들어 재고가 부족한 상태에서 출고를 시도하거나, 결재 한도를 초과한 금액이 입력될 때 해당 에러가 발생합니다. 이 경우 에러 메시지를 정확히 읽으면 어떤 비즈니스 규칙이 깨졌는지 바로 파악할 수 있습니다.
2. 데이터 유효성 검사 실패 (Data Validation Failure)
입력 데이터가 사전에 정의된 형식, 범위, 또는 참조 무결성 조건을 만족하지 못할 때 트리거나 프로시저가 에러를 발생시킵니다. 예를 들어 NULL이 허용되지 않는 필드에 NULL 값이 전달되거나, 날짜 범위가 올바르지 않은 경우가 해당됩니다. 이런 경우 애플리케이션 측 입력 검증 로직과 데이터베이스 측 제약 조건 모두를 점검해야 합니다.
3. 권한 및 상태 체크 실패 (Authorization / State Check Failure)
특정 작업을 수행하기 위한 권한이 없거나, 데이터가 올바른 상태가 아닐 때 PL/SQL 코드가 에러를 발생시킵니다. 예를 들어 이미 마감 처리된 주문에 대해 수정을 시도하거나, 비활성화된 사용자 계정으로 특정 트랜잭션을 수행하려 할 때 발생합니다. 이 경우 해당 데이터의 현재 상태와 처리 흐름을 우선 확인해야 합니다.
해결 방법
원인 1: 비즈니스 규칙 위반 해결
에러 메시지를 통해 어떤 프로시저 또는 트리거에서 발생했는지 파악한 후, 해당 코드를 검토합니다.
-- 에러를 발생시키는 프로시저 예시
CREATE OR REPLACE PROCEDURE process_order(p_order_id IN NUMBER, p_qty IN NUMBER) AS
v_stock NUMBER;
BEGIN
SELECT stock_qty INTO v_stock
FROM inventory
WHERE product_id = p_order_id;
IF v_stock < p_qty THEN
RAISE_APPLICATION_ERROR(-20001, '재고 부족: 요청 수량(' || p_qty || ')이 현재 재고(' || v_stock || ')를 초과합니다.');
END IF;
-- 정상 처리 로직
UPDATE inventory SET stock_qty = stock_qty - p_qty WHERE product_id = p_order_id;
COMMIT;
END;
/
-- 에러 발생 호출 예시
BEGIN
process_order(101, 9999); -- 재고보다 많은 수량 요청
END;
/
-- ORA-20001: 재고 부족 메시지 출력
-- 해결: 호출 전 재고 확인
DECLARE
v_available_stock NUMBER;
BEGIN
SELECT stock_qty INTO v_available_stock
FROM inventory
WHERE product_id = 101;
IF v_available_stock >= 100 THEN
process_order(101, 100); -- 재고 범위 내 수량으로 호출
DBMS_OUTPUT.PUT_LINE('주문 처리 완료');
ELSE
DBMS_OUTPUT.PUT_LINE('재고 부족으로 주문 불가: 현재 재고 = ' || v_available_stock);
END IF;
END;
/
원인 2: 데이터 유효성 검사 실패 해결
트리거 내 유효성 검사 로직을 확인하고, 입력 값을 올바르게 수정합니다.
-- 유효성 검사 트리거 예시
CREATE OR REPLACE TRIGGER trg_validate_employee
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
-- 급여 범위 검사
IF :NEW.salary < 0 OR :NEW.salary > 100000000 THEN
RAISE_APPLICATION_ERROR(-20010, '급여는 0 이상 100,000,000 이하이어야 합니다. 입력값: ' || :NEW.salary);
END IF;
-- 입사일 검사
IF :NEW.hire_date > SYSDATE THEN
RAISE_APPLICATION_ERROR(-20011, '입사일은 현재 날짜보다 미래일 수 없습니다. 입력값: ' || TO_CHAR(:NEW.hire_date, 'YYYY-MM-DD'));
END IF;
-- 이메일 형식 기본 검사
IF :NEW.email IS NULL OR INSTR(:NEW.email, '@') = 0 THEN
RAISE_APPLICATION_ERROR(-20012, '유효한 이메일 주소를 입력하세요.');
END IF;
END;
/
-- 에러 발생 INSERT 예시
INSERT INTO employees (employee_id, first_name, salary, hire_date, email)
VALUES (9999, 'TEST', -5000, SYSDATE, 'invalid_email');
-- ORA-20010 또는 ORA-20012 발생
-- 해결: 올바른 데이터로 재시도
INSERT INTO employees (employee_id, first_name, salary, hire_date, email)
VALUES (9999, 'TEST', 5000000, SYSDATE, 'test@company.com');
COMMIT;
원인 3: 권한 및 상태 체크 실패 해결
데이터 상태를 먼저 조회하여 처리 가능 여부를 확인하고 적절히 대응합니다.
-- 상태 체크 프로시저 예시
CREATE OR REPLACE PROCEDURE modify_order(p_order_id IN NUMBER, p_new_qty IN NUMBER) AS
v_status VARCHAR2(20);
BEGIN
SELECT order_status INTO v_status
FROM orders
WHERE order_id = p_order_id;
IF v_status = 'CLOSED' THEN
RAISE_APPLICATION_ERROR(-20020, '마감된 주문(' || p_order_id || ')은 수정할 수 없습니다. 현재 상태: ' || v_status);
ELSIF v_status = 'CANCELLED' THEN
RAISE_APPLICATION_ERROR(-20021, '취소된 주문은 수정 불가합니다.');
END IF;
UPDATE orders SET quantity = p_new_qty WHERE order_id = p_order_id;
COMMIT;
DBMS_OUTPUT.PUT_LINE('주문 수정 완료: ' || p_order_id);
END;
/
-- 에러 발생 및 해결 예시
DECLARE
v_status VARCHAR2(20);
v_order_id NUMBER := 5001;
BEGIN
-- 먼저 상태 확인
SELECT order_status INTO v_status
FROM orders
WHERE order_id = v_order_id;
DBMS_OUTPUT.PUT_LINE('현재 주문 상태: ' || v_status);
IF v_status IN ('OPEN', 'PENDING') THEN
modify_order(v_order_id, 50);
ELSE
DBMS_OUTPUT.PUT_LINE('주문 상태(' || v_status || ')로 인해 수정 불가. 관리자에게 문의하세요.');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('에러 발생: ' || SQLERRM);
ROLLBACK;
END;
/
에러 코드별 원인 추적 쿼리
실무에서 어떤 에러 번호대가 자주 발생하는지 추적하려면 아래 쿼리를 활용할 수 있습니다.
-- Alert Log 대신 애플리케이션 에러 로그 테이블 활용 예시
CREATE TABLE app_error_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY,
error_code NUMBER,
error_msg VARCHAR2(4000),
module_name VARCHAR2(200),
created_at TIMESTAMP DEFAULT SYSTIMESTAMP,
created_by VARCHAR2(100) DEFAULT USER
);
-- 에러 로깅 프로시저
CREATE OR REPLACE PROCEDURE log_app_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_module IN VARCHAR2
) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO app_error_log (error_code, error_msg, module_name)
VALUES (p_error_code, p_error_msg, p_module);
COMMIT;
END;
/
-- 에러 발생 현황 조회
SELECT error_code,
COUNT(*) AS occurrence_count,
MIN(created_at) AS first_occurred,
MAX(created_at) AS last_occurred,
SUBSTR(MAX(error_msg), 1, 100) AS sample_message
FROM app_error_log
WHERE created_at >= SYSDATE - 7
GROUP BY error_code
ORDER BY occurrence_count DESC;
예방 방법
1. 표준화된 에러 코드 체계 및 중앙 집중식 에러 핸들링 구축
팀 또는 프로젝트 전체에서 사용할 ORA-20000 ~ ORA-20999 범위의 에러 코드 레지스트리를 문서화하여 관리하고, 중복 코드 사용을 방지합니다. 아래와 같이 에러 코드 상수를 패키지로 관리하면 유지보수성이 크게 향상됩니다. 모든 PL/SQL 예외 처리는 WHEN OTHERS 절에서 에러 정보를 반드시 로깅하도록 표준을 수립하세요.
-- 에러 코드 중앙 관리 패키지 예시
CREATE OR REPLACE PACKAGE pkg_error_codes AS
-- 재고 관련 에러 (20001 ~ 20010)
C_ERR_STOCK_INSUFFICIENT CONSTANT NUMBER := -20001;
C_ERR_STOCK_NOT_FOUND CONSTANT NUMBER := -20002;
-- 주문 관련 에러 (20011 ~ 20020)
C_ERR_ORDER_CLOSED CONSTANT NUMBER := -20011;
C_ERR_ORDER_CANCELLED CONSTANT NUMBER := -20012;
-- 사용자 관련 에러 (20021 ~ 20030)
C_ERR_USER_INACTIVE CONSTANT NUMBER := -20021;
C_ERR_USER_NO_PERMISSION CONSTANT NUMBER := -20022;
-- 공통 에러 핸들링 프로시저
PROCEDURE handle_error(p_module IN VARCHAR2);
END pkg_error_codes;
/
CREATE OR REPLACE PACKAGE BODY pkg_error_codes AS
PROCEDURE handle_error(p_module IN VARCHAR2) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO app_error_log (error_code, error_msg, module_name)
VALUES (SQLCODE, SQLERRM, p_module);
COMMIT;
END;
END pkg_error_codes;
/
2. 애플리케이션 레이어와 DB 레이어의 역할 분리 및 입력 검증 이중화
데이터베이스 트리거나 프로시저에만 유효성 검사를 의존하지 말고, 애플리케이션 레이어에서도 동일한 비즈니스 규칙을 사전 검증하여 에러 발생 빈도를 줄입니다. DB 레이어의 RAISE_APPLICATION_ERROR는 최후의 안전망으로 활용하고, 에러 메시지는 사용자가 이해할 수 있는 명확한 언어로 작성합니다. 에러 메시지에는 반드시 에러 코드, 발생 모듈, 입력 값, 기대 값 등을 포함하여 디버깅에 소요되는 시간을 최소화하세요.
관련 에러
- ORA-06512:
RAISE_APPLICATION_ERROR가 호출된 정확한 라인 번호와 오브젝트 이름을 함께 표시하며, ORA-20000과 항상 함께 나타납니다. 스택 트레이스를 역으로 추적하면 에러 발생 지점을 빠르게 찾을 수 있습니다. - ORA-06500 ~ ORA-06599: PL/SQL 내부 에러로,
RAISE_APPLICATION_ERROR호출 자체에 문제가 있을 때 나타날 수 있습니다. - ORA-01403 (NO_DATA_FOUND): 프로시저 내에서 SELECT INTO로 데이터를 조회할 때 데이터가 없는 경우 발생하며, 이를 핸들링하지 않으면 ORA-20000으로 감싸서 재던지는 패턴이 자주 쓰입니다.
- ORA-00001 (UNIQUE CONSTRAINT VIOLATED): 유니크 제약 위반을 트리거에서 포착하여 ORA-20000으로 변환해 사용자 친화적 메시지를 전달하는 데 활용됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.