본문 바로가기
카테고리 없음

트랜잭셔널 메세지 PGMQ를 활용하며

by 공덕 Bro 2025. 5. 9.

사내 이기종 DB 데이터 처리 고도화 프로젝트를 진행하며 트랜잭셔널 메세지에 대해서 알아볼 수 있는 좋은 기회가 생겼다.

기존에는 maria db에서 binary log 를 만들어내고 이를 읽고 처리해서 postgres (web application에서 사용하는 db) 로 데이터를 api로 전송하게 해줬다

 

이 방식은 트랜잭션 로그 테일링 패턴과 유사하지만 부족한 점이 많았음

그러나, 최근 maria db 에서 postgres로의 migration을 진행했고 이에 따라 이기종 db 데이터 처리 프로젝트도 함께 변화해야 했다.

 

이 과정에서 트랜잭셔널 메세징 방식인 OutBox Pattern을 사용하고자 제안했고, 이를 더 간편하게 해주는 pgmq 라이브러리를 적용하게 됐는데 현재 포스팅에선 pgmq 라이브러리 사용 방식이 아닌 핵심 아이디어인 Outbox Pattern과 트랜잭션 메세징에 대해 알아볼 예정이다. (postgres가 아니더라도 나중에는 반드시 트랜잭션 메세징을 사용할 날이 올 것이므로!)

 

 

트랜잭션 메세징이란?

트랜잭션 메세징은 데이터베이스 작업(Transaction)과 메세지 발송을 원자적으로 처리하는 개념을 뜻한다. 보통 트랜잭션 메세징은 분산 시스템 및 이벤트 기반 아키텍쳐에서 데이터를 변경하면서, 동시에 다른 시스템으로 해당 변경 내용을 안전하고 일관되게 전파하기 위해 사용한다.

 

일반적으로 데이터베이스 변경 (CRUD)과 메세지 발송 (예 : Kafka)는 서로 별개의 작업으로 처리된다. 그러나 별개의 작업 이라는 단어에서 볼 수 있듯이 문제점을 예상해 볼 수 있다.

 

문제 1 : 상태 불일치

  • Application이 DB 작업 이후 Message (kafka)를 발송한다고 가정
    • DB 작업 성공 → Message 전송 실패 (네트워크 문제로 인하여)
    • 결과적으로 DB에는 반영됐지만 Message는 발송되지 않음
    • 데이터 변경 상태와 Message 상태가 불일치 하게됨

문제 2 : 중복 메세지 전송 가능성

  • 문제 1과 동일한 작업 프로세스를 따른다고 가정
    • DB 작업 실패로 인한 Rollback → Message 전송
    • DB엔 반영되지 않았지만 Message는 전송됐으므로 추후 동일 프로세스를 다시 진행할 경우 중복 Message 발생 가능
    • 중복 메세지를 처리해줘야 함.

별개의 작업 이라는 단어만 보더라도 이러한 문제가 발생한다는 것을 확인 할 수 있다.

트랜잭션 메세징은 위와 같은 상황을 해결하기 위해 데이터 변경메세지 발송을 하나의 트랜잭션으로 묶어 원자적으로 처리하는 것을 말한다.


트랜잭션 메세징의 구현

트랜잭션 메세징은 다양한 방식의 구현 방식이 있지만 대표적인 방법을 소개하도록 하겠다.

Outbox Pattern

Outbox Pattern은 데이터베이스 자체를 활용하여 데이터 변경과 메세지 발송을 하나의 트랜잭션으로 처리해준다. 참고로 이 방식은 PGMQ가 구현된 아이디어다.

 

 

예시로 주문 서비스에서 Order table에 데이터를 변경시킨다면 Outbox Table에 동일하게 메세지를 삽입해주는 과정인 것이다

SQL 로 Transaction 과정을 보면 다음과 같다

 

BEGIN;

INSERT INTO ORDER (order_id, product_id, quantity) VALUES (1, '치킨', 3);

INSERT INTO OUTBOX(event_type, payload, created_at) VALUES ('ORDER_CREATE',
 '{"order_id":1}, NOW());
 
 COMMIT;

 

위 SQL 문을 보면 ORDER 테이블에 접근하는 것과 OUTBOX 테이블 접근이 하나의 트랜잭션으로 묶여 있기에 트랜잭션 메세지의 문제점인 상태 불일치와 중복 메세지 전송에 대한 문제가 해결 될 수 있다는 것을 알 수 있다.

 

OUTBOX에도 데이터가 정상적으로 삽입됐다면 메세지 발송은 별도의 Worker에 의해서 OUTBOX 테이블에서 처리하면 된다. 나의 경우 Message Relay 부분을 Postgres에서 제공하는 LISTEN/NOTIFY 기능을 활용했다. 각 row 별 trigger를 적용시켜 데이터 변화가 감지되면 NOTIFY를 보내주는 방식을 활용했다.

Transaction log tailing Pattern

OutBox 패턴과 동일하게 OUTBOX 테이블에 INSERT 하는 것 까진 동일하나 OUTBOX 테이블의 내용을 읽을 때 Transaction Log를 읽게된다

  • Transaction Log란?Postgres : WAL
  • AWS DynamoDB : table streams
  • MySQL : binlog

 

이 경우 아웃박스 테이블의 데이터 변경분만 메세지 큐에 보내게 된다. (이 방식이 고도화 프로젝트 이전 방식의 이기종 DB 데이터 처리 방식이였다)

 

이 외에도 많은 방식이 존재하지만 주로 사용하고 복잡도를 생각할 경우 위의 방법들이 가장 많이 쓰이는 것으로 생각된다.