|
1 | | -# WEB7_9_B2ST_BE |
2 | | -> **TT(Ticket & Trade)** — 공연 예매(대기열/좌석 선점/추첨·신청 예매)와 교환·양도(거래), 결제 흐름을 지원하는 백엔드 서버입니다. |
| 1 | +# TT (Ticket & Trade) - Backend |
3 | 2 |
|
4 | | -## 🎯 프로젝트 소개 |
5 | | -<img width="1408" height="633" alt="image" src="https://github.com/user-attachments/assets/23181df2-a498-4e96-9f33-7a71cb2848fc" /> |
| 3 | +> 공연 예매(대기열/좌석 선점/추첨·신청 예매)와 티켓 거래(교환/양도), 결제 흐름을 지원하는 Spring Boot 기반 백엔드 서버 |
6 | 4 |
|
7 | | -(작성) |
| 5 | +<p align="center"> |
| 6 | + <img src="https://img.shields.io/badge/Java-21-ED8B00?logo=openjdk&logoColor=white" alt="Java 21"/> |
| 7 | + <img src="https://img.shields.io/badge/Spring%20Boot-4.0.0-6DB33F?logo=springboot&logoColor=white" alt="Spring Boot 4.0.0"/> |
| 8 | + <img src="https://img.shields.io/badge/PostgreSQL-16-4169E1?logo=postgresql&logoColor=white" alt="PostgreSQL"/> |
| 9 | + <img src="https://img.shields.io/badge/Redis%20Cluster-7-DC382D?logo=redis&logoColor=white" alt="Redis Cluster"/> |
| 10 | + <img src="https://img.shields.io/badge/Docker-Compose-2496ED?logo=docker&logoColor=white" alt="Docker"/> |
| 11 | +</p> |
8 | 12 |
|
9 | | -## 👥 팀 구성 & 링크 |
10 | | -- 팀 구성: (작성) |
11 | | -- 배포 링크: https://doncrytt.vercel.app/ |
12 | | -- Frontend Github: https://github.com/prgrms-web-devcourse-final-project/WEB7_9_B2ST_FE |
13 | | -- Backend Github: https://github.com/prgrms-web-devcourse-final-project/WEB7_9_B2ST_BE |
| 13 | +## 📋 목차 |
14 | 14 |
|
| 15 | +- [프로젝트 개요](#-프로젝트-개요) |
| 16 | +- [팀 구성](#-팀-구성) |
| 17 | +- [링크](#-링크) |
| 18 | +- [핵심 기능](#️-핵심-기능) |
| 19 | +- [기술 스택](#️-기술-스택) |
| 20 | +- [시스템 아키텍처](#️-시스템-아키텍처) |
| 21 | +- [ERD](#️-erd) |
| 22 | +- [프로젝트 구조](#-프로젝트-구조) |
| 23 | +- [모니터링 구성](#-모니터링-구성) |
| 24 | +- [협업 규칙](#-협업-규칙) |
15 | 25 |
|
16 | | -## ⚙️ 핵심 기능 |
| 26 | +--- |
| 27 | + |
| 28 | +## 🎯 프로젝트 개요 |
| 29 | + |
| 30 | +**TT(Ticket & Trade)** 는 공연 티켓 예매 및 2차 거래(교환/양도) 플랫폼의 백엔드 서버입니다. |
17 | 31 |
|
18 | | -### 🎫 공연/회차 |
19 | | -- 공연 생성/조회(관리자/사용자 분리) |
20 | | -- 회차(공연 일정) 생성 및 조회 |
21 | | -- 좌석 등급/가격 정책 관리 |
| 32 | +### 개발 기간 |
22 | 33 |
|
23 | | -### ⏳ 대기열 |
24 | | -- 예매 트래픽 집중 상황에서 대기열 기반 접근 제어 |
25 | | -- Redis 기반 상태 관리(환경 설정으로 on/off 가능) |
| 34 | +- 2024.12.03 ~ 2025.01.12 |
26 | 35 |
|
27 | | -### 🪑 좌석 선점/예매 |
28 | | -- 좌석 선점(HOLD) → 예매 확정(SOLD) 흐름 |
29 | | -- 선점 만료/실패 처리로 좌석 상태 복구 |
| 36 | +### 주요 도메인 |
30 | 37 |
|
31 | | -### 🎲 추첨 예매 |
32 | | -- 추첨 응모 생성 및 내 응모 내역 조회 |
33 | | -- 추첨 결과 처리 및 좌석 배정 흐름 지원 |
| 38 | +| 도메인 | 설명 | |
| 39 | +|:------------|:---------------------------------------| |
| 40 | +| **대기열** | Redis 기반 트래픽 분산, 순차 접근 제어 | |
| 41 | +| **좌석 예매** | 좌석 선점(HOLD → SOLD), 만료 자동 복구 | |
| 42 | +| **추첨 예매** | 등급별 응모, 공정 추첨, 당첨 알림 | |
| 43 | +| **사전신청 예매** | 오픈/마감 정책 기반 신청 처리 | |
| 44 | +| **거래** | 티켓 소유권 검증, 교환·양도 흐름 | |
| 45 | +| **결제** | 도메인별 결제 흐름 분리, 상태 관리 | |
| 46 | +| **인증** | JWT + Refresh Token Rotation, 카카오 OIDC | |
34 | 47 |
|
35 | | -### 📝 신청 예매(사전신청) |
36 | | -- 신청 예매 오픈/마감 정책 |
37 | | -- 신청/예약 생성 및 만료 처리 |
| 48 | +--- |
38 | 49 |
|
39 | | -### 💳 결제 |
40 | | -- 도메인별 결제 흐름 분리(예매/신청/추첨/거래) |
41 | | -- 결제 상태 관리 및 후처리 구조 |
| 50 | +## 👥 팀 구성 |
42 | 51 |
|
43 | | -### 🔁 교환·양도(거래) |
44 | | -- 거래 등록/조회 |
45 | | -- 거래 요청/승인 흐름 |
46 | | -- 티켓 상태와 소유권 검증 |
| 52 | +| 이름 | 역할 | GitHub | |
| 53 | +|:--------------:|:-------:|:----------------------------------------------------------------------------------------------------------------------------------:| |
| 54 | +| whyin | Backend | [](https://github.com/whyin) | |
| 55 | +| Chehyeon-Kim23 | Backend | [](https://github.com/Chehyeon-Kim23) | |
| 56 | +| Nomi | Backend | [](https://github.com/77r77r) | |
| 57 | +| Minhyung Park | Backend | [](https://github.com/minibr) | |
| 58 | +| WeeRim | Backend | [](https://github.com/weilim0513-tech) | |
47 | 59 |
|
48 | | -### 🧑💼 관리자 |
49 | | -- 관리자 전용 API(`/api/admin/**`) 권한 분리 |
50 | | -- 공연/예매/거래 운영 지원 기능 |
| 60 | +--- |
51 | 61 |
|
| 62 | +## 🔗 링크 |
| 63 | + |
| 64 | +| 구분 | 링크 | |
| 65 | +|:-----------:|:--------------------------------------------------------------------------------------:| |
| 66 | +| 🌐 배포 | [https://doncrytt.vercel.app](https://doncrytt.vercel.app/) | |
| 67 | +| 💻 Frontend | [WEB7_9_B2ST_FE](https://github.com/prgrms-web-devcourse-final-project/WEB7_9_B2ST_FE) | |
| 68 | +| 🔧 Backend | [WEB7_9_B2ST_BE](https://github.com/prgrms-web-devcourse-final-project/WEB7_9_B2ST_BE) | |
| 69 | +| 📖 API 문서 | [Swagger UI](http://15.165.115.135:8080/swagger-ui/index.html#/) | |
| 70 | + |
| 71 | +--- |
| 72 | + |
| 73 | +## ⚙️ 핵심 기능 |
| 74 | + |
| 75 | +### 🔐 인증/인가 (Auth) |
| 76 | + |
| 77 | +| 기능 | 구현 상세 | |
| 78 | +|:---------------|:-----------------------------------------------------------------------| |
| 79 | +| JWT 인증 | Access Token(30분) + Refresh Token(7일, Redis 저장) | |
| 80 | +| Token Rotation | 재발급 시 Family/Generation 기반 탈취 감지, 이전 토큰 사용 시 전체 세션 무효화 | |
| 81 | +| 카카오 OIDC | ID Token RSA 서명 검증(JWKS 24시간 캐싱), nonce 1회성 검증, 자동 계정 연동 | |
| 82 | +| 로그인 보안 | 5회 실패 시 10분 잠금(Redis TTL), Lua Script 원자적 카운팅 | |
| 83 | +| 위협 탐지 | Credential Stuffing(IP당 10+ 계정), Brute Force(IP당 50+ 실패) 탐지 → Slack 알림 | |
| 84 | + |
| 85 | +### 👤 회원 (Member) |
| 86 | + |
| 87 | +| 기능 | 구현 상세 | |
| 88 | +|:-------|:--------------------------------------------------| |
| 89 | +| 회원가입 | BCrypt 암호화, IP별 Rate Limiting(시간당 3회, Lua Script) | |
| 90 | +| 이메일 인증 | SecureRandom 6자리 코드, Redis TTL 5분, 시도 횟수 제한(5회) | |
| 91 | +| 탈퇴/복구 | Soft Delete + 30일 복구 유예, 복구 토큰(UUID, 24시간 TTL) | |
| 92 | +| 감사 로그 | 로그인/가입 이벤트 비동기 저장(@Async + REQUIRES_NEW) | |
| 93 | + |
| 94 | +### ⏳ 대기열 (Queue) |
| 95 | + |
| 96 | +| 기능 | 구현 상세 | |
| 97 | +|:-------|:---------------------------------------| |
| 98 | +| 대기열 진입 | Redis Sorted Set 기반, 타임스탬프 스코어로 순서 보장 | |
| 99 | +| 순번 조회 | ZRANK 명령으로 실시간 대기 순번 반환 | |
| 100 | +| 입장 처리 | 순차적 입장 토큰 발급, 유효 시간 제한 | |
| 101 | +| 상태 관리 | WAITING → PROCESSING → COMPLETED 상태 전이 | |
| 102 | +| 환경 설정 | 대기열 on/off 설정 가능, 트래픽 상황에 따라 유연하게 적용 | |
| 103 | + |
| 104 | +### 🪑 좌석 예매 (Reservation) |
| 105 | + |
| 106 | +| 기능 | 구현 상세 | |
| 107 | +|:------|:-------------------------------------------| |
| 108 | +| 좌석 선점 | AVAILABLE → HOLD 상태 전이, 5분 TTL 설정 | |
| 109 | +| 중복 방지 | 동일 좌석 동시 선점 시도 시 낙관적 락으로 충돌 감지 | |
| 110 | +| 예매 확정 | 결제 완료 시 HOLD → SOLD 상태 전이 | |
| 111 | +| 선점 만료 | 스케줄러 기반 TTL 만료 좌석 자동 복구 (HOLD → AVAILABLE) | |
| 112 | +| 예매 취소 | 예매 취소 시 좌석 상태 원복, 환불 처리 연동 | |
| 113 | + |
| 114 | +### 🎲 추첨 예매 (Lottery) |
| 115 | + |
| 116 | +| 기능 | 구현 상세 | |
| 117 | +|:-------|:---------------------------| |
| 118 | +| 응모 등록 | 회차/등급별 응모, 중복 응모 검증 | |
| 119 | +| 응모 제한 | 1인당 등급별 최대 응모 수량 제한 | |
| 120 | +| 추첨 처리 | SecureRandom 기반 공정 추첨 알고리즘 | |
| 121 | +| 당첨 처리 | 당첨자 좌석 자동 배정, 결제 기한 설정 | |
| 122 | +| 결과 알림 | 당첨/낙첨 이메일 비동기 발송 | |
| 123 | +| 미결제 처리 | 결제 기한 초과 시 자동 당첨 취소, 좌석 반환 | |
| 124 | + |
| 125 | +### 📝 사전신청 예매 (Pre-Reservation) |
| 126 | + |
| 127 | +| 기능 | 구현 상세 | |
| 128 | +|:------|:------------------------| |
| 129 | +| 신청 기간 | 오픈/마감 일시 기반 신청 가능 기간 검증 | |
| 130 | +| 신청 등록 | 회차/등급별 사전신청, 수량 지정 | |
| 131 | +| 신청 확정 | 신청 → 결제 대기 → 결제 완료 흐름 | |
| 132 | +| 만료 처리 | 결제 기한 초과 시 신청 자동 만료 | |
| 133 | +| 신청 취소 | 사용자 요청에 의한 신청 취소 처리 | |
| 134 | + |
| 135 | +### 💳 결제 (Payment) |
| 136 | + |
| 137 | +| 기능 | 구현 상세 | |
| 138 | +|:--------|:--------------------------------------------------| |
| 139 | +| 도메인별 분리 | 좌석예매/추첨/사전신청/거래 각각 독립된 결제 흐름 | |
| 140 | +| 상태 관리 | PENDING → PROCESSING → COMPLETED/FAILED/CANCELLED | |
| 141 | +| 환불 계좌 | 계좌번호 마스킹 저장, BankCode Enum 매핑 | |
| 142 | + |
| 143 | +### 🔁 거래 (Trade) |
| 144 | + |
| 145 | +| 기능 | 구현 상세 | |
| 146 | +|:-------|:-----------------------| |
| 147 | +| 거래 등록 | 티켓 소유권 검증, 중복 등록 방지 | |
| 148 | +| 거래 요청 | 구매자 거래 요청, 판매자 승인 대기 | |
| 149 | +| 거래 승인 | 판매자 승인 시 결제 프로세스 진입 | |
| 150 | +| 소유권 이전 | 결제 완료 시 티켓 소유권 구매자로 변경 | |
| 151 | + |
| 152 | +### 🧑💼 관리자 (Admin) |
| 153 | + |
| 154 | +| 기능 | 구현 상세 | |
| 155 | +|:------|:------------------------------------------------| |
| 156 | +| 회원 관리 | 검색/필터링/페이징, 대시보드 통계 | |
| 157 | +| 인증 관리 | 로그인/가입 로그 조회, 계정 잠금 해제 | |
| 158 | +| 권한 분리 | `/api/admin/**` URL 레벨 + `@PreAuthorize` 메서드 레벨 | |
| 159 | + |
| 160 | +--- |
52 | 161 |
|
53 | 162 | ## 🛠️ 기술 스택 |
54 | 163 |
|
|
60 | 169 |  |
61 | 170 |  |
62 | 171 |  |
| 172 | + |
63 | 173 | ### \<DataBase\> |
64 | 174 |  |
65 | 175 |  |
|
88 | 198 |  |
89 | 199 |  |
90 | 200 |
|
91 | | -## 🏗️ 아키텍처 |
92 | | -(작성) |
| 201 | +--- |
| 202 | + |
| 203 | +## 🏗️ 시스템 아키텍처 |
| 204 | + |
| 205 | +<img width="2430" height="1386" alt="Image" src="https://github.com/user-attachments/assets/9d08cc5f-a93b-45b5-9fcd-9c9b39275a24" /> |
| 206 | + |
| 207 | +--- |
93 | 208 |
|
94 | 209 | ## 🗂️ ERD |
95 | | -(작성) |
96 | 210 |
|
97 | | -## 🎬 시연 영상 |
98 | | -(작성) |
| 211 | + |
| 212 | + |
| 213 | +--- |
| 214 | + |
| 215 | +## 📁 프로젝트 구조 |
| 216 | + |
| 217 | +<details> |
| 218 | +<summary><b>펼쳐보기</b></summary> |
| 219 | + |
| 220 | +``` |
| 221 | +src/main/java/com/back/b2st/ |
| 222 | +├── domain/ |
| 223 | +│ ├── auth/ # JWT, OAuth, 로그인 보안, 토큰 관리 |
| 224 | +│ │ ├── client/ # KakaoApiClient, KakaoJwksClient |
| 225 | +│ │ ├── controller/ # AuthController, AuthAdminController |
| 226 | +│ │ ├── service/ # AuthService, LoginSecurityService, SecurityThreatDetectionService |
| 227 | +│ │ ├── entity/ # RefreshToken(Redis), LoginLog, OAuthNonce |
| 228 | +│ │ ├── listener/ # LoginEventListener (비동기 로그 저장) |
| 229 | +│ │ └── metrics/ # AuthMetrics, SecurityMetrics |
| 230 | +│ │ |
| 231 | +│ ├── member/ # 회원 CRUD, 탈퇴/복구, Rate Limiting |
| 232 | +│ ├── email/ # 이메일 인증, 비동기 발송 |
| 233 | +│ ├── performance/ # 공연 관리 |
| 234 | +│ ├── performanceschedule/# 공연 회차 |
| 235 | +│ ├── seat/ # 좌석 관리 |
| 236 | +│ ├── scheduleseat/ # 회차별 좌석 상태 |
| 237 | +│ ├── queue/ # 대기열 시스템 |
| 238 | +│ ├── reservation/ # 좌석 예매 |
| 239 | +│ ├── prereservation/ # 사전 신청 |
| 240 | +│ ├── lottery/ # 추첨 예매 |
| 241 | +│ ├── payment/ # 결제 |
| 242 | +│ ├── ticket/ # 티켓 관리 |
| 243 | +│ ├── trade/ # 거래 (교환/양도) |
| 244 | +│ ├── venue/ # 공연장 |
| 245 | +│ └── bank/ # 은행 코드 Enum |
| 246 | +│ |
| 247 | +├── global/ |
| 248 | +│ ├── alert/ # SlackAlertService (Webhook 연동) |
| 249 | +│ ├── config/ # Redis, S3, Redisson, Alert 설정 |
| 250 | +│ ├── error/ # GlobalExceptionHandler, ErrorCode |
| 251 | +│ ├── jwt/ # JwtTokenProvider, JwtAuthenticationFilter |
| 252 | +│ ├── jpa/ # BaseEntity, QueryDslConfig, AuditorAware |
| 253 | +│ ├── s3/ # S3Service, PresignedUrl |
| 254 | +│ ├── util/ # MaskingUtil, CookieUtils, SecurityUtils |
| 255 | +│ └── metrics/ # MetricsConfig |
| 256 | +│ |
| 257 | +└── security/ # Spring Security 설정 |
| 258 | + ├── SecurityConfig.java |
| 259 | + ├── CustomUserDetails.java |
| 260 | + ├── CustomUserDetailsService.java |
| 261 | + ├── JwtAuthenticationEntryPoint.java |
| 262 | + └── JwtAccessDeniedHandler.java |
| 263 | +
|
| 264 | +docker/ |
| 265 | +├── docker-compose.yml # 전체 스택 (App, DB, Redis Cluster, Monitoring) |
| 266 | +├── monitoring/ |
| 267 | +│ ├── prometheus/ |
| 268 | +│ │ ├── prometheus.yml |
| 269 | +│ │ └── rules/auth-alerts.yml |
| 270 | +│ ├── grafana/ |
| 271 | +│ │ ├── provisioning/ |
| 272 | +│ │ └── dashboards/ |
| 273 | +│ └── alertmanager/ |
| 274 | +│ └── alertmanager.yml |
| 275 | +└── init-*.sh # Redis Cluster 초기화 스크립트 |
| 276 | +``` |
| 277 | + |
| 278 | +</details> |
| 279 | + |
| 280 | +--- |
| 281 | + |
| 282 | +## 📊 모니터링 구성 |
| 283 | + |
| 284 | +<details> |
| 285 | +<summary><b>Grafana 대시보드</b></summary> |
| 286 | + |
| 287 | +| 계층 | 대시보드 | 주요 메트릭 | |
| 288 | +|:------------|:-------------------------|:----------------------------------------------------------------------------| |
| 289 | +| **Service** | tt-service-overview | 요청 수, 에러율, 응답 시간 분포 | |
| 290 | +| **Domain** | tt-auth-dashboard | `auth_login_total`, `auth_account_locked_total`, `auth_token_reissue_total` | |
| 291 | +| | tt-email-dashboard | `email_sent_total`, `email_verification_total` | |
| 292 | +| | tt-queue-dashboard | 대기열 상태, 처리량 | |
| 293 | +| | tt-reservation-dashboard | 예매 생성, 좌석 선점/취소 | |
| 294 | +| | tt-lottery-dashboard | 응모 수, 당첨 처리 | |
| 295 | +| | tt-payment-dashboard | 결제 요청/완료/실패 | |
| 296 | +| | tt-trade-dashboard | 거래 등록/완료 | |
| 297 | +| **Infra** | tt-jvm-dashboard | 힙 메모리, GC, 스레드 풀 | |
| 298 | +| | tt-database-dashboard | 커넥션 풀, 쿼리 성능 | |
| 299 | +| | tt-redis-dashboard | 메모리, 커맨드/sec, 키 상태 | |
| 300 | + |
| 301 | +</details> |
| 302 | + |
| 303 | +<details> |
| 304 | +<summary><b>Alertmanager 규칙</b></summary> |
| 305 | + |
| 306 | +```yaml |
| 307 | +// 예시 |
| 308 | +groups: |
| 309 | + - name: auth-alerts |
| 310 | + rules: |
| 311 | + - alert: HighLoginFailureRate |
| 312 | + expr: rate(auth_login_total{result="failure"}[5m]) > 0.1 |
| 313 | + for: 2m |
| 314 | + labels: |
| 315 | + severity: warning |
| 316 | + annotations: |
| 317 | + summary: "로그인 실패율 증가" |
| 318 | + |
| 319 | + - alert: AccountLockDetected |
| 320 | + expr: increase(auth_account_locked_total[5m]) > 0 |
| 321 | + labels: |
| 322 | + severity: critical |
| 323 | + annotations: |
| 324 | + summary: "계정 잠금 발생" |
| 325 | +``` |
| 326 | +
|
| 327 | +</details> |
| 328 | +
|
| 329 | +--- |
| 330 | +
|
99 | 331 | ## 🧩 협업 규칙 |
100 | 332 |
|
101 | | -### 💬 코드 컨벤션 |
| 333 | +<details> |
| 334 | +<summary><b>펼쳐보기</b></summary> |
| 335 | +
|
| 336 | +### 코드 컨벤션 |
| 337 | +
|
102 | 338 | - **Naver Java Convention** 기반 |
103 | | -- **기본 원칙** |
104 | | - - 가독성 최우선 |
105 | | - - 특별한 이유가 없는 경우 **IntelliJ IDEA 자동 서식** 준수 |
| 339 | +- IntelliJ IDEA 자동 서식 준수 |
| 340 | +
|
| 341 | +### Git 브랜치 전략 |
| 342 | +
|
| 343 | +- `main`: 프로덕션 |
| 344 | +- `develop`: 개발 통합 |
| 345 | +- `feature/*`: 기능 개발 |
| 346 | +- 머지 조건: 최소 1명 리뷰 승인 |
| 347 | + |
| 348 | +</details> |
| 349 | + |
| 350 | +--- |
106 | 351 |
|
107 | 352 | ### 🏷️ 네이밍 & 작성 규칙 |
108 | 353 |
|
| 354 | +<details> |
| 355 | +<summary><b>펼쳐보기</b></summary> |
| 356 | + |
109 | 357 | #### 이슈(Issue) |
110 | 358 | - **제목 규칙**: `[타입] 작업내용` |
111 | 359 | - 예시: `[feat] 로그인 기능 추가` |
|
134 | 382 | | `refactor` | 코드 리팩토링(동작 변화 없음) | |
135 | 383 | | `test` | 테스트 코드 추가/수정 | |
136 | 384 |
|
| 385 | +</details> |
137 | 386 |
|
| 387 | +## 📄 라이선스 |
138 | 388 |
|
139 | | - |
| 389 | +프로그래머스 K-Digital Training 파이널 프로젝트 |
0 commit comments