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 + 정확성 셋이 다 어려운 도메인.
학습 목표 두 가지:
- 인프라부터 직접 구현 — Redis, Kafka 같은 핵심 부품을 외부 매니지드 서비스 대신 Kotlin/Netty로 직접 짜본 뒤 그 위에 도메인을 얹는다. 동작 원리 체득.
- 점진적 진화 — 처음부터 다이어그램 통째로 구현하지 않는다. "동작하는 최소 → 측정 → 한계 발견 → 진화" 사이클.
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)
완성
- MyRedis — Netty + RESP 직접 구현. 캐시/세션 동작.
- MyKafka MVP — 단일 브로커, PRODUCE/FETCH/CREATE_TOPIC/COMMIT_OFFSET/FETCH_OFFSET 5개 ApiKey, sparse offset index, segment rolling, batch all-or-none. 클라이언트 SDK는 아직 없음.
- auth-service — Spring Boot 4 + JWT + Postgres + MyRedis token store.
- frontend — Next.js, queue/seats/payment/confirmation 페이지.
- ticket-command-service ★이번 세션 — port 8082, 예약 생성/확정/취소. 비관락 + @Version.
- ticket-query-service ★이번 세션 — port 8083, 읽기 전용. hikari
read-only: true+@Transactional(readOnly = true). validate 모드. - mock-data.sql — Event 1 / Section 3 / Seat 292 시드. 멱등.
- 부하 테스트 시나리오 A, B — k6 작성. baseline 측정.
미구현 (예정)
- gateway (뼈대만)
- Queue API (대기열 진입)
- Scheduler (1초마다 ZPOPMIN 100)
- Worker (MQ → DB INSERT)
- MyKafka client SDK
- DB Primary/Replica 분리
다음 한 걸음
부하 테스트 §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. 메타 학습 원칙 (의식적으로 따르는 것)
이 프로젝트 내내 따르는 규칙. 주니어와 공유하면 좋음:
- 학습은 한 번에 한 변수. 새 개념과 새 도구를 동시에 도입하지 않는다.
- 동작하는 최소 → 측정 → 한계 → 진화. 다이어그램 그대로 베끼지 않는다.
- 공유 모델로 묶지 않는다 (지금은). CQRS의 자유도 보존.
- 권한을 미리 잠가둔다. 미래 분리가 쉬워지는 방향.
- 인프라 설치는 사용자가 직접. 도구 자동화는 안내까지만.
이 원칙들이 실제로 어떻게 깨지고 학습 거리가 되는지: discussion §5.