27강
발행과 구독
한 번 일어난 일에 관심 있는 곳이 여럿일 때, 알리는 쪽이 받는 쪽을 몰라도 되게 만든다
먼저 보면 좋은 강의: 메시지 큐
단계별로 보기
스페이스바로 재생/정지, 좌우 화살표로 단계 이동, R로 처음으로 돌아갑니다.
대기 중1 / 4 단계 — 처음에는 듣는 곳이 하나였다
주문이 들어오면 확인 메일을 보냅니다. 관심 있는 곳이 한 군데뿐이라면 직접 호출하든 발행하든 큰 차이가 없어 보입니다. 이 단계에서 구조를 고민하는 사람은 거의 없습니다. 문제는 두 번째 관심사가 생길 때 드러납니다.
이 강의를 마치면
- '명령을 내리는 것'과 '사실을 알리는 것'의 차이를 설명한다.
- 발행자가 구독자를 모르는 구조가 왜 변경 비용을 낮추는지 설명한다.
- 구독자 하나가 고장 나도 다른 구독자가 영향을 받지 않는 이유를 설명한다.
- 구독자를 추가할 때 발행자를 건드리지 않고 확장한다.
주문 하나에 관심 있는 사람들
주문이 하나 들어왔습니다. 이 사실에 관심 있는 곳이 몇 군데나 될까요?
- 메일 담당: 주문 확인 메일을 보내야 합니다.
- 창고 담당: 물건을 꺼내 포장해야 합니다.
- 통계 담당: 오늘 매출에 더해야 합니다.
주문 서버는 이 세 곳을 어떻게 움직여야 할까요?
방법 1 — 일일이 전화를 돌린다
주문 서버가 메일 서비스를 호출하고, 창고 서비스를 호출하고, 통계 서비스를 호출합니다. 단순하고 명확합니다. 그런데 여기에 두 가지 병이 숨어 있습니다.
첫째, 새 관심사가 생길 때마다 주문 서버를 고쳐야 합니다. 내일 마케팅팀이 "주문할 때마다 쿠폰을 주고 싶다"고 하면 주문 서버 코드를 열어 호출을 하나 더 넣습니다. 모레 고객센터가 또 요청합니다. 주문과 아무 상관없는 이유로 주문 서버가 계속 바뀝니다. 가장 중요한 코드가 가장 자주 흔들립니다.
둘째, 남의 사정이 내 사정이 됩니다. 통계 서버가 죽어 있으면 호출이 실패합니다. 그러면 주문도 실패시켜야 할까요? 통계가 안 밀렸다고 주문을 취소하는 건 말이 안 됩니다. 하지만 직접 호출 구조에서는 그 실패가 자연스럽게 위로 번집니다.
방법 2 — 관보에 싣는다
주문 서버는 딱 한 가지만 합니다. "주문이 발생했다"고 알립니다. 끝입니다. 누가 그 소식을 듣는지, 몇 곳이나 듣는지, 듣고 뭘 하는지 주문 서버는 모릅니다. 알 필요도 없습니다.
관심 있는 쪽이 각자 구독합니다. 메일 담당이 읽고, 창고 담당이 읽고, 통계 담당이 읽습니다.
방법 1: 주문 서버 ──호출──> 메일
──호출──> 창고 (주문 서버가 셋을 다 안다)
──호출──> 통계
방법 2: 주문 서버 ──발행──> [주문 발생]
├── 메일이 구독
├── 창고가 구독 (주문 서버는 아무도 모른다)
└── 통계가 구독말투가 바뀐다
이 구조에서 가장 중요한 변화는 그림이 아니라 말투입니다.
| 말투 | 뜻 | |
|---|---|---|
| 명령 | "메일을 보내라" | 상대가 누구인지, 뭘 해야 하는지 내가 안다 |
| 사실 | "주문이 발생했다" | 일어난 일만 말한다. 반응은 듣는 쪽 몫이다 |
발행-구독은 명령이 아니라 사실을 흘려보냅니다. 사실은 누가 듣든 변하지 않으므로, 듣는 쪽이 늘어나도 말하는 쪽은 그대로입니다.
그래서 무엇이 좋아지나
새 구독자를 공짜로 붙일 수 있습니다. 마케팅팀이 쿠폰을 주고 싶다면 자기 구독을 하나 만들면 됩니다. 주문 서버는 한 글자도 바뀌지 않습니다. 담당 조직이 서로 다를 때 이건 코드 문제가 아니라 일정 문제입니다. 남의 팀 배포를 기다리지 않아도 됩니다.
한 곳의 고장이 갇힙니다. 통계 담당이 죽으면 통계 큐에만 일이 쌓입니다. 메일은 정상 발송되고, 창고는 정상 출고되고, 주문은 정상 접수됩니다. 통계 담당이 살아나면 쌓인 것을 읽어 따라잡습니다. 고장이 번지지 않고 한 칸에 머무는 것, 이게 이 구조의 진짜 값어치입니다.
대가도 분명하다
공짜는 아닙니다. 주문 서버 로그만 봐서는 메일이 실제로 갔는지 알 수 없습니다. "발행했다"까지만 압니다. 문제가 생기면 여러 구독자의 상태를 각각 따라가야 합니다. 전체를 한눈에 보기 어려워지는 것, 이것이 느슨하게 묶은 대가입니다.
이 강의에서는 토픽 하나에 구독자마다 큐를 하나씩 매달아 발행-구독을 표현합니다. 토픽이 사본을 만들어 모두에게 보내고, 큐가 구독자별로 나뉘어 있어야 한 구독자가 밀려도 다른 구독자가 멀쩡합니다.
이 강의가 단순화한 것
교육용 모형은 언제나 무언가를 생략합니다. 무엇을 생략했는지 아는 것도 학습의 일부입니다.
전제한 것
- 토픽은 실린 사건을 구독마다 사본으로 복제해 보낸다. 배달 순서는 구독을 연결한 순서로 고정되어 있다. 실제 제품에서는 구독자 간 배달 순서를 보장하지 않는 경우가 많다.
- 토픽 자체는 사건을 보관하지 않는다. 보관은 구독마다 달린 큐가 한다. 실제 제품에서는 토픽이 사건을 일정 기간 보관해 나중에 붙은 구독자가 과거를 읽어가게 하기도 한다.
- 발행은 구독자의 처리를 기다리지 않고 곧바로 끝난다. 그래서 구독자 쪽에서 일어난 실패는 사용자 지표에 잡히지 않는다 — 이것은 단순화가 아니라 이 구조가 실제로 치르는 대가다.
- 발행이 성공하면 구독자에게 언젠가 도달한다고 가정한다. 실제로는 발행 자체가 실패할 수 있어 별도의 보장 장치가 필요하다.
다루지 않은 것
- 토픽·파티션·구독 그룹의 구체적 모델
- 메시지 순서 보장과 구독자 간 순서 차이
- 구독자별 재처리(replay)와 오프셋 관리
- 이벤트 스키마 변경과 하위 호환성
더 읽을거리: Enterprise Integration Patterns — Publish-Subscribe Channel
이제 직접 만들어 보세요
읽어서 아는 것과 만들어서 아는 것은 다릅니다. 컴포넌트를 배치하고 트래픽을 흘려서 실제로 동작하는지 확인해 보세요.
이 강의에 물어보기
하루 5번이 강의 본문만 근거로 답합니다. 강의에 없는 내용은 지어내지 않습니다.