오라클 트리거(Trigger) 관리 완벽 가이드 - 실무에서 바로 쓰는 핵심 쿼리
오라클 트리거(Trigger)는 테이블에 특정 이벤트가 발생할 때 자동으로 실행되는 PL/SQL 블록입니다. 데이터 무결성 유지, 감사 로그 기록, 자동 계산 등에 활용되며, 실무에서는 트리거의 상태 관리와 모니터링이 매우 중요합니다. 이 가이드에서는 트리거 관리에 필요한 모든 쿼리를 목적별로 정리했습니다.
목차
- 트리거 기본 정보 조회
- 트리거 상세 정보 조회
- 트리거 소스 코드 조회
- 테이블별 트리거 목록 조회
- 비활성화된 트리거 조회
- INVALID 트리거 조회 및 오류 확인
- 트리거 활성화/비활성화
- 트리거 실행 순서 확인
1. 트리거 기본 정보 조회
트리거의 존재 여부와 기본 상태를 확인합니다.
SELECT
TRIGGER_NAME,
TABLE_NAME,
TRIGGER_TYPE,
TRIGGERING_EVENT,
STATUS,
OWNER
FROM ALL_TRIGGERS
WHERE TRIGGER_NAME = UPPER('트리거명');
주요 컬럼 설명:
TRIGGER_TYPE: BEFORE/AFTER, STATEMENT/ROW 레벨TRIGGERING_EVENT: INSERT, UPDATE, DELETE 등STATUS: ENABLED(활성) 또는 DISABLED(비활성)
본인 스키마만 조회:
-- USER_TRIGGERS 사용 (더 빠름)
SELECT
TRIGGER_NAME,
TABLE_NAME,
TRIGGER_TYPE,
TRIGGERING_EVENT,
STATUS
FROM USER_TRIGGERS
WHERE TRIGGER_NAME = UPPER('트리거명');
2. 트리거 상세 정보 조회
트리거의 모든 속성을 한눈에 확인합니다.
SELECT
TRIGGER_NAME AS "트리거명",
TABLE_NAME AS "테이블명",
TRIGGER_TYPE AS "타입",
TRIGGERING_EVENT AS "이벤트",
STATUS AS "상태",
WHEN_CLAUSE AS "조건",
DESCRIPTION AS "설명",
ACTION_TYPE AS "액션타입",
TRIGGER_BODY AS "본문"
FROM USER_TRIGGERS
WHERE TRIGGER_NAME = UPPER('트리거명');
TRIGGER_TYPE 종류:
BEFORE STATEMENT: 문장 실행 전 (1회)BEFORE EACH ROW: 각 행 처리 전AFTER STATEMENT: 문장 실행 후 (1회)AFTER EACH ROW: 각 행 처리 후
TRIGGERING_EVENT 조합 예시:
INSERTUPDATEDELETEINSERT OR UPDATEINSERT OR UPDATE OR DELETE
3. 트리거 소스 코드 조회
트리거의 전체 로직을 확인하고 분석합니다.
-- 방법 1: TRIGGER_BODY 컬럼 사용
SELECT TRIGGER_BODY
FROM USER_TRIGGERS
WHERE TRIGGER_NAME = UPPER('트리거명');
-- 방법 2: ALL_SOURCE 뷰 사용 (라인별 조회)
SELECT
LINE,
TEXT
FROM ALL_SOURCE
WHERE NAME = UPPER('트리거명')
AND TYPE = 'TRIGGER'
ORDER BY LINE;
팁: 복잡한 트리거는 ALL_SOURCE를 사용하면 라인별로 분석하기 편합니다.
4. 테이블별 트리거 목록 조회
특정 테이블에 걸려있는 모든 트리거를 확인합니다.
SELECT
TRIGGER_NAME AS "트리거명",
TRIGGER_TYPE AS "타입",
TRIGGERING_EVENT AS "이벤트",
STATUS AS "상태",
DESCRIPTION AS "설명"
FROM USER_TRIGGERS
WHERE TABLE_NAME = UPPER('테이블명')
ORDER BY TRIGGER_NAME;
실무 활용:
- 테이블 구조 변경 전 영향도 분석
- 데이터 입력/수정 시 자동 실행되는 로직 파악
- 성능 이슈 발생 시 트리거 확인
5. 비활성화된 트리거 조회
현재 비활성 상태인 트리거를 찾습니다.
SELECT
TRIGGER_NAME AS "트리거명",
TABLE_NAME AS "테이블명",
TRIGGERING_EVENT AS "이벤트",
STATUS AS "상태"
FROM USER_TRIGGERS
WHERE STATUS = 'DISABLED'
ORDER BY TABLE_NAME, TRIGGER_NAME;
확인이 필요한 경우:
- 배포 후 의도치 않게 비활성화된 트리거 확인
- 성능 테스트를 위해 일시적으로 비활성화한 트리거 재확인
- 운영 중 장애 발생 시 트리거 상태 점검
6. INVALID 트리거 조회 및 오류 확인
컴파일 오류가 발생한 트리거를 찾고 원인을 파악합니다.
-- INVALID 트리거 목록
SELECT
OBJECT_NAME AS "트리거명",
STATUS AS "상태",
LAST_DDL_TIME AS "최종수정일"
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'TRIGGER'
AND STATUS = 'INVALID'
ORDER BY LAST_DDL_TIME DESC;
-- 컴파일 오류 상세 확인
SELECT
LINE,
POSITION,
TEXT AS "오류내용"
FROM USER_ERRORS
WHERE NAME = UPPER('트리거명')
AND TYPE = 'TRIGGER'
ORDER BY SEQUENCE;
주요 INVALID 원인:
- 참조하는 테이블 컬럼 변경/삭제
- 사용 중인 함수/프로시저 변경
- 권한 문제
- 문법 오류
7. 트리거 활성화/비활성화
트리거를 켜고 끄는 DDL 명령어입니다.
개별 트리거 제어
-- 트리거 비활성화
ALTER TRIGGER 트리거명 DISABLE;
-- 트리거 활성화
ALTER TRIGGER 트리거명 ENABLE;
테이블의 모든 트리거 제어
-- 테이블의 모든 트리거 비활성화
ALTER TABLE 테이블명 DISABLE ALL TRIGGERS;
-- 테이블의 모든 트리거 활성화
ALTER TABLE 테이블명 ENABLE ALL TRIGGERS;
스키마의 모든 트리거 제어 (스크립트)
-- 모든 트리거 비활성화 스크립트 생성
SELECT 'ALTER TRIGGER ' || TRIGGER_NAME || ' DISABLE;'
FROM USER_TRIGGERS
WHERE STATUS = 'ENABLED';
-- 모든 트리거 활성화 스크립트 생성
SELECT 'ALTER TRIGGER ' || TRIGGER_NAME || ' ENABLE;'
FROM USER_TRIGGERS
WHERE STATUS = 'DISABLED';
실무 활용 시나리오:
- 대량 데이터 입력 시 성능 향상을 위해 임시 비활성화
- 데이터 마이그레이션 작업 시
- 테스트 환경에서 특정 로직 제외
8. 트리거 실행 순서 확인
동일 이벤트에 여러 트리거가 있을 때 실행 순서를 확인합니다.
SELECT
TRIGGER_NAME,
TABLE_NAME,
TRIGGER_TYPE,
TRIGGERING_EVENT,
STATUS,
-- Oracle 11g 이상에서 순서 확인 가능
ACTION_ORDER
FROM USER_TRIGGERS
WHERE TABLE_NAME = UPPER('테이블명')
AND TRIGGERING_EVENT LIKE '%INSERT%'
ORDER BY ACTION_ORDER NULLS LAST, TRIGGER_NAME;
Oracle 11g 이상: FOLLOWS 절로 순서 제어
CREATE OR REPLACE TRIGGER trg_second
AFTER INSERT ON employees
FOLLOWS trg_first -- trg_first 다음에 실행
FOR EACH ROW
BEGIN
-- 트리거 로직
END;
실무 활용 시나리오별 쿼리 조합
| 상황 | 사용 쿼리 |
|---|---|
| 트리거 존재 여부 확인 | USER_TRIGGERS 기본 조회 |
| 테이블 변경 전 영향도 분석 | 테이블별 트리거 목록 |
| 성능 이슈 원인 파악 | 테이블별 트리거 + 소스 코드 |
| 배포 후 상태 점검 | INVALID 트리거 + 비활성화 트리거 |
| 대량 데이터 작업 전 | 트리거 비활성화 스크립트 |
트리거 종합 관리 쿼리
전체 트리거 현황 (대시보드용)
SELECT
ROWNUM AS NO,
TRIGGER_NAME AS "트리거명",
TABLE_NAME AS "테이블명",
TRIGGER_TYPE AS "타입",
TRIGGERING_EVENT AS "이벤트",
STATUS AS "상태",
CASE
WHEN STATUS = 'ENABLED' THEN '정상'
ELSE '비활성'
END AS "비고"
FROM USER_TRIGGERS
ORDER BY TABLE_NAME, TRIGGER_NAME;
테이블별 트리거 통계
SELECT
TABLE_NAME AS "테이블명",
COUNT(*) AS "트리거수",
SUM(CASE WHEN STATUS = 'ENABLED' THEN 1 ELSE 0 END) AS "활성",
SUM(CASE WHEN STATUS = 'DISABLED' THEN 1 ELSE 0 END) AS "비활성"
FROM USER_TRIGGERS
GROUP BY TABLE_NAME
ORDER BY COUNT(*) DESC;
이벤트별 트리거 통계
SELECT
TRIGGERING_EVENT AS "이벤트",
COUNT(*) AS "트리거수",
SUM(CASE WHEN STATUS = 'ENABLED' THEN 1 ELSE 0 END) AS "활성"
FROM USER_TRIGGERS
GROUP BY TRIGGERING_EVENT
ORDER BY COUNT(*) DESC;
트리거 성능 모니터링
트리거 실행 통계 (V$SQL 활용)
SELECT
s.sql_text,
s.executions AS "실행횟수",
ROUND(s.elapsed_time/1000000, 2) AS "총소요시간(초)",
ROUND(s.elapsed_time/s.executions/1000000, 4) AS "평균소요시간(초)"
FROM v$sql s
WHERE UPPER(s.sql_text) LIKE '%트리거명%'
ORDER BY s.executions DESC;
느린 트리거 찾기
-- 실행 시간이 긴 트리거 식별
SELECT
t.trigger_name,
t.table_name,
t.status,
LENGTH(t.trigger_body) AS "코드길이"
FROM user_triggers t
WHERE t.status = 'ENABLED'
ORDER BY LENGTH(t.trigger_body) DESC;
트리거 의존성 조회
트리거가 참조하는 객체들을 확인합니다.
SELECT
NAME AS "트리거명",
TYPE AS "타입",
REFERENCED_OWNER AS "참조스키마",
REFERENCED_NAME AS "참조객체",
REFERENCED_TYPE AS "참조타입"
FROM USER_DEPENDENCIES
WHERE NAME = UPPER('트리거명')
AND TYPE = 'TRIGGER'
ORDER BY REFERENCED_TYPE, REFERENCED_NAME;
활용:
- 테이블 구조 변경 시 영향받는 트리거 파악
- 프로시저/함수 변경 시 관련 트리거 확인
- 리팩토링 범위 결정
트리거 재컴파일
INVALID 상태의 트리거를 재컴파일합니다.
-- 개별 트리거 재컴파일
ALTER TRIGGER 트리거명 COMPILE;
-- 전체 INVALID 트리거 재컴파일 스크립트 생성
SELECT 'ALTER TRIGGER ' || OBJECT_NAME || ' COMPILE;'
FROM USER_OBJECTS
WHERE OBJECT_TYPE = 'TRIGGER'
AND STATUS = 'INVALID';
트리거 백업 및 복원
DDL 추출 (백업용)
-- DBMS_METADATA 패키지 사용
SELECT DBMS_METADATA.GET_DDL('TRIGGER', '트리거명') AS ddl
FROM DUAL;
-- 전체 트리거 DDL 추출
SELECT DBMS_METADATA.GET_DDL('TRIGGER', TRIGGER_NAME) AS ddl
FROM USER_TRIGGERS
WHERE STATUS = 'ENABLED';
팁: 결과를 파일로 저장하여 버전 관리 시스템에 보관하세요.
트리거 작성 Best Practices
1. 명명 규칙
-- 권장 명명 규칙
-- TRG_[테이블명]_[이벤트]_[타이밍]
-- 예: TRG_EMPLOYEES_INSERT_BEFORE
-- 예: TRG_ORDERS_UPDATE_AFTER
2. 성능을 고려한 트리거 작성
CREATE OR REPLACE TRIGGER trg_example
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
-- ❌ 나쁜 예: 트리거 내에서 SELECT 사용
-- SELECT COUNT(*) INTO v_count FROM departments;
-- ✅ 좋은 예: 필요한 경우만 최소한의 쿼리
IF :NEW.department_id IS NOT NULL THEN
:NEW.updated_date := SYSDATE;
END IF;
END;
3. 순환 참조 방지
-- ❌ 위험: A 테이블 트리거가 B 테이블 수정 → B 테이블 트리거가 A 테이블 수정
-- 순환 참조로 인한 무한 루프 발생 가능
-- ✅ 안전: 플래그 변수로 제어
CREATE OR REPLACE TRIGGER trg_safe
BEFORE UPDATE ON table_a
FOR EACH ROW
DECLARE
v_trigger_active BOOLEAN := FALSE;
BEGIN
IF NOT v_trigger_active THEN
v_trigger_active := TRUE;
-- 로직 수행
v_trigger_active := FALSE;
END IF;
END;
트리거 디버깅
트리거 로그 테이블 생성
CREATE TABLE trigger_log (
log_id NUMBER PRIMARY KEY,
trigger_name VARCHAR2(100),
table_name VARCHAR2(100),
event_type VARCHAR2(50),
old_value VARCHAR2(4000),
new_value VARCHAR2(4000),
log_date DATE DEFAULT SYSDATE,
username VARCHAR2(50) DEFAULT USER
);
CREATE SEQUENCE seq_trigger_log START WITH 1;
트리거 내 로깅 예시
CREATE OR REPLACE TRIGGER trg_employee_audit
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
INSERT INTO trigger_log (
log_id, trigger_name, table_name, event_type,
old_value, new_value
) VALUES (
seq_trigger_log.NEXTVAL,
'TRG_EMPLOYEE_AUDIT',
'EMPLOYEES',
'UPDATE',
:OLD.salary,
:NEW.salary
);
END;
엑셀 추출용 쿼리
문서화나 보고서 작성을 위한 쿼리입니다.
SELECT
ROWNUM AS "NO",
TRIGGER_NAME AS "트리거명",
TABLE_NAME AS "테이블명",
CASE
WHEN TRIGGER_TYPE LIKE 'BEFORE%' THEN 'BEFORE'
ELSE 'AFTER'
END AS "실행시점",
CASE
WHEN TRIGGER_TYPE LIKE '%EACH ROW%' THEN 'ROW'
ELSE 'STATEMENT'
END AS "레벨",
TRIGGERING_EVENT AS "이벤트",
STATUS AS "상태",
SUBSTR(DESCRIPTION, 1, 100) AS "설명"
FROM USER_TRIGGERS
ORDER BY TABLE_NAME, TRIGGER_NAME;
문제 해결 가이드
Q1. 트리거가 실행되지 않아요
체크리스트:
- 트리거 상태 확인 (ENABLED/DISABLED)
- 트리거 조건(WHEN 절) 확인
- 트리거 이벤트 타입 확인 (INSERT/UPDATE/DELETE)
- 권한 문제 확인
-- 상태 및 조건 확인
SELECT
trigger_name,
status,
when_clause,
triggering_event
FROM user_triggers
WHERE trigger_name = 'YOUR_TRIGGER';
Q2. 트리거로 인해 성능이 느려요
원인 분석:
-- 테이블의 트리거 개수 확인
SELECT table_name, COUNT(*) AS trigger_count
FROM user_triggers
WHERE status = 'ENABLED'
GROUP BY table_name
HAVING COUNT(*) > 3
ORDER BY COUNT(*) DESC;
해결방안:
- 불필요한 트리거 제거
- ROW 레벨 → STATEMENT 레벨 변경 검토
- 트리거 내 복잡한 쿼리 최적화
- 트리거 로직을 애플리케이션으로 이전 검토
Q3. 트리거 순환 참조 오류
오류 메시지:
ORA-04091: table X is mutating, trigger/function may not see it
해결방법:
- AUTONOMOUS_TRANSACTION 사용
- Compound Trigger 사용 (11g 이상)
- 패키지 변수를 활용한 제어
마치며
오라클 트리거는 강력한 기능이지만, 잘못 사용하면 성능 저하와 유지보수의 어려움을 초래할 수 있습니다. 이 가이드의 쿼리들을 활용하면:
- ✅ 트리거 상태를 체계적으로 관리할 수 있습니다
- ✅ 성능 문제를 빠르게 진단할 수 있습니다
- ✅ 배포 전후 트리거 상태를 정확히 점검할 수 있습니다
- ✅ 트리거 의존성을 명확히 파악할 수 있습니다
트리거 사용 권장사항:
- 꼭 필요한 경우에만 사용
- 트리거 내 복잡한 로직은 프로시저로 분리
- 정기적인 성능 모니터링
- 명확한 명명 규칙과 문서화