데이터베이스 용어 사전
제약·뷰TRIGGER · 저장 프로시저

트리거

테이블 변경에 반응해 자동 실행되는 코드. 강력하지만 흐름이 코드에 안 보여 신중해야 한다.

INSERT·UPDATE·DELETE에 반응해 자동으로 실행되는 코드.

기본형

-- PostgreSQL: 함수를 만들고 트리거로 연결한다
CREATE OR REPLACE FUNCTION set_updated_at() RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END $$ LANGUAGE plpgsql;

CREATE TRIGGER trg_member_updated
  BEFORE UPDATE ON member
  FOR EACH ROW EXECUTE FUNCTION set_updated_at();
-- MySQL: 본문을 트리거에 직접 쓴다
CREATE TRIGGER trg_member_updated BEFORE UPDATE ON member
  FOR EACH ROW SET NEW.updated_at = NOW();

BEFORE와 AFTER

  • BEFORE — NEW 값을 고칠 수 있다 → 값 보정 · 정규화 · 검증
  • AFTER — 이미 확정됐다 → 감사 로그 · 집계 갱신 · 다른 테이블 반영

잘 맞는 용도

-- 감사 로그 — 누가 무엇을 바꿨는지 빠짐없이 남긴다
CREATE TRIGGER trg_audit AFTER UPDATE ON account
  FOR EACH ROW EXECUTE FUNCTION write_audit_log();

애플리케이션 경로가 여러 개여도 트리거는 반드시 실행된다. 감사·이력처럼 "절대 빠지면 안 되는" 일에 강하다.

왜 신중해야 하나

  • ① 흐름이 코드에 안 보인다

    • UPDATE 한 줄이 실제로 무엇을 하는지 애플리케이션 코드만 봐서는 알 수 없다
    • 신규 입사자가 원인 모를 부작용에 시간을 쏟는다
  • ② 디버깅이 어렵다

    • 스택 트레이스에 안 나온다
  • ③ 쓰기 경로에 부하와 락을 더한다

    • 대량 INSERT 시 행마다 실행된다
  • ④ 대량 작업에서 폭발한다

    • 100만 행 UPDATE → 트리거 100만 번

확인하는 법

-- PostgreSQL: 이 테이블에 어떤 트리거가 걸려 있나
SELECT tgname, pg_get_triggerdef(oid) FROM pg_trigger
 WHERE tgrelid = 'member'::regclass AND NOT tgisinternal;

-- MySQL
SHOW TRIGGERS LIKE 'member';

성능 문제를 조사할 때 트리거 목록을 먼저 확인하는 습관이 필요하다.

저장 프로시저는 어떤가

  • 장점 — 네트워크 왕복 감소 · DB 안에서 집합 연산

  • 단점 — 버전 관리·테스트·배포가 애플리케이션 코드보다 훨씬 불편하다

    • 벤더 종속(PL/pgSQL ↔ MySQL 문법이 다르다)
  • 요즘은 대량 배치 처리처럼 왕복이 결정적인 곳으로 용도가 좁아졌다

실무 기준

  • 트리거로 — 빠지면 안 되는 것 (감사 로그 · updated_at · 정합성 보정)
  • 코드로 — 업무 로직 (주문 상태 전이 · 할인 계산)

면접 함정

  • "트리거로 업무 로직을 넣으면 안전하다" → 안전하지만 유지보수가 급격히 나빠진다. 범위를 좁게 유지한다.
  • "트리거는 트랜잭션 밖에서 돈다" → 같은 트랜잭션 안이다. 실패하면 원래 문장까지 롤백된다.

대량 작업에서 무슨 일이 생기나

-- 행마다 도는 트리거가 걸린 테이블에 대량 UPDATE
UPDATE member SET status = 'ACTIVE' WHERE status IS NULL;   -- 100만 행
-- → 트리거가 100만 번 실행된다
-- → 각각이 다른 테이블을 갱신하면 락도 100만 번

-- 문장 단위 트리거로 바꾸면 한 번만 돈다 (PostgreSQL)
CREATE TRIGGER trg_stmt AFTER UPDATE ON member
  REFERENCING NEW TABLE AS changed
  FOR EACH STATEMENT EXECUTE FUNCTION handle_batch();

FOR EACH STATEMENT + 전이 테이블(NEW TABLE)을 쓰면 변경된 행 전체를 한 번에 집합으로 처리할 수 있다. 대량 작업 성능이 완전히 달라진다.

일시적으로 끄기

ALTER TABLE member DISABLE TRIGGER trg_audit;   -- 대량 이관 중
-- ... 작업 ...
ALTER TABLE member ENABLE TRIGGER trg_audit;    -- 반드시 다시 켠다 (잊으면 감사 구멍)

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 제약·트리거·뷰 — DB가 지키는 규칙·가상 테이블