AI VIDEO BRIEFING

인박스 패턴(Inbox Pattern) in .NET: 메시지 중복 처리와 정확히 한 번 실행 구현하기

분산 메시징에서 소비자가 같은 메시지를 두 번 처리하는 문제를, 인박스 패턴으로 막는 방법을 MassTransit·Postgres·RabbitMQ 예제로 설명합니다. 아웃박스와의 차이도 정리했습니다.

.NET 인박스 패턴: 아웃박스가 채우지 못한 소비자 쪽 신뢰성 영상 대표 이미지

핵심 메시지

  • 분산 시스템에서는 어떤 것도 보장되지 않으며, 네트워크 문제나 브로커 재전송으로 소비자가 같은 메시지를 두 번 처리할 수 있다.
  • 아웃박스가 발행 측 신뢰성을 다룬다면, 인박스 패턴은 소비 측에서 중복 처리를 막는 짝이다.
  • 인박스는 메시지 ID를 유일 키로 하는 버퍼 테이블이라, 중복 ID가 들어오면 무시해 정확히 한 번 처리를 보장한다.
  • 발표자는 인박스에는 페이로드만 저장하고, 백그라운드 처리기가 미처리 메시지를 하나씩 핸들러로 넘기는 방식을 선호한다(비즈니스 로직과 한 트랜잭션에 묶을 때의 불일치 위험 회피).
  • SQL의 ON CONFLICT DO NOTHING으로 중복 삽입을 우아하게 무시하며, 직접 구현이 부담되면 Wolverine·MassTransit 같은 라이브러리가 인박스·아웃박스를 제공한다.

쉽게 이해하기

영상은 분산 시스템의 첫 번째 규칙, 즉 아무것도 보장되지 않는다는 점에서 출발합니다. 메시지 브로커와 소비자 사이의 통신은 더 큰 분산 시스템의 작은 조각이지만 여러 방식으로 실패할 수 있습니다. 네트워크 문제로 같은 메시지가 두 번 처리되거나, 소비자가 처리에는 성공했지만 브로커에 알리지 못해 재전송이 일어날 수 있습니다.

인박스 패턴은 SQL 데이터베이스 안에 중간 버퍼 테이블(인박스)을 두어 이 문제를 다룹니다. 아웃박스의 정반대로, 들어온 메시지의 ID와 페이로드를 저장하되 인박스 테이블의 유일 제약 덕분에 중복 ID(재전송)는 그냥 무시합니다. 단일 삽입은 그 자체로 원자적이라 명시적 트랜잭션이 꼭 필요하진 않습니다. 트랜잭션이 의미를 갖는 것은 인박스 저장과 비즈니스 로직을 함께 실행하려 할 때인데, 저장은 성공하고 로직이 실패하거나 커밋이 실패하면 불일치 상태에 빠질 수 있습니다.

그래서 발표자는 소비자 안에서는 인박스에 페이로드만 저장하고, 별도의 백그라운드 처리기가 미처리 메시지를 찾아 각 핸들러로 하나씩 넘기는 방식을 선호합니다. 대부분의 브로커가 최소 한 번(at least once) 전달을 보장하므로 같은 메시지가 한 번 이상 올 가능성은 늘 존재하며, 인박스는 소비자가 정확히 한 번 실행되도록(실무에서는 특히 부수 효과가 정확히 한 번 일어나도록) 보장하고 메시지 유실 가능성도 줄입니다.

구현 예제는 이미 아웃박스 패턴을 갖춘 애플리케이션에서 시작합니다. 메시징은 오픈소스 MassTransit, 인프라는 Postgres와 RabbitMQ(도커 컨테이너)를 사용합니다. 아웃박스 메시지를 본떠 inbox_messages 테이블(ID, 타입, 바이너리 JSON 콘텐츠, 수신 시각, 오류 컬럼)과 미처리 메시지 조회용 필터 인덱스를 만듭니다.

소비자는 MassTransit의 IConsumer를 구현해 OrderCreated 통합 이벤트를 받습니다. Npgsql 데이터 소스로 연결을 열고 Dapper로 INSERT 하되, 메시지 ID를 기본 키로 두고 ON CONFLICT (id) DO NOTHING을 써서 중복 시 조용히 실패시킵니다. 통합 이벤트에 MessageId를 가진 추상 베이스 레코드를 두어 인박스·아웃박스에서 같은 ID를 쓰게 합니다. 마지막으로 인박스 백그라운드 서비스가 미처리 메시지를 배치로 골라 핸들러에 넘기고 처리 완료로 표시하면서, 인박스와 아웃박스가 함께 동작합니다.

주요 인사이트

  • 아웃박스는 발행 측, 인박스는 소비 측의 신뢰성을 담당하며 둘은 분산 메시징에서 서로의 짝으로 동작한다.
  • 메시지 ID를 유일 키(기본 키)로 두면 재전송된 중복 메시지를 데이터베이스 제약만으로 걸러낼 수 있어, 애플리케이션이 별도 존재 확인을 하지 않아도 된다.
  • 인박스 저장과 비즈니스 로직을 한 트랜잭션에 묶으면 부분 실패 시 불일치 위험이 커지므로, 저장과 처리를 분리(페이로드만 저장 후 백그라운드 처리)하는 편이 견고하다.
  • 브로커는 보통 최소 한 번 전달을 보장하므로 중복은 전제로 두어야 하며, 실무의 목표는 부수 효과가 정확히 한 번 일어나게 만드는 것이다.
  • 직접 유지보수는 간단치 않으므로, Wolverine이나 MassTransit 등 대부분의 메시징 라이브러리가 제공하는 인박스·아웃박스 기능을 활용하는 선택지가 있다.

자주 묻는 질문

인박스 패턴은 무엇을 위한 것인가요?

분산 메시징에서 소비자가 같은 메시지를 두 번 처리하는 것을 막기 위한 패턴입니다. 들어온 메시지를 유일 키(메시지 ID) 기반의 인박스 테이블에 저장해 중복을 걸러내고, 정확히 한 번 처리를 보장합니다.

아웃박스 패턴과 어떻게 다른가요?

아웃박스는 발행(publisher) 측에서 메시지를 안정적으로 내보내기 위한 것이고, 인박스는 소비(consumer) 측에서 중복 수신을 처리하기 위한 것입니다. 영상에서는 둘을 서로의 짝으로 설명합니다.

중복 메시지는 코드에서 어떻게 처리하나요?

메시지 ID를 기본 키로 두고 SQL의 ON CONFLICT (id) DO NOTHING을 사용합니다. 같은 메시지가 다시 와도 삽입을 시도하되 중복이면 조용히 무시하므로, 별도의 존재 여부 확인이나 예외 처리가 필요 없습니다.

원문과 출처

이 글은 원본 영상의 자막을 바탕으로 한국어 독자를 위해 요약했습니다. 전체 맥락과 최신 정보는 원문에서 확인하세요.

YouTube 원본 영상 보기 ↗

관련 AI 소식

#인박스 패턴#아웃박스 패턴#.NET#MassTransit#분산 시스템