Oracle ORA-04075 오류 원인과 해결 방법 완벽 가이드

ORA-04075
2026년 08월 24일 | DBMS Error 가이드

이 글에서 다루는 내용

ORA-04075 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

ORA-04075 invalid trigger specification 는?

ORA-04075 에러는 Oracle 데이터베이스에서 트리거(Trigger)를 생성하거나 수정할 때 트리거 정의 구문이 잘못되었을 경우 발생하는 오류입니다. 트리거의 타입, 이벤트, 대상 객체 등의 조합이 Oracle이 허용하지 않는 형태로 작성되었을 때 주로 발생합니다. 예를 들어 INSTEAD OF 트리거를 일반 테이블에 적용하거나, BEFORE/AFTER 트리거를 뷰에 적용하는 등의 잘못된 조합이 대표적인 원인입니다.


주요 발생 원인

  • INSTEAD OF 트리거를 테이블에 적용하는 경우

INSTEAD OF 트리거는 오직 뷰(VIEW)에만 적용 가능한 트리거 타입입니다. 일반 테이블에 INSTEAD OF 트리거를 생성하려고 하면 Oracle은 이를 유효하지 않은 트리거 정의로 간주하여 ORA-04075 에러를 발생시킵니다. 이는 실무에서 뷰와 테이블을 혼동하거나, 트리거 타입의 적용 범위를 정확히 숙지하지 못한 개발자들이 자주 범하는 실수입니다.

  • DML 이벤트와 DDL 이벤트를 혼용하거나 잘못된 이벤트 타입 지정

트리거에서 사용 가능한 이벤트 타입은 DML(INSERT, UPDATE, DELETE), DDL(CREATE, ALTER, DROP), 데이터베이스 이벤트(LOGON, LOGOFF, STARTUP 등)로 구분됩니다. 이들 이벤트 타입을 잘못된 트리거 타입과 조합하거나, 지원되지 않는 이벤트를 지정하면 ORA-04075가 발생합니다. 예를 들어 INSTEAD OF 트리거에 DDL 이벤트를 지정하거나, ROW 레벨 트리거에 DDL 이벤트를 적용하는 경우가 이에 해당합니다.

  • FOR EACH ROW 절의 부적절한 사용

FOR EACH ROW는 행 레벨(Row-Level) 트리거를 정의할 때 사용하는 구문으로, 문장 레벨(Statement-Level) 트리거에는 사용할 수 없는 특정 조합이 존재합니다. DDL 트리거나 데이터베이스 이벤트 트리거에 FOR EACH ROW 절을 포함하면 ORA-04075 에러가 발생합니다. 실무에서는 대규모 배치 작업 최적화를 위해 트리거 레벨을 변경하다가 이 오류를 만나는 경우가 많습니다.


해결 방법

원인 1 해결: INSTEAD OF 트리거는 뷰에만 적용

INSTEAD OF 트리거를 테이블에 적용하려 했다면, 반드시 뷰를 대상으로 변경하거나 트리거 타입을 BEFORE/AFTER로 바꿔야 합니다.

잘못된 예 (에러 발생):

-- 일반 테이블에 INSTEAD OF 트리거 생성 시도 → ORA-04075 발생
CREATE OR REPLACE TRIGGER trg_emp_instead
INSTEAD OF INSERT ON employees  -- employees는 테이블
FOR EACH ROW
BEGIN
    NULL;
END;
/

올바른 예 1 – 뷰에 INSTEAD OF 트리거 적용:

-- 먼저 뷰를 생성
CREATE OR REPLACE VIEW emp_dept_view AS
    SELECT e.employee_id, e.first_name, e.last_name, d.department_name
    FROM employees e
    JOIN departments d ON e.department_id = d.department_id;

-- 뷰에 INSTEAD OF 트리거 적용 (정상)
CREATE OR REPLACE TRIGGER trg_emp_dept_view_insert
INSTEAD OF INSERT ON emp_dept_view
FOR EACH ROW
BEGIN
    INSERT INTO employees (employee_id, first_name, last_name)
    VALUES (:NEW.employee_id, :NEW.first_name, :NEW.last_name);
END;
/

올바른 예 2 – 테이블에는 BEFORE/AFTER 트리거 사용:

-- 테이블에는 BEFORE 또는 AFTER 트리거 사용
CREATE OR REPLACE TRIGGER trg_emp_before_insert
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
    IF :NEW.salary IS NULL THEN
        :NEW.salary := 3000;
    END IF;
END;
/

원인 2 해결: 올바른 이벤트 타입 조합 사용

트리거 타입과 이벤트 타입의 허용 조합을 정확히 이해하고 사용해야 합니다.

잘못된 예 (에러 발생):

-- INSTEAD OF 트리거에 DDL 이벤트 지정 → ORA-04075 발생
CREATE OR REPLACE TRIGGER trg_ddl_instead
INSTEAD OF CREATE ON SCHEMA
BEGIN
    NULL;
END;
/

올바른 예 – DDL 이벤트에는 BEFORE/AFTER 트리거 사용:

-- DDL 트리거는 BEFORE 또는 AFTER로 생성해야 함
CREATE OR REPLACE TRIGGER trg_ddl_audit
BEFORE CREATE ON SCHEMA
BEGIN
    INSERT INTO ddl_audit_log (event_type, object_name, event_date)
    VALUES (ora_sysevent, ora_dict_obj_name, SYSDATE);
END;
/

올바른 예 – 데이터베이스 이벤트 트리거:

-- 로그온 이벤트 트리거 (올바른 형식)
CREATE OR REPLACE TRIGGER trg_logon_audit
AFTER LOGON ON DATABASE
BEGIN
    INSERT INTO logon_audit_log (username, logon_time, ip_address)
    VALUES (SYS_CONTEXT('USERENV', 'SESSION_USER'),
            SYSDATE,
            SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
    COMMIT;
END;
/

원인 3 해결: FOR EACH ROW 절의 적절한 사용

DDL 트리거와 데이터베이스 이벤트 트리거에서는 FOR EACH ROW 절을 제거해야 합니다.

잘못된 예 (에러 발생):

-- DDL 트리거에 FOR EACH ROW 사용 → ORA-04075 발생
CREATE OR REPLACE TRIGGER trg_ddl_row_level
AFTER CREATE ON SCHEMA
FOR EACH ROW  -- DDL 트리거에는 FOR EACH ROW 사용 불가
BEGIN
    NULL;
END;
/

올바른 예 – FOR EACH ROW 제거:

-- DDL 트리거는 문장 레벨(Statement-Level)로만 정의
CREATE OR REPLACE TRIGGER trg_ddl_row_level
AFTER CREATE ON SCHEMA
BEGIN
    DBMS_OUTPUT.PUT_LINE('DDL 이벤트 감지: ' || ora_dict_obj_name);
    INSERT INTO ddl_log (obj_name, event_type, created_by, created_at)
    VALUES (ora_dict_obj_name, ora_sysevent, USER, SYSDATE);
    COMMIT;
END;
/

트리거 타입별 FOR EACH ROW 사용 가능 여부 확인 쿼리:

-- 현재 스키마의 트리거 정보 확인
SELECT trigger_name,
       trigger_type,
       triggering_event,
       table_name,
       action_type,
       status
FROM user_triggers
ORDER BY trigger_name;

-- 트리거 소스 확인
SELECT text
FROM user_source
WHERE name = 'TRG_EMP_BEFORE_INSERT'
  AND type = 'TRIGGER'
ORDER BY line;

예방 방법

  • 트리거 생성 전 타입 호환성 체크리스트 활용

트리거를 생성하기 전에 반드시 트리거 타입(BEFORE/AFTER/INSTEAD OF), 이벤트(DML/DDL/DB Event), 대상 객체(TABLE/VIEW/SCHEMA/DATABASE), 레벨(ROW/STATEMENT)의 허용 조합을 팀 내 표준 체크리스트로 정리하여 활용하십시오. 아래 조합표를 개발 가이드로 공유하면 신규 개발자의 실수를 효과적으로 예방할 수 있습니다.

| 트리거 타입 | 대상 | FOR EACH ROW | 허용 여부 |

|————|——|————-|———|

| BEFORE/AFTER | TABLE | 가능/불가 | ✅ |

| INSTEAD OF | VIEW | 필수 | ✅ |

| INSTEAD OF | TABLE | – | ❌ |

| BEFORE/AFTER DDL | SCHEMA/DB | 불가 | ✅ |

| INSTEAD OF | DDL 이벤트 | – | ❌ |

  • 개발 환경에서 트리거 생성 후 즉시 컴파일 상태 검증

트리거 생성 후 반드시 USER_TRIGGERS 뷰와 USER_ERRORS 뷰를 통해 컴파일 상태와 오류를 확인하는 루틴을 CI/CD 파이프라인 또는 배포 스크립트에 포함시키십시오. 아래 검증 스크립트를 표준 배포 절차에 포함하면 운영 환경 배포 전 오류를 사전에 차단할 수 있습니다.

“`sql

— 트리거 생성 후 상태 검증 스크립트

SELECT trigger_name, status

FROM user_triggers

WHERE trigger_name = ‘TRG_EMP_BEFORE_INSERT’;

— 컴파일 오류 확인

SELECT name, type, line, position, text

FROM user_errors

WHERE type = ‘TRIGGER’

ORDER BY name, line;

— INVALID 상태 트리거 전체 조회

SELECT trigger_name, table_name, status

FROM user_triggers

WHERE status = ‘DISABLED’

OR status = ‘INVALID’;

“`


관련 에러

  • ORA-04076: invalid NEW or OLD specification — 트리거 내에서 :NEW 또는 :OLD 의사 레코드(Pseudo Record)를 잘못 참조할 때 발생합니다. 문장 레벨 트리거에서 :NEW/:OLD를 사용하거나, DDL 트리거에서 참조할 때 주로 나타납니다.
  • ORA-04077: WHEN clause cannot be used with table level triggers — WHEN 절은 행 레벨(FOR EACH ROW) 트리거에서만 사용 가능하며, 문장 레벨 트리거에 WHEN 절을 추가하면 발생합니다.
  • ORA-04079: invalid trigger specification — 트리거 정의 내의 구체적인 구문 오류로, ORA-04075와 함께 자주 발생하는 유사한 에러입니다.
  • ORA-00904: invalid identifier — 트리거 본문에서 존재하지 않는 컬럼명이나 변수명을 참조할 때 발생하며, 트리거 작성 시 자주 동반되는 에러입니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기