728x90
안녕하세요. 이번 글에서는 비동기 메시지 전송 시 데이터 일관성을 유지하기 위한 효과적인 접근법인 Outbox Pattern에 대해서 알아보겠습니다.
해당 내용에 대한 예시코드는 [깃허브 예시코드] 에서 확인 하실 수 있습니다.
Outbox Pattern이란?
Outbox Pattern(아웃박스 패턴)은 데이터베이스 트랜잭션과 메시지 브로커 간의 데이터 일관성을 보장하기 위해 사용하는 설계 패턴입니다. 특히 마이크로서비스 아키텍처(MSA)에서 데이터 변경 이벤트를 안전하게 외부 시스템에 안정적으로 전달하거나, 응답 여부가 크게 중요하지 않은 HTTP 비동기 호출 작업의 기록 추적에도 활용할 수 있습니다.
Outbox Pattern의 필요성
먼저, 분산 시스템에서는 데이터베이스의 상태 변경과 메시지 브로커로의 이벤트 발행이 동시에 일어나야 하는 경우가 많습니다. 하지만 이 두 작업을 각각 별도의 트랜잭션으로 처리하면 다음과 같은 문제가 발생할 수 있습니다.
- 데이터베이스에는 변경이 반영되었지만, 메시지 발행에 실패하는 경우 데이터 불일치 발생
- 메시지는 발행되었으나, 데이터베이스 트랜잭션이 롤백되어 잘못된 이벤트가 외부로 전달되는 문제
이러한 문제를 해결하기 위해 Outbox Pattern을 사용하여 데이터와 메시지의 일관성을 보장할 수 있습니다.
메시지 브로커를 활용 할 수 없는 상황이라면 Kafka 처럼 메시지 발행의 로그를 남기는 역할을 가능하게 해줄 수 있으며, 또한 HTTP를 이용한 비동기 호출 방식의 경우, 네트워크 장애나 서비스 다운타임 등의 문제로 인해 메시지 손실률이 높아질 수 있습니다. Outbox Pattern은 이런 비동기 호출 방식의 메시지 손실 문제를 해결하여 데이터와 메시지의 일관성을 보장할 수 있습니다.
Outbox Pattern의 동작 방식
- 데이터베이스 내에 별도의 Outbox 테이블을 만듭니다. 이 테이블은 발행할 메시지를 임시로 저장합니다.
- 비즈니스 로직 처리 중 데이터 변경이 이루어져 트랜잭션이 정상적으로 커밋되면, 이벤트 발행 이전에 Outbox 테이블에 데이터를 저장합니다.
- 별도의 프로세스가 Outbox 테이블을 주기적으로 조회(polling)하여 처리되지 않은 이벤트를 실제 메시지 브로커에 전달합니다.
- 메시지 전달이 성공하면 Outbox 테이블의 처리 완료 상태로 변경하거나, 실패 시 Failed 상태로 전환합니다.
- Failed 상태의 메시지는 비즈니스 정책 상 처리 할 수 있는 재시도 횟수를 지정하여 실패한 메시지를 읽어들여 재시도합니다.
CREATE TABLE outbox (
id bigint not null auto_increment PRIMARY KEY,
message_id varchar(255) not null,
payload varchar(255) not null,
message_type varchar(255) not null,
fail_count integer not null,
failed_at datetime(6),
occurred_at datetime(6) not null,
processed_at datetime(6),
last_failed_reason varchar(255),
status enum ('DONE','FAILED','WAITING') not null,
)
일반적인 비동기 방식과의 비교
Outbox Pattern은 기존의 일반적인 비동기 방식과 다음과 같은 차이점이 있습니다.
- 일반적인 비동기 호출 방식: 메시지 전송과 데이터 변경이 독립된 트랜잭션으로 관리되어 실패 시 데이터 불일치 및 메시지 손실이 발생할 수 있습니다.
- Outbox Pattern: 데이터 변경과 메시지 생성이 동일한 트랜잭션으로 묶여있어 데이터 일관성을 보장하고 메시지 손실 문제를 해결합니다.
Outbox Pattern의 장단점
장점
- 데이터베이스와 메시지 브로커 간의 일관성 보장
- 장애 상황에서도 메시지 손실 방지 및 재처리 가능
- 마이크로서비스 환경에서 확장성 및 유연성 확보
단점
- 메시지 전달을 위한 별도의 프로세스를 운영해야 합니다.
- Outbox 테이블의 크기가 관리되지 않으면 비대해질 수 있어 별도의 관리가 필요합니다.
사용 사례
- 결제 처리 시스템에서 결제 완료 이벤트 발송
- 주문 시스템에서 주문 상태 변경 이벤트 발송
- 재고 관리 시스템에서 재고 변동 이벤트 발송
'Backend' 카테고리의 다른 글
| Kotlin 리스트 확장함수 사용 시 타입을 조심하자. (1) | 2025.06.01 |
|---|---|
| Rate Limiter 시리즈 - 이론편 (0) | 2025.03.16 |
| EC2 재부팅 시 Docker, Nginx 자동 실행 (1) | 2023.12.11 |
| java Test 코드 작성 시 lombok을 사용할 수 없는 경우 해결법 (0) | 2023.04.13 |