현재 진행중인 학회 프로젝트에서, 앱 개발 쪽 요구사항이 새로 들어왔다.
앱에서는 도감 데이터를 일괄적으로 서버로부터 제공받아 캐싱해 사용하고, 주기적으로 해당 데이터에서 변경된 부분만 확인해 받아오려고 합니다.
그럼 서버 쪽에서는
1. 도감 데이터를 전체 제공하는 API와,
2. 특정 시각 이후 갱신된 데이터만 확인해 제공하는 API
를 제공할 필요가 있다.
이때 문제는 2번, 특정 시각 이후 갱신된 데이터를 확인하는 것이다.
언뜻 보면 "updated_at 필드만 보면 되지 않을까?" 라고 생각할 수 있지만, 문제는 관련 데이터가 여러 테이블에 걸쳐 있다는 것이다.
구체적으로는 본 테이블(bird)에 bird_habitat, bird_image, bird_residency라는 서브 테이블들이 연결된 상황.
이 방법은 일단 어렵다고 판단했다. 이유는 첫번째, 각 테이블마다 created_at, updated_at, deleted_at 존재 여부가 서로 다르고, 이 문제를 해결하기 위해서 일괄적으로 그것들을 테이블마다 붙이는 것은 배보다 배꼽이 더 큰 격이라고 생각했다. 이거 하나 때문에 모든 관련 테이블을 Soft Delete로 처리하라니! 현실적으로 비효율적이다.
두번째, 성능적으로도 비효율적이다. 서브 테이블들은 모두 본 테이블에 대해 1:N으로 연결되어 있다. 이들 각각 JOIN시켜서 하나하나 확인하고 있자니, 그것도 모든 도감 데이터에 대해서!
아무래도 다른 방법이 필요했다.
이거 괜찮았다. 예를 들어 bird_habitat 테이블에서 관련 데이터가 생성/수정/삭제될 경우마다 bird.updated_at을 갱신하는 것이다. 그러면 쿼리도 간단해지고 효율적이다.
🤔 하지만 어떻게 하지?
ChatGPT와의 대화를 통해 Trigger 개념에 대해 학습했다.
대화 전체는 여기서 볼 수 있다.
이 대화를 통해 배운 내용들을 요약하면:
- 트리거는 특정 DML 이벤트(INSERT, UPDATE, DELETE)에 반응해 자동 실행된다.
- PostgreSQL 트리거는 함수 형태로 작성되며
plpgsql
언어를 사용한다.BEFORE
트리거는 DML 실행 전,AFTER
트리거는 실행 직후 (단, 커밋 전) 실행된다.NEW
객체는 INSERT와 UPDATE 시 사용 가능하며, 변경 후 값을 담고 있다.OLD
객체는 UPDATE와 DELETE 시 사용 가능하며, 변경 전 값을 담고 있다.- 트리거는 같은 테이블 내 필드뿐 아니라 다른 테이블도 명시적 쿼리로 수정할 수 있다.
- 관계된 테이블의 변경으로 본 테이블의
updated_at
을 갱신하려면 트리거 함수 내 UPDATE 쿼리를 써야 한다.INSERT OR UPDATE
는AFTER
트리거로 처리하는 것이 일반적이다.DELETE
는OLD
를 사용해야 하므로 별도의 트리거로 관리하는 것이 명확하다.- 트리거는 트랜잭션 내부에서 실행되며, 커밋 전에 효과가 반영된다.
bird_habitat
, bird_image
, bird_residency
각각에 대해 다음 트리거들을 설정했다:
AFTER INSERT OR UPDATE
: NEW.bird_id
로 해당 bird
의 updated_at
갱신AFTER DELETE
: OLD.bird_id
로 갱신이제 도감 데이터 갱신 API는 단순히 bird.updated_at이 지난번 fetch한 시각보다 큰지만 확인하면 된다.
결과적으로, Trigger를 통해 관련 테이블의 변경을 본 테이블의 updated_at 하나로 일괄 추적할 수 있게 되었고, 클라이언트는 단순한 조건 비교만으로 변경된 데이터를 손쉽게 가져올 수 있게 되었다. 복잡한 연관 구조를 단순하게 관리하는 실용적인 해결책이었다!