CodeGym /행동 /SQL SELF /PostgreSQL에서 트리거 타입: BEFORE, AFTER, INSTEAD OF

PostgreSQL에서 트리거 타입: BEFORE, AFTER, INSTEAD OF

SQL SELF
레벨 57, 레슨 1
사용 가능

PostgreSQL에서 트리거는 세 가지 주요 카테고리로 나뉘어:

  1. BEFORE — 메인 작업(예: INSERT, UPDATE 또는 DELETE) 전에 실행돼. 이걸로 작업을 막거나, 저장 전에 데이터를 수정할 수 있어.

  2. AFTER — 메인 작업이 끝난 후에 실행돼. 주로 로그 남기기, 관련 레코드 만들기, 작업이 성공적으로 끝난 뒤에 뭔가 해야 할 때 써.

  3. INSTEAD OF — 실제 작업 대신 실행돼. 이건 오직 뷰(view)에서만 쓸 수 있어. 예를 들어, 사용자가 뷰에 데이터를 넣으려고 하면, INSTEAD OF 트리거로 그 과정을 직접 컨트롤할 수 있지.

BEFORE 트리거

BEFORE 트리거는 PostgreSQL이 메인 작업을 실행하기 전에 동작해. 저장 직전에 데이터를 체크하거나 바꾸고 싶을 때 유용하지. 비행기 타기 전에 짐 검사하는 거랑 비슷하다고 생각하면 돼: 짐이 안 맞으면 바꾸거나 아예 막을 수 있지.

데이터를 삽입하기 전에 검증하는 예시를 들어볼게. 우리에겐 students라는 테이블이 있고, 학생 정보를 저장해. 학생 나이가 100살을 넘지 않게 하고 싶어(물론 거의 없겠지만 ㅎㅎ).

테이블을 만들어보자:

CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    age INT NOT NULL
);

트리거용 함수를 만들어보자:

CREATE OR REPLACE FUNCTION validate_age()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.age > 100 THEN
        RAISE EXCEPTION '학생 나이는 100살을 넘을 수 없어!';
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

트리거를 만들어보자:

CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION validate_age();

이제 학생 나이가 100살 넘는 값을 넣으려고 하면 PostgreSQL이 에러를 뱉어:

INSERT INTO students (name, age) VALUES ('이반 이바노프', 120);
-- 에러: 학생 나이는 100살을 넘을 수 없어!

이렇게 체크하는 거야!

AFTER 트리거

AFTER 트리거는 메인 작업이 성공적으로 끝난 후에 실행돼. 작업 결과에 따라 뭔가 해야 할 때 좋아. 예를 들어 로그 남기거나, 관련 레코드 만들 때.

시나리오: students 테이블이 있고, 모든 변경사항을 별도의 로그 테이블에 기록하고 싶어.

로그 테이블을 만들어보자:

CREATE TABLE students_log (
    id SERIAL PRIMARY KEY,
    student_id INT,
    operation TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

트리거용 함수를 만들어보자:

CREATE OR REPLACE FUNCTION log_student_changes()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO students_log (student_id, operation)
    VALUES (NEW.id, TG_OP);  -- TG_OP에는 작업 타입: INSERT, UPDATE, DELETE가 들어감
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

트리거를 만들어보자:

CREATE TRIGGER after_insert_students
AFTER INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION log_student_changes();

이제 새 학생을 추가하면 PostgreSQL이 자동으로 로그에 기록해:

INSERT INTO students (name, age) VALUES ('안나 링', 22);

SELECT * FROM students_log;
-- 결과:
-- id | student_id | operation |        timestamp
--  1 |          1 | INSERT    | 2023-11-15 12:00:00

INSTEAD OF 트리거

INSTEAD OF 트리거는 작업 대신 실행돼. 이건 뷰(view)에서만 쓸 수 있는 유일한 트리거야. 뷰에서 직접 작업할 수 없는 경우, 이걸로 유연하게 처리할 수 있어.

시나리오: courses와 teachers라는 두 테이블이 있어. 이걸 합친 뷰를 만들고, 그 뷰에 INSERT할 때 동작하는 트리거를 만들어볼 거야.

테이블을 만들어보자:

CREATE TABLE courses (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    teacher_id INT NOT NULL
);

CREATE TABLE teachers (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

뷰를 만들어보자:

CREATE VIEW course_details AS
SELECT 
    courses.id AS course_id,
    courses.name AS course_name,
    teachers.name AS teacher_name
FROM courses
JOIN teachers ON courses.teacher_id = teachers.id;

문제: 뷰는 두 테이블의 데이터를 합치니까, 그냥 INSERT할 수 없어. 해결책: INSTEAD OF 트리거를 쓰자.

트리거용 함수를 만들어보자:

CREATE OR REPLACE FUNCTION insert_course_details()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO teachers (name) VALUES (NEW.teacher_name) RETURNING id INTO NEW.teacher_id;
    INSERT INTO courses (name, teacher_id) VALUES (NEW.course_name, NEW.teacher_id);
    RETURN NULL;  -- 뷰에는 데이터가 저장되지 않아
END;
$$ LANGUAGE plpgsql;

트리거를 만들어보자:

CREATE TRIGGER instead_of_insert_course_details
INSTEAD OF INSERT ON course_details
FOR EACH ROW
EXECUTE FUNCTION insert_course_details();

이제 뷰에 바로 데이터를 넣을 수 있어:

INSERT INTO course_details (course_name, teacher_name)
VALUES ('수학', '알렉스 밍');

SELECT * FROM courses;
-- 결과:
-- id | name        | teacher_id
--  1 | 수학        | 1

SELECT * FROM teachers;
-- 결과:
-- id | name
--  1 | 알렉스 밍

트리거 타입 비교

트리거 타입 언제 실행됨 주요 용도
BEFORE 작업 실행 전 검증, 데이터 준비
AFTER 성공적으로 끝난 후 로그, 관련 데이터 업데이트
INSTEAD OF 작업 대신 뷰에서 작업 처리

특징과 제한사항

BEFORE 트리거는 작업 전에 데이터를 바꿀 수 있어. 예를 들어, 이름을 자동으로 대문자로 바꿀 수도 있지.

AFTER 트리거는 이미 작업이 끝난 뒤라 데이터를 바꿀 수 없어. 오직 후처리용이야.

INSTEAD OF 트리거는 뷰에만 적용돼. 여러 관련 테이블에 복잡한 삽입/수정 로직을 구현할 수 있어.

오늘은 여기까지! BEFORE, AFTER, INSTEAD OF가 좀 헷갈릴 수도 있는데, 걱정하지 마. 핵심 원리랑 언제 쓰는지만 기억하면 돼. 직접 예제 몇 개 만들어보면서 연습해봐!

2
과제
SQL SELF, 레벨 57, 레슨 1
잠금
데이터 검증을 위한 `BEFORE` 트리거 만들기
데이터 검증을 위한 `BEFORE` 트리거 만들기
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION