트랜잭셔널 아웃박스: 주문 저장과 외부 알림을 연결하기
업무 변경과 전달할 사건을 같은 트랜잭션에 기록하고, 전달기·소비자의 중복 처리·상태 전이·재시도와 수동 복구를 설계하는 방법입니다.
주문을 저장한 뒤 외부 알림을 보내는 기능에는 두 작업이 어긋날 수 있는 구간이 있습니다. 주문 저장은 성공했지만 알림이 실패하거나, 알림은 전달됐는데 주문 저장이 취소될 수 있습니다. 요청을 다시 실행하는 것만으로는 어떤 행동이 끝났는지 알기 어려워 중복 알림과 상태 불일치가 생길 수 있습니다.
트랜잭셔널 아웃박스는 업무 변경과 전달할 사건의 기록을 같은 데이터베이스 트랜잭션에 남기는 패턴입니다. 별도 전달기가 그 기록을 읽어 외부로 보내며 이후의 상태를 추적합니다. 이 글은 가상의 주문 알림을 예로 접수 보장과 외부 전달, 중복과 복구의 책임을 나누는 운영 설계를 설명합니다.
1. 어떤 두 작업을 묶을지 정한다
먼저 업무 데이터 변경과 그 결과로 전달할 사건을 구분합니다. 주문 생성과 고객 알림, 재고 예약 요청이 모두 같은 행동은 아닙니다. 어떤 사건이 반드시 남아야 하는지와 외부 시스템이 실제로 수행할 일을 목록으로 정리해야 전달의 의미가 분명해집니다.
업무 변경과 아웃박스 기록이 같은 트랜잭션에 들어가는지 확인합니다. 서로 다른 데이터베이스에 각각 저장하면 원래 해결하려던 이중 쓰기 문제가 다시 생길 수 있습니다. 사용하는 저장 기술과 트랜잭션 범위를 검토하고 제품 이름만으로 원자성을 가정하지 않습니다.
커밋이 끝났다는 사실은 외부 알림 완료가 아닙니다. 사용자가 알아야 하는 주문 상태와 알림 상태를 분리합니다. 주문 자체를 취소해야 하는 업무 조건인지, 알림만 다시 전달하면 되는지 합의해 복구가 원래 업무를 불필요하게 반복하지 않도록 합니다.
2. 사건의 식별자와 내용을 정의한다
이벤트에는 고유 식별자와 업무 객체, 사건 유형과 버전, 생성 시각을 연결합니다. 같은 주문에서 여러 변화가 생기면 각각의 사건을 구분할 수 있어야 합니다. 객체 식별자만으로 모든 메시지를 중복으로 판단하거나 전달 시도마다 새 사건을 만들지 않습니다.
전달 내용은 목적에 필요한 범위로 정합니다. 당시 값의 기록을 보낼지 외부에서 현재 객체를 조회하도록 할지 계약을 정하고 의미를 설명합니다. 민감한 정보를 무조건 복사하지 않으며 이벤트 기록의 접근과 보관 범위를 함께 검토합니다.
스키마 변경도 고려합니다. 소비자가 이전 버전만 이해하는 동안 새 필드와 의미를 어떻게 처리할지 확인합니다. 사건의 버전과 업무 상태 버전을 혼동하지 않고 과거 이벤트를 재처리할 때 해석 기준을 찾을 수 있도록 문서와 시험 자료를 남깁니다.
3. 전달기의 작업 점유를 설계한다
여러 전달기가 같은 대기 사건을 읽을 수 있다면 누가 처리 중인지 정할 방법이 필요합니다. 사용하는 데이터베이스와 실행 환경에 맞춰 점유와 동시성 제어를 설계합니다. 한 작업자가 멈췄을 때 다른 작업자가 언제 이어받을지도 확인합니다.
처리 중 상태가 영원히 남지 않도록 복구 기준을 둡니다. 다만 오래 걸린다는 이유만으로 다른 작업자가 즉시 같은 외부 행동을 시작하면 중복이 생길 수 있습니다. 점유의 유효성과 결과 확인, 재시도 정책을 함께 설계해야 합니다.
전달기는 업무 요청 처리와 자원 사용을 분리해 관찰합니다. 적체를 빠르게 줄이려다 데이터베이스 조회와 연결이 몰려 주문 기능까지 느려질 수 있습니다. 작업 크기와 동시 처리량, 외부 서비스의 한도를 검토하고 상황에 따라 조절할 수 있게 합니다.

4. 외부 전달과 완료 기록의 간격을 다룬다
외부 서비스가 요청을 받은 뒤 전달기가 완료 기록을 남기기 전에 중단될 수 있습니다. 아웃박스가 있다는 이유만으로 이 구간의 중복이 사라지지는 않습니다. 다시 처리할 때 외부 결과를 조회하거나 동일 요청 식별자를 사용할 수 있는지 계약을 확인합니다.
소비자도 처리한 이벤트를 식별하고 같은 업무를 반복하지 않도록 설계해야 합니다. 수신 기록과 업무 반영의 관계를 검토하고 외부 알림 같은 추가 부수 효과도 따로 추적합니다. 정확히 한 번 실행된다고 단정하기보다 실제로 보장하는 범위와 남은 불확실성을 설명합니다.
완료 표시의 의미도 정합니다. 메시지 브로커 접수와 최종 소비자의 업무 완료는 다를 수 있습니다. 어느 단계까지 확인한 상태인지 구분하고 필요한 경우 후속 결과를 별도로 연결합니다. 전달 완료라는 말 하나로 모든 처리의 성공을 표현하지 않습니다.
5. 순서와 현재 상태의 관계를 정한다
같은 주문의 생성과 취소 사건이 서로 다른 순서로 도착할 수 있는지 확인합니다. 전달기와 브로커, 소비자가 각각 어떤 순서 보장을 제공하는지 살펴봅니다. 전체 사건에 하나의 순서가 있다고 가정하기보다 업무 객체별로 필요한 일관성을 정합니다.
소비자는 오래된 사건이 최신 상태를 되돌리지 않게 상태 전이와 버전을 검사할 수 있어야 합니다. 필요한 정보가 부족하면 원본 조회나 보류 경로를 둡니다. 생성 시각만으로 모든 사건의 인과관계를 확정할 수 있다고 가정하지 않습니다.
재처리의 목적도 구분합니다. 데이터를 다시 구성하려는 작업에서 고객 알림을 과거처럼 다시 보내면 문제가 생길 수 있습니다. 상태 재구성과 외부 행동의 허용 범위를 따로 정하고 운영자가 재처리 대상과 결과를 확인할 수 있게 합니다.
6. 재시도와 확인 대기 정책을 만든다
일시적인 연결 실패와 인증 문제, 잘못된 메시지 형식을 구분합니다. 시간이 지나면 해결될 가능성이 있는 오류에는 간격과 횟수를 정하고, 같은 입력으로 반복되는 문제는 확인 대기열로 보냅니다. 모든 실패를 계속 같은 속도로 재시도하지 않습니다.
대기 기록에는 마지막 오류와 시도 이력, 이벤트와 업무 대상, 다음 조치를 남깁니다. 운영자가 원문 전체를 복사하지 않아도 필요한 근거를 볼 수 있어야 합니다. 비밀 키나 인증 헤더가 오류 메시지와 함께 저장되지 않는지도 점검합니다.
수동 복구는 승인된 범위로 수행합니다. 메시지 내용을 바꾸는 정정과 동일 사건의 재전달을 구분하고 변경 이력을 남깁니다. 과거 성공한 외부 행동을 다시 만들지 않도록 결과를 대조하며, 확인이 어려운 작업은 책임자와 후속 절차를 지정합니다.

7. 장애 시험은 커밋 전후에서 수행한다
업무 저장과 아웃박스 기록 사이에 실패를 넣어 둘 다 반영되거나 둘 다 취소되는지 확인합니다. 트랜잭션 경계가 코드의 의도와 실제 저장 동작에서 일치해야 합니다. 오류가 발생했다는 로그보다 최종 업무와 사건 기록을 직접 대조합니다.
외부 접수 뒤 응답 유실, 전달기 중단과 동시 처리도 시험합니다. 같은 사건을 재전달했을 때 업무와 알림이 어떤 결과가 되는지 확인합니다. 정상 요청 하나가 성공한 결과만으로 중복 방지와 복구가 준비됐다고 결론 내리지 않습니다.
이벤트 버전 변경과 잘못된 메시지, 소비자 장기 중단을 포함합니다. 대기열이 쌓여도 오래된 사건과 확인 필요 작업을 찾을 수 있어야 합니다. 문제가 있는 사건 하나 때문에 뒤의 모든 전달이 막히는지와 그 판단이 업무 순서 요구에 맞는지도 검토합니다.
8. 적체와 미해결 업무를 운영 지표로 삼는다
전달 횟수와 성공 응답 외에 가장 오래된 대기 사건과 재시도, 확인 필요 상태를 봅니다. 특정 고객이나 사건 유형만 멈춰 있지 않은지 구분합니다. 외부 전달 실패가 실제 주문과 고객 안내에 어떤 영향을 주는지 연결해 우선순위를 정합니다.
정리 정책에는 재시도와 감사에 필요한 기록의 보존을 포함합니다. 보냈다는 이유만으로 근거를 즉시 지우거나 모든 내용을 무기한 보관하지 않습니다. 처리한 식별자의 보관 기간과 과거 사건이 다시 들어올 가능성을 함께 검토해 중복 판단의 범위를 설명합니다.
운영 문서에는 업무 커밋과 전달, 소비자 처리와 복구 책임을 연결합니다. 담당자가 바뀌어도 어느 단계가 끝났고 무엇을 다시 해야 하는지 알 수 있어야 합니다. 아웃박스의 가치는 메시지 테이블 하나보다 실패 구간을 명시하고 업무 결과를 일관되게 추적하는 전체 과정에서 나옵니다.
9. 알림 접수 뒤 전달기가 중단된 주문을 복구한다
주문과 알림 이벤트가 같은 트랜잭션으로 저장된 뒤 전달기가 외부 알림을 보냈다고 가정합니다. 외부는 접수했지만 전달기가 완료 상태를 저장하기 전에 중단되도록 시험합니다. 주문이 존재하고 사건이 대기로 남았다는 사실을 확인한 뒤 어떤 근거로 다음 전송을 결정할지 운영 절차와 대조합니다.
재시작한 전달기는 기존 사건 식별자를 유지하고 외부의 결과 조회나 동일 요청 키 정책을 사용합니다. 새로운 사건을 만들어 보내면 기존 중복 방지 범위를 벗어날 수 있으므로 동일 재시도와 내용 정정을 구분해야 합니다. 결과를 확인할 방법이 없다면 확인 필요로 남기고 담당자가 외부 처리 상태를 조사할 경로를 마련합니다.
같은 대기 사건을 여러 전달기가 동시에 읽는 상황을 추가합니다. 작업 점유와 복구 조건이 맞는지 확인하고 한 작업자가 오래 걸리는 동안 다른 작업자가 같은 외부 행동을 시작하지 않는지 살펴봅니다. 점유가 풀렸다는 사실만으로 외부 행동이 실패했다고 단정하지 않고 접수와 완료 사이의 불확실성을 다시 검토합니다.
그 사이 주문 취소 사건이 생겼다면 소비자의 상태 전이도 대조합니다. 오래된 생성 이벤트가 뒤늦게 도착해 취소 상태를 되돌리지 않는지 확인합니다. 상태 복구를 위해 과거 사건을 다시 읽을 때 고객 알림까지 반복되지 않도록 재처리 목적과 허용 행동을 명시합니다. 원래 업무와 전달 이력을 같은 식별 관계에서 찾을 수 있어야 합니다.
종료 판단은 대기 행을 모두 지웠는지가 아니라 주문과 알림 결과가 합의한 상태인지에 따릅니다. 확인이 끝난 건과 아직 불명확한 외부 행동을 구분하고 후자에는 책임자와 다음 조치를 남깁니다. 이 시험을 배포와 저장 구조 변경 뒤에도 반복할 자료로 보관하면 시스템이 실패 구간을 실제로 처리하는지 지속적으로 확인할 수 있습니다.
적용 전 체크리스트
- 업무 변경과 사건 기록이 같은 트랜잭션인가
- 사건 식별자·객체·유형·버전이 연결되는가
- 전달기 점유와 중단·응답 유실을 다루는가
- 소비자의 중복·순서·재처리 목적을 검증하는가
- 완료 의미와 미해결 작업·정리 정책이 있는가
참고 자료와 적용 범위
이중 쓰기와 중복 전달·멱등성의 배경은 공식 자료를 참고했습니다. 작업 점유·상태·복구와 도식은 응용 서비스 설계 제안입니다.
대표 이미지는 AI로 제작한 개념 시각화이며, 본문 도식과 함께 이해를 돕기 위한 자료입니다. 실제 고객의 설비·데이터·서비스 화면을 나타내지 않습니다.
#아웃박스 #트랜잭션 #이벤트처리 #멱등성 #중복방지 #비동기처리 #데이터일관성 #장애복구 #시스템연동 #네이처프랙탈
이 주제를 실제 시스템으로 옮기려면 웹 서비스 개발 살펴보기