사전 트리거
용어함정

트리거

gabury1고친 사람 github-actions[bot]

트리거는 어떤 일이 벌어지면 정해 둔 동작이 사람 손 없이 시작되게 합니다. 그런데 무엇을 트리거라고 부르는지가 분야마다 다릅니다. 데이터베이스에서는 테이블의 값이 바뀔 때 저절로 실행되는 코드 묶음을 트리거라고 부릅니다. 자동화 도구에서는 워크플로를 출발시키는 조건을 트리거라고 부릅니다. 워크플로는 빌드·테스트·배포처럼 여러 단계로 나뉜 일을 차례대로 자동 처리하는 흐름입니다.

쉽고 빠른 이해

무슨 일을 하는 물건인가 — 「이 일이 생기면 저 일을 한다」를 미리 걸어 둡니다. 주문 테이블의 상태가 바뀔 때마다 기록 테이블에 한 줄을 남기는 것도 트리거입니다. 코드를 올릴 때마다 테스트를 돌리게 하는 설정도 트리거입니다.

왜 이렇게 하나 — 사람이 매번 기억해서 챙기면 언젠가 빠뜨립니다. 일이 생기는 곳에 동작을 걸어 두면 누가 그 일을 일으키든 빠짐없이 실행됩니다.

어떻게 도나

  1. 어떤 일을 기다릴지 정합니다. 값 수정, 코드 올리기, 정해진 시각 같은 것입니다
  2. 그 일이 생기면 시스템이 알아챕니다
  3. 걸어 둔 동작을 시작합니다

대가 — 동작이 눈에 안 띄는 곳에서 실행됩니다. 데이터베이스 쪽은 앱 코드만 읽어서는 왜 값이 바뀌었는지 모르게 됩니다. 자동화 쪽은 조건을 너무 넓게 걸면 필요 없는 실행이 쌓입니다. 그리고 두 뜻을 섞어 부르면 대화가 엇갈립니다.

상세

이 절은 「트리거」가 가리키는 두 물건을 나란히 놓고 봅니다. 먼저 두 뜻을 표 하나로 가릅니다. 그다음 데이터베이스 트리거와 워크플로 트리거를 차례로 따라갑니다.

끝에서는 대화에서 어느 뜻인지 알아채는 단서를 모읍니다. 이름이 비슷한 이웃 낱말도 함께 짚습니다.

두 뜻

두 뜻 모두 「일이 생기면 무언가를 시작한다」는 뼈대를 나눕니다. 갈리는 곳은 낱말이 가리키는 범위입니다. 데이터베이스에서는 기다리는 일과 실행할 동작을 한데 묶은 것이 트리거입니다. 워크플로에서는 기다리는 일, 곧 출발 조건만 트리거입니다.

맥락 트리거가 가리키는 것 실행되는 동작은 예
데이터베이스 테이블에 걸어 둔 규칙 한 벌. 사건과 동작을 함께 담는다 트리거 안에 들어 있다 주문 상태가 바뀌면 기록 테이블에 한 줄 남기기
워크플로 · 자동화 워크플로를 출발시키는 조건 트리거 밖의 워크플로다 코드를 올리면 테스트 워크플로 시작

표의 「실행되는 동작은」 열이 가장 헷갈리는 대목입니다. 「트리거를 지웠다」는 말이 데이터베이스에서는 동작까지 사라졌다는 뜻입니다. 워크플로에서는 출발 조건 하나만 빠졌고 워크플로 자체는 남아 있다는 뜻입니다.

데이터베이스 트리거

데이터베이스 트리거는 테이블에 붙여 두는 규칙입니다. 그 테이블에 행이 들어오거나, 바뀌거나, 지워지면 데이터베이스가 규칙 안의 SQL(Structured Query Language, 구조화 질의 언어) 문장을 스스로 실행합니다. 앱이 따로 부르지 않아도 됩니다.

한 테이블을 여러 앱과 배치 작업이 함께 고친다고 해 봅시다. 「상태가 바뀌면 이력을 남긴다」는 규칙을 앱마다 넣으면 어느 한 곳은 꼭 빠뜨립니다. 규칙을 데이터 곁에 두면 누가 고치든 한 번씩 실행됩니다.

흔히 쓰는 곳은 셋입니다.

쓰임 하는 일
감사 로그 누가 언제 무엇을 바꿨는지 다른 테이블에 남긴다
파생 값 갱신 주문 행이 늘면 고객 테이블의 주문 수도 함께 올린다
복잡한 검사 제약조건으로 못 거는 규칙을 검사하고 어기면 막는다

아래는 감사 로그를 남기는 트리거입니다. SQLite 문법으로 적었습니다. 주문 테이블의 status 열이 바뀔 때마다 기록 테이블에 옛 값과 새 값을 한 줄씩 넣습니다.

SQL
CREATE TRIGGER order_audit
AFTER UPDATE OF status ON orders
FOR EACH ROW
BEGIN
  INSERT INTO order_log
  VALUES (OLD.id, OLD.status, NEW.status);
END;

AFTER UPDATE OF status ON orders 는 orders 테이블의 status 열이 바뀐 뒤에 실행하라는 뜻입니다. FOR EACH ROW 는 바뀐 행마다 한 번씩 실행하라는 뜻입니다.

OLD 는 바뀌기 전의 행이고 NEW 는 바뀐 뒤의 행입니다. 트리거 안에서만 쓰는 이름입니다.

이제 앱은 주문 상태만 고칩니다. 7번 주문의 상태가 new 였다고 해 봅시다. 기록 테이블에는 앱이 손대지 않았는데도 한 줄이 생깁니다.

SQL
UPDATE orders SET status = 'paid'
WHERE id = 7;
SELECT * FROM order_log;  -- 7|new|paid

데이터베이스 트리거를 정하는 세 가지

트리거를 만들 때는 세 가지를 고릅니다. 무슨 일을 기다릴지, 그 일의 앞과 뒤 중 언제 돌지, 몇 번 돌지입니다.

고르는 것 선택지 뜻
기다리는 일 INSERT · UPDATE · DELETE 행이 들어올 때 · 바뀔 때 · 지워질 때
도는 시점 BEFORE · AFTER 변경이 반영되기 전 · 반영된 뒤
도는 횟수 행마다(FOR EACH ROW) · 문장마다 바뀐 행 하나에 한 번 · SQL 문장 하나에 한 번

BEFORE 트리거는 값이 들어가기 전에 실행됩니다. 그래서 규칙에 어긋나는 값을 보고 오류를 내 변경을 막는 데 씁니다. AFTER 트리거는 변경이 끝난 뒤 실행됩니다. 위의 감사 로그처럼 결과를 다른 곳에 옮겨 적는 데 씁니다.

도는 횟수는 제품마다 지원 범위가 다릅니다. 행마다 도는 트리거는 대부분이 지원합니다. 문장마다 한 번 도는 트리거는 없는 제품도 있습니다. 위 예제의 SQLite 는 FOR EACH ROW 만 받습니다.

데이터베이스 트리거와 트랜잭션

트리거는 자기를 깨운 SQL 문장과 같은 트랜잭션 안에서 실행됩니다. 트랜잭션은 여러 변경을 「전부 되거나 전부 안 되거나」로 묶는 단위입니다.

롤백은 트랜잭션 안의 변경을 없던 일로 되돌리는 것입니다. 트리거가 실패하면 원래 문장의 변경도 함께 롤백됩니다.

sequenceDiagram
    participant 앱
    participant 주문 테이블
    participant 트리거
    participant 기록 테이블
    앱->>주문 테이블: UPDATE 로 상태를 바꾼다
    주문 테이블->>트리거: 행이 바뀌었다
    트리거->>기록 테이블: INSERT 로 한 줄 남긴다
    Note over 앱,기록 테이블: UPDATE 와 INSERT 가 한 트랜잭션이다
    주문 테이블-->>앱: 둘 다 성공해야 UPDATE 가 끝난다

그림에서 앱이 보낸 것은 UPDATE 한 문장뿐입니다. 그래도 기록 테이블의 INSERT 가 같은 트랜잭션에 묶입니다. 기록을 못 남기면 상태 변경도 취소됩니다. 그래서 이력이 빠진 변경은 남지 않습니다.

데이터베이스 트리거의 대가

첫째 대가는 동작이 숨는다는 것입니다. 앱 코드에는 UPDATE 한 줄만 보입니다. 다른 테이블이 왜 바뀌었는지 찾으려면 데이터베이스에 걸린 트리거 목록을 따로 열어 봐야 합니다.

둘째는 느려질 수 있다는 것입니다. 행마다 도는 트리거는 한 문장이 행 만 개를 고치면 만 번 실행됩니다. 대량으로 데이터를 넣을 때 트리거가 걸려 있으면 그만큼 일이 늘어납니다.

셋째는 연쇄입니다. 트리거가 고친 테이블에 또 다른 트리거가 걸려 있으면 그 트리거도 실행됩니다. 이런 사슬이 길어지면 한 문장이 무엇까지 건드리는지 읽어 내기 어렵습니다.

워크플로 트리거

워크플로는 여러 단계로 나뉜 일을 정해진 차례대로 자동으로 처리하는 것입니다. 빌드하고, 테스트하고, 배포하는 흐름이 흔한 예입니다. 워크플로 트리거는 이 흐름을 언제 출발시킬지 정하는 조건입니다.

코드를 올릴 때마다 누군가 테스트를 손으로 돌려야 한다면 바쁜 날에는 건너뜁니다. 출발 조건을 걸어 두면 조건이 맞을 때마다 워크플로가 스스로 시작합니다. 지속적 통합이 기대는 것도 이 장치입니다. 지속적 통합은 코드를 합칠 때마다 빌드와 테스트를 자동으로 돌리는 방식입니다.

코드 저장소에서 쓰는 낱말 둘을 먼저 짚습니다. 브랜치는 저장소 안에서 갈라져 따로 쌓이는 작업 줄기입니다. 푸시는 내 컴퓨터에서 만든 코드 변경을 원격 저장소로 올리는 일입니다.

트리거는 대개 네 가지로 나뉩니다.

종류 출발하는 때 예
이벤트 저장소에서 어떤 일이 벌어졌을 때 코드를 푸시했을 때 · 풀 리퀘스트(내 브랜치를 합쳐 달라는 요청)를 열었을 때
스케줄 정해 둔 시각이 됐을 때 매일 새벽 세 시
수동 실행 사람이 버튼이나 명령으로 시작할 때 배포를 손으로 한 번 돌리기
외부 호출 다른 시스템이 알려 왔을 때 웹훅 요청 · 다른 워크플로가 끝났을 때

이벤트 트리거에는 보통 거르는 조건을 덧붙입니다. 「푸시했을 때」만 걸면 모든 브랜치의 모든 푸시에 워크플로가 실행됩니다. 「main 브랜치에 푸시했을 때」로 좁히면 필요한 때만 실행됩니다.

flowchart TD
    A["코드를 푸시한다"] --> B{"main 브랜치인가"}
    B -->|그렇다| C["테스트 워크플로를 시작한다"]
    B -->|아니다| D["아무것도 하지 않는다"]

아래는 GitHub Actions 의 설정 파일 일부입니다. YAML(YAML Ain't Markup Language) 형식으로 적습니다. on: 아래가 트리거입니다. 세 트리거를 한 워크플로에 함께 걸었습니다.

YAML
on:
  push:
    branches: [main]  # main 푸시
  schedule:
    - cron: "0 3 * * *"  # 매일 3시
  workflow_dispatch:  # 손으로 시작

cron 줄의 다섯 칸은 분 · 시 · 일 · 월 · 요일입니다. * 는 「아무 값이나」라는 뜻이라 0 3 * * * 는 날짜와 요일을 가리지 않고 매일 3시 0분입니다. 이 표기법은 유닉스의 cron 에서 왔습니다. GitHub Actions 는 이 시각을 UTC(Coordinated Universal Time, 협정 세계시)로 읽습니다.

세 조건은 「또는」으로 묶입니다. 하나라도 맞으면 같은 워크플로가 출발합니다.

워크플로 트리거의 대가

조건을 넓게 걸면 필요 없는 실행이 쌓입니다. 문서 한 줄을 고쳐도 전체 빌드가 돌아갑니다. 그만큼 기다림과 실행 비용이 늘어납니다.

좁게 걸면 반대로 돌아야 할 때 안 돌아갑니다. 파일 경로로 조건을 거르다가 새 폴더를 빼먹으면 그 폴더의 변경은 테스트 없이 합쳐집니다. 워크플로가 실행되지 않은 것은 오류로 뜨지 않아서 늦게 발견됩니다.

어느 뜻인지 가르는 단서

대화나 문서에서 「트리거」가 나오면 함께 붙은 낱말을 봅니다. 대개 그것만으로 어느 뜻인지 갈립니다.

함께 나오는 말 뜻
테이블에 건다 · CREATE TRIGGER · BEFORE · AFTER · NEW · OLD 데이터베이스
행마다 · 트리거가 롤백시켰다 데이터베이스
워크플로가 트리거됐다 · on: · 푸시 트리거 · 스케줄 트리거 워크플로
파이프라인(워크플로를 달리 부르는 말) · 빌드 · 수동으로 트리거한다 워크플로

워크플로나 빌드 이야기가 아닌 곳에서 「트리거한다」를 동사로 쓰면 대개 「시작시킨다」는 뜻의 일상어입니다. 「이 버튼이 다시 그리기를 트리거한다」 같은 문장은 두 뜻 어느 쪽도 아닙니다.

이름이 비슷한 에지 트리거와 레벨 트리거는 또 다른 이웃입니다. 운영체제가 프로그램에 「이 소켓에서 읽을 수 있다」 같은 알림을 줄 때, 상태가 바뀌는 순간에만 줄지 상태가 이어지는 동안 계속 줄지를 가르는 말입니다. 데이터베이스 트리거와도 워크플로 트리거와도 다른 물건입니다.

관련 항목

데이터베이스 트리거가 걸리는 데이터베이스 객체

테이블 · 뷰 · 인덱스 · 제약조건 · 스키마 · 저장 프로시저

데이터베이스 트리거가 기대는 트랜잭션 성질

트랜잭션 · 롤백 · 커밋 · 원자성 · 참조 무결성

데이터베이스 트리거를 쓰는 용도

감사 로그 · 파생 값 · 비정규화 · EAV · 변경 데이터 캡처

데이터베이스 트리거를 구현한 제품

SQLite · PostgreSQL · MySQL · 관계형 데이터베이스 · SQL

워크플로 트리거가 출발시키는 흐름

워크플로 · 파이프라인 · 지속적 통합 · CI/CD · 커밋에서 배포까지

워크플로 트리거의 종류

이벤트 · 스케줄 · cron · 수동 실행 · 웹훅 · 푸시 · 풀 리퀘스트 · 머지 리퀘스트

워크플로 트리거를 적는 도구와 형식

GitHub Actions · YAML · 브랜치 · 콜백 · 이벤트 기반 모델

트리거와 이름이 겹치는 이웃

에지 트리거 · 레벨 트리거 · 이벤트 리스너 · 훅

다른 이름: trigger · 데이터베이스 트리거 · 워크플로 트리거