예를 들어, 너가 학생이랑 강좌를 관리하는 앱을 만들고 있다고 해보자. students라는 테이블이 있고, 거기엔 last_modified라는 필드가 있어. 이 필드는 학생 데이터(예를 들어 이름이나 나이)를 바꿀 때마다 자동으로 업데이트돼야 해.
매번 SQL 쿼리마다 last_modified 업데이트하는 코드를 직접 쓰는 대신, 우리가 트리거를 만들어서 이걸 자동으로 처리하게 할 거야.
students 테이블 구조
먼저 예제에서 쓸 students 테이블을 만들어보자. 이 테이블엔 학생에 대한 기본 정보가 들어가:
CREATE TABLE students (
student_id SERIAL PRIMARY KEY, -- 학생 고유 식별자
name VARCHAR(100) NOT NULL, -- 학생 이름
age INT, -- 학생 나이
last_modified TIMESTAMP NOT NULL DEFAULT NOW() -- 마지막 변경 시간
);
last_modified필드는 레코드가 생성될 때 현재 시간(NOW())으로 자동 채워져.- 이 필드는 학생 데이터가 바뀔 때마다 자동으로 업데이트될 거야.
테이블에 테스트용 데이터를 좀 넣어보자:
INSERT INTO students (name, age)
VALUES
('오토 린', 20),
('마리아 치', 22),
('알렉스 송', 19);
이제 테이블 데이터는 이렇게 보여:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | 오토 린 | 20 | 2023-10-15 12:00:00 |
| 2 | 마리아 치 | 22 | 2023-10-15 12:00:00 |
| 3 | 알렉스 송 | 19 | 2023-10-15 12:00:00 |
last_modified 업데이트용 함수 만들기
PL/pgSQL 함수는 트리거에서 last_modified 필드 값을 업데이트할 때 쓸 거야. 이 함수는 레코드가 바뀌기 전에 자동으로 호출돼.
update_last_modified 함수를 만들어보자:
CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
-- last_modified 필드를 현재 시간으로 업데이트
NEW.last_modified := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
NEW는 변경된(업데이트된) 레코드의 새로운 데이터를 담고 있는 특별한 변수야.NEW.last_modified에NOW()(현재 날짜와 시간)을 넣어줘.- 함수는 업데이트된
NEW를 리턴해야 트리거가 제대로 동작해.
트리거 만들기
이제 students 테이블에서 레코드가 업데이트될 때마다 update_last_modified 함수를 자동으로 호출하는 트리거를 만들어보자.
CREATE TRIGGER set_last_modified
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();
여기서 무슨 일이 일어나냐면:
BEFORE UPDATE는 업데이트 작업이 실행되기 전에 트리거가 동작한다는 뜻이야.FOR EACH ROW는 바뀌는 각 행마다 트리거가 실행된다는 의미야.EXECUTE FUNCTION update_last_modified()는update_last_modified함수를 호출하라는 뜻이야.
트리거 테스트하기
이제 트리거가 잘 동작하는지 확인해보자. students 테이블에서 현재 데이터를 조회해보자:
SELECT * FROM students;
결과:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | 오토 린 | 20 | 2023-10-15 12:00:00 |
| 2 | 마리아 치 | 22 | 2023-10-15 12:00:00 |
| 3 | 알렉스 송 | 19 | 2023-10-15 12:00:00 |
이제 student_id = 1인 학생의 나이를 업데이트해보자:
UPDATE students
SET age = 21
WHERE student_id = 1;
다시 테이블 데이터를 조회해보자:
SELECT * FROM students;
예상 결과:
| student_id | name | age | last_modified |
|---|---|---|---|
| 1 | 오토 린 | 21 | 2023-10-15 14:00:00 |
| 2 | 마리아 치 | 22 | 2023-10-15 12:00:00 |
| 3 | 알렉스 송 | 19 | 2023-10-15 12:00:00 |
여기서 보면 student_id = 1인 레코드의 last_modified 필드만 현재 시간으로 바뀌었고, 나머지는 그대로야.
트리거 로직 확장하기
이번엔 last_modified 필드가 특정 컬럼이 바뀔 때만 업데이트되게 하고 싶다고 해보자. 예를 들어, 학생 이름이나 나이가 바뀔 때만 트리거가 동작하고, 다른 변경엔 동작하지 않게 말이야.
이럴 땐 트리거 정의에 WHEN 조건을 추가하면 돼.
조건이 있는 새 트리거를 만들어보자:
DROP TRIGGER IF EXISTS set_last_modified ON students;
CREATE TRIGGER set_last_modified
BEFORE UPDATE ON students
FOR EACH ROW
WHEN (OLD.name IS DISTINCT FROM NEW.name OR OLD.age IS DISTINCT FROM NEW.age)
EXECUTE FUNCTION update_last_modified();
여기서:
WHEN조건은name이나age컬럼의 이전 값(OLD)과 새 값(NEW)이 다를 때만 트리거가 동작하게 해.- 이 컬럼들이 안 바뀌면 트리거는 실행되지 않아.
이제 다시 테이블 데이터를 업데이트해서 새 로직을 테스트해보자.
트리거 사용할 때 팁
- 트리거를 너무 남발하지 마. 편하긴 한데, DB 로직이 복잡해지고 디버깅이 어려워질 수 있어.
- 트리거가 뭘 하는지, 언제 쓰는지 항상 문서화해둬.
WHEN조건을 잘 써서 불필요한 트리거 호출을 줄여.- 트리거는 특히 테이블에 데이터가 많을 때 DB 성능에 영향을 줄 수 있다는 걸 기억해.
트리거 쓸 때 흔한 실수
데이터를 잘못 바꿈. 예를 들어 NEW에 값을 안 넣고 원래 데이터 그대로 리턴하면 안 돼.
조건을 잘못 씀. 예를 들어 WHEN 조건을 빼먹어서, 바꿀 필요 없을 때도 트리거가 실행될 수 있어.
재귀 호출. 트리거가 함수를 호출하고, 그 함수가 다시 트리거를 부르면 무한 루프가 생길 수 있어. PostgreSQL이 재귀 방지 기능이 있긴 하지만, 이런 상황은 피하는 게 좋아.
이 예제처럼 트리거를 잘 쓰면 데이터 자동 업데이트 작업이 훨씬 쉬워져. 실제 프로젝트에서도 변경 이력 관리, 데이터 무결성 유지, 반복 작업 자동화 등에 자주 쓰이는 테크닉이야.
GO TO FULL VERSION