티켓팅 시스템. 동시 100명 예매에서 초과판매 0건을 보장합니다. Redis Lua Script로 재고를 원자적으로 제어하고, Kafka 비동기 처리로 DB 부하를 분산합니다.
| 기능 | 설명 |
|---|---|
| 회원가입 / 로그인 | JWT (Access 30분 + Refresh 7일) |
| 공연 목록 / 검색 | Elasticsearch nori 형태소 분석 한국어 검색 |
| 공연 상세 | 이벤트 조회 + 좌석 선택 |
| 실시간 재고 표시 | SSE로 잔여석 즉시 반영 |
| 좌석 예매 | Redis Lua Script 원자 감소 → Kafka 비동기 DB 저장 (202) |
| 예매 상태 폴링 | 202 후 자동 폴링 → 완료/실패 토스트 알림 |
| 예매 취소 | DB 상태 변경 + Redis 재고 복구 |
| 영역 | 스택 |
|---|---|
| Backend | Java 21, Spring Boot 3.x, Gradle 멀티모듈 |
| Database | MySQL 8.x, Redis 7.2 |
| Search | Elasticsearch 8.x, nori 한국어 분석기 |
| Messaging | Apache Kafka (Confluent 7.5) |
| Security | Spring Security, JWT |
| Frontend | Next.js 14 (App Router), TypeScript, Tailwind CSS |
| Infra | Docker Compose |
┌─────────────────────────────────────────────────┐
│ Next.js 14 (Frontend) │
└──────────────────────┬──────────┬───────────────┘
│ HTTP │ SSE
│ /REST │ (실시간 재고)
┌──────────────────────▼──────────▼───────────────┐
│ Spring Boot API (api module) │
│ Auth · Event · Reservation · SSE Controller │
└──────┬──────────────────┬────────────┬──────────┘
│ JPA │ Lua Script │ Kafka Produce
┌────▼─────┐ ┌──────▼──────┐ ┌─▼────────────────┐
│ MySQL 8 │ │ Redis 7.2 │ │ Kafka Consumer │
│ 영속 데이터 │ │ 재고 관리 │ │ DB 비동기 저장 │
└──────────┘ └─────────────┘ └──────────────────┘
│ Kafka (event-created)
┌────▼──────────────┐
│ Elasticsearch │
│ 한국어 검색 인덱스 │
└───────────────────┘
backend/
├── api (bootJar=true)
│ ├── depends → core-domain
│ ├── depends → core-infra-redis
│ ├── depends → core-infra-kafka
│ ├── depends → core-infra-security
│ └── depends → core-infra-elasticsearch
│
└── core/
├── core-domain Entity, Repository interface
│ └── (외부 의존 없음 — 순수 도메인)
├── core-infra-redis Redis Lua Script 재고 처리
│ └── depends → core-domain
├── core-infra-kafka Kafka Producer / Consumer
│ └── depends → core-domain
├── core-infra-security JWT, Spring Security
│ └── depends → core-domain
└── core-infra-elasticsearch Elasticsearch 문서/리포지토리
└── depends → core-domain
의존 방향: api → core-infra-* → core-domain (역방향 금지)
사전 준비: Docker Desktop, Java 21, Node.js 20+
# 1. 인프라 (MySQL + Redis + Kafka + Elasticsearch)
docker compose up -d
# 2. 백엔드
cd backend && ./gradlew :api:bootRun
# → http://localhost:8080
# → http://localhost:8080/swagger-ui/index.html
# 3. 프론트엔드
cd frontend && npm install && npm run dev
# → http://localhost:3000프론트엔드 .env.local 생성:
NEXT_PUBLIC_API_URL=http://localhost:8080
NEXT_PUBLIC_SSE_URL=http://localhost:8080cd backend && ./gradlew :api:test| 레이어 | 도구 | 케이스 |
|---|---|---|
| Service | JUnit5 + Mockito | 예매·취소·상태조회·검색 비즈니스 로직 |
| Repository | @DataJpaTest | JPA 쿼리 검증 |
| Redis | @EmbeddedRedis | Lua Script 원자적 재고 감소 |
| Kafka | @EmbeddedKafka | 예매 이벤트 Consumer / ES 동기화 Consumer |
| Controller | MockMvc | 전 엔드포인트 E2E |
목표: 동시 100명 예매에서 초과판매 0건
# JWT 토큰 발급 후 실행
k6 run -e TOKEN=<jwt> -e SEAT_ID=1 load-test/reservation.js| 시나리오 | VUs | 시간 | 검증 기준 |
|---|---|---|---|
| 동시 예매 | 100 | 10s | 202 + 409만 허용, 5xx = 0 |
결과 해석:
reservations_accepted: 예매 성공 (202) 건수 → DB 실제 저장 건수와 일치해야 함reservations_sold_out: 재고 소진 (409) 건수- 합계 = accepted + sold_out ≤ 총 좌석 수 → 초과판매 0건 보장
Elasticsearch 기동 후 수동 확인:
# 1. 공연 데이터 생성 (POST /events)
# 2. Kafka를 통해 ES 인덱싱 확인 (2초 이내)
curl http://localhost:9200/events/_search?q=콘서트
# 3. 검색 API 응답 시간 확인
curl -w "\n응답시간: %{time_total}s\n" \
"http://localhost:8080/events?q=콘서트"
# 목표: 200ms 이하| Method | Path | 설명 | 인증 |
|---|---|---|---|
| POST | /auth/signup |
회원가입 | - |
| POST | /auth/login |
로그인 (JWT 발급) | - |
| GET | /events |
공연 목록 (페이징) | - |
| GET | /events?q={keyword} |
공연 한국어 검색 (ES) | - |
| GET | /events/{id} |
공연 상세 + 좌석 | - |
| POST | /reservations/{seatId} |
좌석 예매 (202) | Bearer |
| GET | /reservations/{id}/status |
예매 상태 조회 (폴링용) | Bearer |
| GET | /reservations/my |
내 예매 목록 | Bearer |
| DELETE | /reservations/{id} |
예매 취소 | Bearer |
| GET | /sse/inventory/{eventId} |
실시간 재고 구독 | - |