msa

MSA — 선착순 예매 시스템

이 폴더 가이드

대규모 트래픽 티켓팅 시스템을 MSA로 학습하기 위한 사이드 프로젝트 정리. 주니어와 토론하기 위해 만든 노트.

파일 무엇을 다루나 언제 읽나
msa.md (이 파일) 전체 개요 + 진행 상황 + 다이어그램 시작할 때
<a href="/msa/msa-design.html">msa-design</a> 왜 이렇게 설계했나 (결정들) "왜 MSA?", "왜 CQRS?" 질문 받았을 때
<a href="/msa/cqrs.html">cqrs</a> CQRS 깊은 이야기 (단계 진화 + 우리 구현) command/query 분리의 의미 깊이 토론
<a href="/msa/kafka.html">kafka</a> Kafka 깊은 이야기 (직접 구현 포함) Kafka가 왜 필요하고 어떻게 동작하나 토론
<a href="/msa/discussion.html">discussion</a> 토론 프롬프트 모음 주니어가 답해보면 좋은 질문들

1. 우리가 만드는 것

선착순 예매(티켓팅) 시스템. 콘서트 티켓 예매 같은 도메인. 대규모 트래픽이 동시에 몰리고 (수만~수십만 동접), 좌석은 한정되어 있어 정확히 한 사람만 한 좌석을 가져가야 함 — 동시성 + throughput + 정확성 셋이 다 어려운 도메인.

학습 목표 두 가지:

  1. 인프라부터 직접 구현 — Redis, Kafka 같은 핵심 부품을 외부 매니지드 서비스 대신 Kotlin/Netty로 직접 짜본 뒤 그 위에 도메인을 얹는다. 동작 원리 체득.
  2. 점진적 진화 — 처음부터 다이어그램 통째로 구현하지 않는다. "동작하는 최소 → 측정 → 한계 발견 → 진화" 사이클.

2. 도착지 다이어그램 (참고만)

                  ┌────────────────────┐
                  │  Client (Browser)  │
                  │  polling 5s → 1s   │
                  └─────────┬──────────┘
                            │
                  ┌─────────▼──────────┐
                  │  API Gateway       │
                  │  URL routing       │
                  └─────────┬──────────┘
              ┌─────────────┼──────────────────┐
              │             │                  │
      Queue Server Group           Booking Server Group
      (자원 격리)                  (자원 격리)
              │                            │
      ┌───────▼───────┐           ┌────────▼───────┐
      │ Queue API × N │           │ Booking API × N│
      │ UUID·JWT·순번  │           │ 토큰검증·좌석·결제│
      └───────┬───────┘           └────────┬───────┘
              │                            │
      ┌───────▼──────────┐         ┌───────▼──────────┐
      │ 대기열 Redis      │         │ 잔여좌석 Redis    │
      │ ZSET [UUID:ts]   │         │ Seat Counter Lua │
      │ Active User Set  │         │ Seat Lock SETNX  │
      └──────────────────┘         └────────┬─────────┘
                                            │
                              ┌─────────────▼───────┐
                              │   Message Queue     │ ← Kafka
                              │   (events)          │
                              └─────────┬───────────┘
                                        │ consume
                                ┌───────▼───────┐
                                │   Worker      │
                                │   (MQ → DB)   │
                                └───────┬───────┘
                                        │ INSERT
                                ┌───────▼───────┐
                                │   RDB         │  ← Postgres
                                │  Primary/Replica
                                └───────────────┘

이건 최종 도착지. 우리는 한 부품씩 만들어가며 "왜 이게 거기 있는가"를 직접 체감하는 게 목적. 자세한 설계 의도는 msa-design.


3. 핵심 패턴 한 줄씩

패턴 어디 적용 본질
CQRS command service / query service 분리 읽기/쓰기 스케일링 곡선이 다르니 모델·서비스도 따로
Bulkhead Queue tier ↔ Booking tier 자원 격리 한 쪽이 무너져도 다른 쪽이 살아있게
Polling 가변 주기 Client 5s → 1s 순번 가까울수록 빨라짐. 서버 부담 vs UX 균형
Sorted Set 순번 관리 대기열 Redis (ZADD/ZRANK/ZPOPMIN) "순서"를 데이터 구조 자체에 위임
분산락 (SETNX) + Lua atomic 잔여좌석 Redis DB락 대신 인메모리, 처리량 ↑
MQ 비동기 영속화 Booking API → Kafka → Worker → DB API 응답 빠르게, DB 부담 완충
DB Primary/Replica command writes Primary, query reads Replica 조회 부하 분산. replica lag 트레이드오프

각 패턴이 왜 거기 있는지는 msa-design, kafka 에서.


4. 지금까지 진행 상황 (2026-05-26)

완성

미구현 (예정)

다음 한 걸음

부하 테스트 §3 (cold/warm 분리) 또는 시나리오 C (점진 증가). 그 결과로 Worker 도입 트리거를 직접 확인하기.


5. 컴포넌트 매핑표

다이어그램 부품 우리 구현 상태
API Gateway gateway/ 뼈대만
Queue API (없음) 미구현
Booking API ticket-command-service DONE ★
(다이어그램 외) Read service ticket-query-service DONE ★
Scheduler (없음) 미구현
대기열 Redis MyRedis 공용 ZSET/SETNX/Lua 지원 확인 필요
잔여좌석 Redis MyRedis 공용 위와 동일
MQ MyKafka MVP DONE, client SDK 없음
Worker (없음) 미구현
RDB Postgres ticket_db 단일 (replica 미분리)

6. 진화 순서 (현재 위치 표시)

[1단계 · 현재] command 동기 DB 쓰기 (비관락)
    ↓ 부하 테스트로 한계 측정 ← 지금 여기
[2단계] command → MyKafka publish → Worker → DB INSERT (비동기)
    ↓ "API 폭주에도 안 죽음" 학습
[3단계] DB Primary/Replica → query는 replica
    ↓ "조회 부하 분산 + replica lag" 학습
[4단계] Queue API + Scheduler 도입 (대기열 throttling)
    ↓ "예매 진입 자체를 제어" 학습
[5단계] Gateway 완성 (JWT 검증 + 라우팅)

각 단계마다 discussion 의 "X 단계 토론 질문" 참조.


7. 메타 학습 원칙 (의식적으로 따르는 것)

이 프로젝트 내내 따르는 규칙. 주니어와 공유하면 좋음:

  1. 학습은 한 번에 한 변수. 새 개념과 새 도구를 동시에 도입하지 않는다.
  2. 동작하는 최소 → 측정 → 한계 → 진화. 다이어그램 그대로 베끼지 않는다.
  3. 공유 모델로 묶지 않는다 (지금은). CQRS의 자유도 보존.
  4. 권한을 미리 잠가둔다. 미래 분리가 쉬워지는 방향.
  5. 인프라 설치는 사용자가 직접. 도구 자동화는 안내까지만.

이 원칙들이 실제로 어떻게 깨지고 학습 거리가 되는지: discussion §5.