목차
서비스를 쪼개고 큐와 발행/구독으로 이으면 결합이 느슨해진다. 대신 새 문제가 딸려 온다. 이 시리즈는 그 문제마다 붙은 패턴의 이름을 다룬다. 먼저 지도를 편다.
쪼개고 이었더니 새 문제가 생겼다
한 프로세스 안에서 함수를 부르면 셋이 공짜였다. 호출은 즉시 돌아오고, 실패하면 그 자리에서 알고, 하나의 트랜잭션이 전부를 묶었다.
서비스를 쪼개 메시지로 이으면 이 셋이 다 깨진다. 응답은 나중에 오고, 실패도 나중에 오고, 트랜잭션은 경계에서 끊긴다. 이게 message-queue 글과 microservices 글이 “대가”로 나열한 것들이다. 이 시리즈는 그 대가를 감당하는 방법이다.
문제 하나: 같은 메시지가 두 번 온다
메시지 시스템은 대개 “적어도 한 번은 전달한다”를 보장한다. 확실히 전달하려다 보니 애매할 때 다시 보낸다. 그래서 같은 메시지가 두 번 도착한다.
받는 쪽이 그대로 두 번 처리하면 결제가 두 번 되고 재고가 두 번 빠진다. 해법의 이름은 멱등 소비자 - 두 번 와도 한 번과 같게 삼킨다. 멱등 소비자 글에서 다룬다.
문제 둘: 저장은 됐는데 발행이 안 됐다
주문을 DB에 저장하고, 이어서 “주문 생성됨” 이벤트를 발행한다. 그런데 저장 직후 프로세스가 죽으면? DB엔 주문이 있는데 이벤트는 안 나갔다. 반대 순서면 이벤트는 나갔는데 주문이 없다.
이 넣었는데 안 넣어진 경우를 두 저장소에 나눠 쓰는 문제(dual-write)라 부른다. 해법의 이름은 아웃박스 - 저장과 발행을 한 트랜잭션으로 묶는다. 아웃박스 글에서 다룬다.
문제 셋: 하나의 일이 여러 서비스에 걸친다
“주문한다”는 한 줄이 결제·재고·배송 세 서비스를 건드린다. 한 트랜잭션으로 묶고 싶지만, 데이터가 갈려 있어 그럴 수 없다. 결제는 됐는데 재고가 없으면 결제를 되돌려야 한다.
이 어긋난 걸 누가 되돌리나의 이름이 사가(Saga) - 여러 로컬 트랜잭션을 잇고, 실패하면 보상으로 되돌린다. 잇는 방식이 둘이라 코레오그래피와 오케스트레이션 두 글로 나눠 다룬다.
문제 넷: 조회가 점점 복잡해진다
쓸 때 좋은 데이터 모양과 읽을 때 좋은 모양은 다르다. 쓰기는 정규화된 작은 덩어리가 편하고, 조회는 여러 곳을 합친 넓은 화면이 편하다. 한 모델로 둘 다 하려면 어느 한쪽이 고생한다.
해법의 이름은 CQRS - 쓰는 모델과 읽는 모델을 아예 가른다. CQRS 글에서 다룬다.
문제 다섯: 어쩌다 그 상태가 됐는지가 없다
DB엔 보통 지금 상태만 남는다. 잔액이 5만원인 건 알아도, 그게 어떤 입출금을 거쳐 왔는지는 덮어써져 사라진다. 감사·추적·되감기가 필요하면 곤란하다.
해법의 이름은 이벤트 소싱 - 상태 대신 일어난 일의 목록을 진실로 저장하고, 필요하면 재생해 상태를 얻는다. 이벤트 소싱 글에서 다룬다.
이 패턴들은 공짜가 아니다
지도를 펴 보면 공통점이 하나 보인다. 이 패턴들은 대부분 “지금 당장 완벽히 일치”를 포기하고 “잠깐 어긋났다가 곧 맞춤”을 받아들인다. 아웃박스의 이벤트도 릴레이가 보낼 때까지 늦고, 사가도 보상이 돌 때까지 어긋나 있고, CQRS의 읽기 모델도 잠깐 옛 값을 보여준다.
이 “잠깐 어긋남”이 바로 결과적 정합성이다. 그래서 이 시리즈의 밑에는 consistency 글이 늘 깔려 있다 - 이벤트 패턴은 결과적 정합성을 실제로 구현하는 방법이라고 봐도 된다.
그래서 언제 꺼내나
이름을 안다고 다 써야 하는 건 아니다. 이 패턴들은 비동기로 이은 시스템의 문제를 푼다. 한 서비스, 한 DB로 충분하면 트랜잭션 하나가 위의 다섯 문제를 전부 안 생기게 한다.
- 아직 안 쪼갰다면 아웃박스·사가는 필요 없다. 로컬 트랜잭션이 다 해준다.
- 쪼갰지만 조회가 단순하면 CQRS·이벤트 소싱은 과하다.
- 문제가 실제로 나타났을 때 해당 패턴을 꺼낸다. 이름을 알아두는 값은 “그 문제엔 이미 답이 있다”를 아는 것이다.
정리
- 쪼개고 비동기로 이으면 즉시·그 자리·한 트랜잭션이라는 세 공짜가 깨진다.
- 그 자리마다 패턴이 있다 - 중복엔 멱등 소비자, 발행 누락엔 아웃박스, 분산 트랜잭션엔 사가, 복잡한 조회엔 CQRS, 이력엔 이벤트 소싱.
- 공통 밑바닥은 결과적 정합성 - “잠깐 어긋났다 곧 맞춤”을 받아들이는 것이다.
- 이름을 아는 것과 쓰는 것은 다르다. 문제가 실제로 나타났을 때 꺼낸다.
다음 글은 첫 번째 벽 - 같은 메시지가 두 번 와도 한 번처럼 삼키는 멱등 소비자로 간다.