CodeGym /행동 /SQL SELF /트리거 개발에서 흔히 발생하는 실수 분석

트리거 개발에서 흔히 발생하는 실수 분석

SQL SELF
레벨 58 , 레슨 4
사용 가능

자, 친구들! 이제 트리거가 뭔지, 종류가 뭔지, 어떻게 동작하는지, 그리고 여러 작업에 트리거를 직접 만들어본 경험도 있을 거야. 근데 프로그래밍에서 항상 그렇듯이, 뭘 할 수 있는지 아는 것도 중요하지만, 뭘 하면 안 되는지도 꼭 알아야 해. 오늘은 트리거 다루면서 개발자들이 자주 저지르는 실수들을 같이 파헤쳐볼 거야. 이걸 알면 몇 시간, 아니면 며칠이나 디버깅하는 시간을 아낄 수 있을지도 몰라!

트리거 재귀: 트리거가 자기 자신을 호출하는 경우

이건 진짜 초보들이 제일 많이 하는 실수야. 예를 들어, last_modified 컬럼 값을 업데이트하는 트리거를 만들었다고 해보자. 근데 이 값이 바뀌면, 그 업데이트 작업이 또 트리거를 호출해. 이게 무한 루프가 돼서 결국 서버가 스택 오버플로우 에러로 죽어버려.

예시:

CREATE OR REPLACE FUNCTION update_last_modified()
RETURNS TRIGGER AS $$
BEGIN
    -- last_modified 필드 업데이트
    UPDATE my_table
    SET last_modified = NOW()
    WHERE id = NEW.id;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_update
AFTER UPDATE ON my_table
FOR EACH ROW
EXECUTE FUNCTION update_last_modified();

여기서 뭐가 문제냐면, 함수 안에서 UPDATE를 하면 그게 또 같은 트리거를 불러. 그래서 무한 루프가 생기는 거지.

어떻게 피할까:

OLD를 써서 값이 진짜 바뀌었는지 비교하고, 바뀌었을 때만 업데이트해:

CREATE OR REPLACE FUNCTION update_last_modified_safe()
RETURNS TRIGGER AS $$
BEGIN
    -- 값이 바뀌었는지 확인
    IF NEW.last_modified IS DISTINCT FROM OLD.last_modified THEN
        NEW.last_modified = NOW();
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

트리거 안에서 불필요한 작업을 호출하지 않도록 꼭 확인해!

OLDNEW 잘못 사용하기

이 변수들은 트리거 쓸 때 진짜 친구 같은 존재인데, 초보 때는 이걸 잘못 써서 머리 아픈 일이 많아. OLD는 행이 바뀌기 전 데이터를, NEW는 바뀐 후 데이터를 담고 있어.

실수는 주로 이 변수들이 없는 상황에서 쓰려고 할 때 생겨. 예를 들어 BEFORE INSERT 트리거에서는 OLD가 없어. 왜냐면 행이 이제 막 만들어지는 거니까.

실수 예시:

-- INSERT에서는 OLD가 없어서 에러남
CREATE OR REPLACE FUNCTION log_inserts()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO audit_log (old_data, new_data)
    VALUES (OLD.my_column, NEW.my_column);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

어떻게 피할까:

OLDNEW를 언제 쓸 수 있는지 잘 구분해야 해:

  • OLDUPDATEDELETE에서만 쓸 수 있어.
  • NEWINSERTUPDATE에서만 쓸 수 있어.

한 작업에 여러 트리거 달기

PostgreSQL에서는 같은 작업, 같은 테이블에 여러 트리거를 달 수 있어. 이게 편해 보일 수 있지만, 실제로는 트리거끼리 충돌하거나 같은 데이터를 바꿔서 혼란이 생길 수 있어.

예시:

-- 트리거 1
CREATE OR REPLACE FUNCTION trigger_one()
RETURNS TRIGGER AS $$
BEGIN
    -- 트리거 1 로직
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 트리거 2
CREATE OR REPLACE FUNCTION trigger_two()
RETURNS TRIGGER AS $$
BEGIN
    -- 트리거 2 로직
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 두 개의 트리거 생성
CREATE TRIGGER trigger_one AFTER INSERT ON my_table EXECUTE FUNCTION trigger_one();
CREATE TRIGGER trigger_two AFTER INSERT ON my_table EXECUTE FUNCTION trigger_two();

두 트리거가 my_table에 행이 추가될 때마다 둘 다 실행돼. 로직이 잘 안 맞으면 예측 불가한 결과가 나올 수 있어.

어떻게 피할까:

  • 트리거 구조를 미리 잘 설계해.
  • 같은 로직을 다루는 트리거라면 하나로 합쳐.

성능 문제

트리거는 연결된 작업마다 추가 연산을 하게 돼. 트리거가 많은 작업이나 데이터가 많은 테이블에 달려 있으면 성능이 확 떨어질 수 있어.

실수 예시:

CREATE OR REPLACE FUNCTION heavy_trigger_function()
RETURNS TRIGGER AS $$
BEGIN
    -- 행이 바뀔 때마다 무거운 작업 실행
    PERFORM some_heavy_query();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER performance_killer AFTER UPDATE ON huge_table EXECUTE FUNCTION heavy_trigger_function();

어떻게 피할까:

  • 트리거 안에서 하는 로직을 최대한 간단하게 만들어. 무거운 작업은 백그라운드 작업으로 빼는 것도 생각해봐.
  • WHEN 조건을 써서 트리거 실행을 제한해:
CREATE TRIGGER optimized_trigger
AFTER UPDATE ON my_table
WHEN (OLD.column_name IS DISTINCT FROM NEW.column_name)
EXECUTE FUNCTION light_function();

트리거와 트랜잭션

트리거는 네 쿼리에서 시작된 트랜잭션 안에서 실행돼. 트리거 안에서 에러가 나면 전체 트랜잭션이 롤백돼. 이게 어떤 상황에서는 유용하지만, 에러 처리가 잘 안 돼 있으면 예상치 못한 문제가 생길 수 있어.

실수 예시:

CREATE OR REPLACE FUNCTION error_prone_trigger()
RETURNS TRIGGER AS $$
BEGIN
    -- 일부러 에러 발생
    RAISE EXCEPTION '뭔가 잘못됐어!';
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

이 트리거가 실행되면, 네 쿼리의 트랜잭션 전체가 롤백돼.

어떻게 피할까:

트리거 안에서 에러 처리를 추가해서 메인 트랜잭션에 영향이 덜 가게 해봐:

CREATE OR REPLACE FUNCTION safe_trigger()
RETURNS TRIGGER AS $$
BEGIN
    BEGIN
        -- 에러가 날 수 있는 코드
        INSERT INTO another_table VALUES (NEW.data);
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE '에러가 났지만, 잘 처리했어.';
    END;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

실전 팁

  1. 트리거는 최대한 단순하게 만들어. 트리거가 너무 크거나 복잡하다고 느껴지면, 함수로 쪼개거나 로직을 다시 생각해봐.

  2. 항상 적은 데이터로 트리거를 테스트해. 중요한 테이블에 트리거 달기 전에 꼭 테스트 환경에서 먼저 확인해봐.

  3. 트리거에 주석을 잘 달아둬. 몇 달 지나면 너도, 네 동료도 왜 이 트리거를 만들었는지 까먹을 수 있어. 문서화 잘 해두면 머리 안 아파.

  4. 애플리케이션 레벨에서 해결할 수 있는 건 트리거로 하지 마. 트리거는 즉시 처리해야 하는 자동화 작업에 좋아. 근데 복잡한 비즈니스 로직은 트리거로 하면 나중에 문제 생길 수 있어.

  5. 항상 성능을 체크해. 데이터나 트래픽이 많아지면 트리거가 DB 성능에 미치는 영향도 커지니까, 모니터링 꼭 해.

이 팁들이랑 오늘 배운 내용만 잘 챙기면, 트리거를 그냥 만드는 게 아니라 진짜 제대로, 효율적으로, 그리고 문제 없이 만들 수 있을 거야!

1
설문조사/퀴즈
행과 테이블 레벨 트리거, 레벨 58, 레슨 4
사용 불가능
행과 테이블 레벨 트리거
행과 테이블 레벨 트리거
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION