카테고리 없음

오라클 트리거(Trigger) 관리 완벽 가이드 - 실무에서 바로 쓰는 핵심 쿼리

shaprimanDev 2025. 12. 22. 22:27
반응형

오라클 트리거(Trigger)는 테이블에 특정 이벤트가 발생할 때 자동으로 실행되는 PL/SQL 블록입니다. 데이터 무결성 유지, 감사 로그 기록, 자동 계산 등에 활용되며, 실무에서는 트리거의 상태 관리와 모니터링이 매우 중요합니다. 이 가이드에서는 트리거 관리에 필요한 모든 쿼리를 목적별로 정리했습니다.

목차

  1. 트리거 기본 정보 조회
  2. 트리거 상세 정보 조회
  3. 트리거 소스 코드 조회
  4. 테이블별 트리거 목록 조회
  5. 비활성화된 트리거 조회
  6. INVALID 트리거 조회 및 오류 확인
  7. 트리거 활성화/비활성화
  8. 트리거 실행 순서 확인

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 조합 예시:

  • INSERT
  • UPDATE
  • DELETE
  • INSERT OR UPDATE
  • INSERT 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. 트리거가 실행되지 않아요

체크리스트:

  1. 트리거 상태 확인 (ENABLED/DISABLED)
  2. 트리거 조건(WHEN 절) 확인
  3. 트리거 이벤트 타입 확인 (INSERT/UPDATE/DELETE)
  4. 권한 문제 확인
-- 상태 및 조건 확인
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

해결방법:

  1. AUTONOMOUS_TRANSACTION 사용
  2. Compound Trigger 사용 (11g 이상)
  3. 패키지 변수를 활용한 제어

마치며

오라클 트리거는 강력한 기능이지만, 잘못 사용하면 성능 저하와 유지보수의 어려움을 초래할 수 있습니다. 이 가이드의 쿼리들을 활용하면:

  • ✅ 트리거 상태를 체계적으로 관리할 수 있습니다
  • ✅ 성능 문제를 빠르게 진단할 수 있습니다
  • ✅ 배포 전후 트리거 상태를 정확히 점검할 수 있습니다
  • ✅ 트리거 의존성을 명확히 파악할 수 있습니다

트리거 사용 권장사항:

  1. 꼭 필요한 경우에만 사용
  2. 트리거 내 복잡한 로직은 프로시저로 분리
  3. 정기적인 성능 모니터링
  4. 명확한 명명 규칙과 문서화

 

반응형