서비스 사이를 잇는 법, 이벤트 버스와 워크플로우
서비스를 비동기로 잇는 두 도구(NATS 이벤트 버스, Temporal 워크플로우)를 살펴보고, 메시지 보관 기간을 잘못 잡아 데인 사고를 정리합니다.
지난 편까지 서비스를 나누고, 거기에 로그인을 붙였습니다. 이번에는 나뉜 서비스들이 서로 어떻게 이야기를 주고받는지를 다뤄 보겠습니다.
직접 부르면 함께 넘어집니다
서비스 A가 일을 끝낸 뒤 B에게 알려야 한다고 해봅시다. 가장 단순한 방법은 A가 B를 직접 호출하는 것입니다. 하지만 직접 호출에는 함정이 있습니다.
전화에 비유하면 이렇습니다. A가 B에게 전화를 걸면, B가 받을 때까지 A는 수화기를 들고 기다려야 합니다. B가 바쁘거나 잠시 죽어 있으면, A도 함께 멈춥니다. 한 서비스의 장애가 전화선을 타고 옆으로 번지는 것이지요.
게다가 "결제가 끝났다"는 사실 하나에 관심 있는 서비스가 여럿일 수 있습니다. 알림도 보내야 하고, 통계도 쌓아야 하고, 영수증도 발급해야 합니다. 그때마다 A가 세 곳에 전화를 돌리는 것은 번거롭고, 한 곳이라도 안 받으면 A가 멈춥니다.
사실은 게시판에 붙입니다 (이벤트 버스, NATS)
그래서 직접 전화 대신 게시판을 두었습니다. A는 "결제가 끝났다"를 게시판에 붙이기만 합니다. 누가 보는지는 신경 쓰지 않습니 다. 관심 있는 서비스가 알아서 와서 읽습니다.

이 게시판 역할을 하는 것이 이벤트 버스(NATS)입니다. 핵심은 발행하는 쪽은 수신자를 모른다는 점입니다.
- A는 게시판에 사실을 붙이고 자기 일을 계속합니다. B가 죽어 있어도 A는 멈추지 않습니다.
- 알림·통계·영수증 서비스가 각자 필요한 글만 골라 읽습니다.
- 수신자가 새로 늘어도 A의 코드는 바뀌지 않습니다. 게시판만 보면 되니까요.
서로 모르기 때문에 서로 묶이지 않습니다. 이것이 비동기로 잇는 첫 번째 도구입니다.
여러 단계는 절차로 묶습니다 (워크플로우, Temporal)
이벤트만으로 충분하지 않은 경우가 있습니다. 여러 단계를 거치는데, 중간에 실패하면 앞 단계를 되돌려야 하는 작업입니다.
예를 들어 "주문 → 결제 → 재고 차감 → 배송 등록"을 생각해 봅시다. 결제는 됐는데 재고 차감에서 실패하면, 결제를 되돌려(환불) 줘야 합니다. 이 되돌리기(보상)를 손으로 짜다 보면, 꼭 한 단계를 빠뜨립니다. "어디까지 진행됐더 라"를 코드 곳곳에서 추적해야 하기 때문입니다.
워크플로우 엔진(Temporal)은 이 절차를 하나의 흐름으로 묶어 줍니다. 택배 송장에 비유할 수 있습니다. 송장에는 단계가 적혀 있고, 어디까지 왔는지가 항상 기록됩니다. 중간에 멈춰도 송장을 보면 다음에 무엇을 할지, 되돌린다면 어디까지 되돌릴지 알 수 있습니다.
- 각 단계의 진행 상태를 엔진이 기억합니다. 서버가 재시작해도 멈춘 지점부터 이어집니다.
- 실패 시 되돌리는 단계(보상)를 흐름 안에 명시적으로 둡니다.
- "어디까지 됐더라"를 코드가 아니라 엔진이 추적합니다.
정리하면, 알리기만 하면 되는 일은 이벤트로, 여러 단계를 책임지고 끝내야 하는 일은 워크플로우로 나눠 맡겼습니다.
크게 데인 사고, 우체통을 매일 비웠다
이벤트 버스에는 한 가지 설정이 있습니다. 게시판에 붙은 글을 얼마 동안 보관할지(보관 기간, retention)입니다. 모든 글을 영원히 둘 수는 없으니, 일정 기간이 지나면 지웁니다.
한 분석용 스트림에서 이 보관 기간을 하루로 잡아 두었습니다. 그런데 그 글을 모아 가는 집계 작업은 하루보다 더 긴 주기로 돌고 있었습니다.
