2026년 09월 30일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-20004 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-20004 user defined error -20004 는?
ORA-20004는 Oracle에서 개발자 또는 DBA가 RAISE_APPLICATION_ERROR 프로시저를 사용하여 의도적으로 발생시키는 사용자 정의 예외(User Defined Exception) 에러입니다. Oracle은 -20000부터 -20999까지의 에러 번호 범위를 사용자 정의 에러 전용으로 예약해 두었으며, ORA-20004는 그 중 하나입니다. 이 에러는 비즈니스 로직 검증 실패, 데이터 무결성 위반, 또는 특정 조건 충족 실패 시 PL/SQL 코드 내에서 명시적으로 발생시키는 경우가 대부분입니다.
주요 발생 원인
1. 비즈니스 로직 검증 실패로 인한 명시적 예외 발생
가장 일반적인 원인으로, 개발자가 특정 비즈니스 규칙을 위반했을 때 RAISE_APPLICATION_ERROR(-20004, '메시지')를 호출하도록 PL/SQL 코드를 작성한 경우입니다. 예를 들어, 재고 수량이 0 이하일 때 주문을 처리하려 하거나, 특정 권한이 없는 사용자가 민감한 데이터에 접근하려는 경우 트리거나 프로시저에서 이 에러를 발생시킵니다. 이런 경우 에러 메시지를 자세히 확인하여 어떤 비즈니스 규칙을 위반했는지 파악하는 것이 우선입니다.
2. 데이터베이스 트리거(Trigger)에 의한 자동 발생
테이블에 설정된 DML 트리거(INSERT, UPDATE, DELETE) 내부에서 데이터 무결성이나 참조 규칙을 검사하다가 조건이 맞지 않을 때 발생합니다. 운영 환경에서 대량 배치 작업이나 데이터 마이그레이션 수행 중 트리거 조건에 걸려 에러가 발생하는 경우가 많습니다. 트리거의 존재를 인지하지 못한 채 DML 작업을 수행하면 원인 파악에 오랜 시간이 걸릴 수 있습니다.
3. 패키지 또는 저장 프로시저 내 데이터 유효성 검사 실패
복잡한 비즈니스 로직을 담고 있는 패키지나 저장 프로시저 내에서 입력 파라미터의 유효성 검증이 실패할 때 발생합니다. 예를 들어, 날짜 범위가 잘못되었거나, 필수 파라미터가 NULL로 전달되었거나, 입력값이 허용 범위를 벗어난 경우 해당 에러 번호로 예외를 발생시키도록 설계된 경우입니다. 이 경우 프로시저의 소스 코드를 직접 확인하거나 개발팀에 문의하여 정확한 발생 조건을 파악해야 합니다.
해결 방법
원인 1: 비즈니스 로직 검증 실패
우선 에러 메시지를 정확히 확인하고, 소스 코드에서 해당 에러 번호(-20004)가 발생하는 위치를 찾아야 합니다.
-- 소스 코드에서 ORA-20004 발생 위치 검색
SELECT owner, name, type, line, text
FROM dba_source
WHERE UPPER(text) LIKE '%20004%'
AND type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE BODY', 'TRIGGER')
ORDER BY owner, name, line;
-- RAISE_APPLICATION_ERROR 사용 예시 (원인 파악용)
CREATE OR REPLACE PROCEDURE check_stock(p_product_id IN NUMBER, p_qty IN NUMBER) AS
v_stock NUMBER;
BEGIN
SELECT stock_qty INTO v_stock
FROM products
WHERE product_id = p_product_id;
IF v_stock < p_qty THEN
RAISE_APPLICATION_ERROR(-20004, '재고 부족: 요청 수량(' || p_qty || ')이 현재 재고(' || v_stock || ')를 초과합니다.');
END IF;
-- 정상 처리 로직
DBMS_OUTPUT.PUT_LINE('주문 처리 완료');
EXCEPTION
WHEN OTHERS THEN
RAISE;
END;
/
비즈니스 규칙을 정확히 이해한 후, 호출 전에 조건을 사전 검증하거나 입력 데이터를 수정하여 에러를 회피합니다.
원인 2: 트리거에 의한 발생
트리거 목록을 먼저 확인하고, 해당 트리거 내 조건과 소스를 분석합니다.
-- 특정 테이블에 설정된 트리거 확인
SELECT trigger_name, trigger_type, triggering_event, status
FROM dba_triggers
WHERE table_name = 'YOUR_TABLE_NAME' -- 대상 테이블명으로 변경
AND owner = 'YOUR_SCHEMA'; -- 스키마명으로 변경
-- 트리거 소스 코드 확인
SELECT text
FROM dba_source
WHERE name = 'YOUR_TRIGGER_NAME' -- 트리거명으로 변경
AND type = 'TRIGGER'
ORDER BY line;
-- 문제가 있는 트리거를 임시 비활성화 (테스트 환경에서만 사용)
ALTER TRIGGER your_trigger_name DISABLE;
-- 작업 완료 후 재활성화
ALTER TRIGGER your_trigger_name ENABLE;
-- 트리거 내 올바른 예외 처리 예시
CREATE OR REPLACE TRIGGER trg_check_salary
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
IF :NEW.salary < 0 THEN
RAISE_APPLICATION_ERROR(-20004, '급여는 0 이상이어야 합니다. 입력값: ' || :NEW.salary);
END IF;
IF :NEW.salary > 100000000 THEN
RAISE_APPLICATION_ERROR(-20004, '급여 입력값이 허용 최대치를 초과했습니다.');
END IF;
END;
/
트리거를 비활성화하는 것은 임시방편이며, 근본적인 해결을 위해서는 데이터를 트리거 조건에 맞게 수정하거나 트리거 로직 자체를 개선해야 합니다.
원인 3: 패키지/프로시저 내 유효성 검사 실패
프로시저 호출 전에 입력값을 검증하고, 예외를 적절히 처리합니다.
-- 안전한 프로시저 호출 예시 (예외 처리 포함)
DECLARE
v_error_code NUMBER;
v_error_msg VARCHAR2(4000);
BEGIN
-- 프로시저 호출 전 사전 검증
your_package.your_procedure(
p_param1 => 'valid_value',
p_param2 => SYSDATE
);
DBMS_OUTPUT.PUT_LINE('처리 완료');
EXCEPTION
WHEN OTHERS THEN
v_error_code := SQLCODE;
v_error_msg := SQLERRM;
IF v_error_code = -20004 THEN
DBMS_OUTPUT.PUT_LINE('사용자 정의 에러 발생: ' || v_error_msg);
-- 에러 로그 테이블에 기록
INSERT INTO error_log (error_code, error_message, log_time)
VALUES (v_error_code, v_error_msg, SYSTIMESTAMP);
COMMIT;
ELSE
RAISE; -- 다른 에러는 상위로 전파
END IF;
END;
/
-- 에러 발생 위치 추적을 위한 DBMS_UTILITY 활용
CREATE OR REPLACE PROCEDURE safe_wrapper AS
BEGIN
your_risky_procedure();
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('에러 위치: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
DBMS_OUTPUT.PUT_LINE('에러 내용: ' || DBMS_UTILITY.FORMAT_ERROR_STACK);
RAISE;
END;
/
예방 방법
1. 사용자 정의 에러 번호 체계적 관리 및 문서화
조직 내에서 -20000 ~ -20999 범위의 에러 번호를 체계적으로 관리하는 에러 코드 레지스트리를 만들어야 합니다. 각 에러 번호가 어떤 비즈니스 규칙을 나타내는지, 어느 프로시저나 트리거에서 발생하는지를 명세 문서로 관리하면 장애 대응 시간을 크게 단축할 수 있습니다. 또한 에러 메시지에는 발생 원인과 조치 방법을 명확히 담아 운영팀이 즉시 대응할 수 있도록 설계하는 것이 좋습니다.
-- 사용자 정의 에러 코드 관리 테이블 예시
CREATE TABLE user_error_registry (
error_code NUMBER PRIMARY KEY,
error_name VARCHAR2(100) NOT NULL,
description VARCHAR2(500),
source_object VARCHAR2(200),
action_guide VARCHAR2(1000),
created_date DATE DEFAULT SYSDATE,
created_by VARCHAR2(100)
);
INSERT INTO user_error_registry VALUES (
-20004,
'BUSINESS_RULE_VIOLATION',
'비즈니스 규칙 위반 시 발생하는 사용자 정의 에러',
'PACKAGE: ORDER_MGMT / TRIGGER: TRG_ORDER_VALIDATION',
'주문 데이터 유효성 확인 후 재시도. 지속 발생 시 개발팀 문의.',
SYSDATE,
'DBA_TEAM'
);
COMMIT;
2. 중앙화된 예외 처리 패키지 구현
애플리케이션 전체에서 일관성 있는 예외 처리를 위해 중앙 예외 처리 패키지를 설계하고 모든 개발자가 이를 활용하도록 표준화해야 합니다. 에러 발생 시 자동으로 로그를 기록하고, 알림을 발송하는 체계를 구축하면 운영 중 발생하는 ORA-20004 에러를 사전에 감지하고 빠르게 조치할 수 있습니다.
-- 중앙화된 예외 처리 패키지 예시
CREATE OR REPLACE PACKAGE pkg_error_handler AS
PROCEDURE log_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_module_name IN VARCHAR2,
p_additional_info IN VARCHAR2 DEFAULT NULL
);
PROCEDURE raise_custom_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_module_name IN VARCHAR2
);
END pkg_error_handler;
/
CREATE OR REPLACE PACKAGE BODY pkg_error_handler AS
PROCEDURE log_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_module_name IN VARCHAR2,
p_additional_info IN VARCHAR2 DEFAULT NULL
) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO error_log (
error_code, error_message, module_name,
additional_info, log_time, session_user
) VALUES (
p_error_code, p_error_msg, p_module_name,
p_additional_info, SYSTIMESTAMP, SYS_CONTEXT('USERENV', 'SESSION_USER')
);
COMMIT;
END log_error;
PROCEDURE raise_custom_error(
p_error_code IN NUMBER,
p_error_msg IN VARCHAR2,
p_module_name IN VARCHAR2
) AS
BEGIN
log_error(p_error_code, p_error_msg, p_module_name);
RAISE_APPLICATION_ERROR(p_error_code, p_error_msg);
END raise_custom_error;
END pkg_error_handler;
/
관련 에러
- ORA-20000 ~ ORA-20999: Oracle이 사용자 정의 에러 전용으로 예약한 전체 에러 번호 범위입니다. ORA-20004와 동일한 메커니즘으로 동작하며, 각 번호는 개발팀의 정의에 따라 다른 의미를 가집니다.
- ORA-06512:
RAISE_APPLICATION_ERROR또는RAISE가 호출된 PL/SQL 소스 코드의 정확한 줄 번호와 스택 정보를 나타냅니다. ORA-20004와 함께 자주 나타나며 에러 추적에 필수적입니다. - ORA-06502: 값의 범위나 타입이 맞지 않을 때 발생하며, 사용자 정의 에러 내부 로직에서 함께 발생하는 경우가 있습니다.
- ORA-01403 (NO_DATA_FOUND): 프로시저 내 SELECT INTO 구문에서 데이터가 없을 때 발생하며, 이를 처리하지 않으면 ORA-20004와 유사한 비즈니스 로직 에러로 재가공되는 경우가 많습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.