Outbox 패턴

비즈니스 로직과 메시지 발행을 같은 트랜잭션으로 묶어 메시지 유실을 방지하는 패턴입니다.

기존 문제

DB 쓰기와 외부 메시지 발행이 하나의 작업처럼 보이더라도, 실제로는 서로 다른 시스템에 대한 이중 쓰기입니다.

주문 저장 (DB 트랜잭션)
→ 메시지 발행 (Kafka)
→ 메시지 발행 실패

주문은 저장됐는데 재고/결제 서비스에 알림 못함
→ 데이터 불일치 발생

반대로 메시지는 발행됐지만 DB 트랜잭션 커밋이 실패하면, 외부 시스템은 존재하지 않는 주문 이벤트를 처리할 수 있습니다. 이런 문제를 줄이기 위해 비즈니스 데이터 저장과 이벤트 저장을 같은 DB 트랜잭션에 묶는 방식을 사용합니다.

해결 방법

메시지를 직접 발행하지 않고
→ Outbox 테이블에 저장 (같은 트랜잭션)
→ 별도 프로세스가 발행

이처럼 서비스 로직 실행과 이벤트 발행 준비를 원자적으로 다루는 방식을 트랜잭셔널 메시징이라고 볼 수 있습니다.

동작 과정

[1단계 - 같은 트랜잭션]
BEGIN TRANSACTION
  orders 테이블에 주문 저장
  outbox 테이블에 이벤트 저장  ← 메시지 대신 DB에 저장
COMMIT
→ 둘 다 성공 or 둘 다 실패 (원자성 보장) O

[2단계 - 별도 프로세스]
Outbox 테이블 조회
→ Kafka 등으로 메시지 발행
→ 발행 완료 후 Outbox에서 삭제 (or 상태 변경)

Outbox 테이블 구조

CREATE TABLE outbox (
    id          BIGINT PRIMARY KEY,
    aggregate   VARCHAR(50),   -- 어떤 도메인 (ORDER, PAYMENT 등)
    payload     JSON,          -- 메시지 내용
    status      VARCHAR(20),   -- PENDING, PUBLISHED
    created_at  TIMESTAMP
);

Java 예시

@Transactional
public void createOrder(OrderRequest request) {
    // 1. 주문 저장
    Order order = orderRepository.save(new Order(request));

    // 2. Outbox에 이벤트 저장 (같은 트랜잭션)
    outboxRepository.save(OutboxEvent.builder()
        .aggregate("ORDER")
        .payload(objectMapper.writeValueAsString(order))
        .status(OutboxStatus.PENDING)
        .build());
    // 트랜잭션 커밋 시 둘 다 저장 O
}

// 별도 스케줄러 or CDC
@Scheduled(fixedDelay = 1000)
public void publishPendingEvents() {
    outboxRepository.findByStatus(OutboxStatus.PENDING)
        .forEach(event -> {
            kafkaTemplate.send("order-topic", event.getPayload());
            event.setStatus(OutboxStatus.PUBLISHED);
            outboxRepository.save(event);
        });
}

Outbox 발행 방법 2가지

1. Polling
   스케줄러가 주기적으로 Outbox 조회 후 발행
   → 구현 간단, 실시간성 낮음

2. CDC (Debezium)
   Outbox 테이블 변경을 CDC가 감지 후 발행
   → 실시간성 높음, 인프라 복잡

장단점

장점단점
메시지 유실 방지Outbox 테이블 관리 필요
트랜잭션 일관성 보장구현 복잡도 증가
At-Least-Once 보장중복 발행 가능 (멱등성 필요)

중복 발행 문제

Outbox → 발행 성공
→ 상태 변경 전 장애 발생
→ 재시도 시 중복 발행

해결: Consumer가 멱등성 보장
→ 같은 메시지 여러 번 받아도 한 번만 처리
→ 메시지 ID로 중복 체크

주의사항

Outbox 패턴은 메시지 유실 없이 트랜잭션 일관성을 보장하는 핵심 패턴으로, 마이크로서비스에서 이벤트 드리븐 아키텍처를 구현할 때 널리 사용됩니다.

→ 트랜잭셔널 아웃박스 패턴에 대해서 설명해주세요

CSLOG에서 보기