Playball Logo

Command Palette

Search for a command to run...

목차 열기

티켓팅 플로우 (추천 ON)

티켓팅은 대기열 진입부터 결제 완료까지 7개 STEP으로 구성됩니다. 각 단계는 보안 토큰과 분산 락으로 보호되어 대기열 우회, 좌석 중복 선점, 미결제 점유를 원천 차단합니다. 일반적인 티켓팅 사이트와 달리 PlayBall은 추천 서비스를 통해 사용자 선호 기반으로 블럭을 추천하고 서버가 최적 연석을 자동 배정합니다.


전체 흐름

STEP 1          STEP 2          STEP 3         STEP 4           STEP 5          STEP 6          STEP 7
경기 상세       대기열 화면      공잡기 미션     추천 블록 카드     좌석 자동        주문서 확인      결제 완료
페이지          (순번 대기)      (VQA)          선택              배정 완료

[예매하기]  →   대기열 진입  →   VQA 통과   →   추천 블록 10개 →  N연석 배정  →    결제 진행   →   좌석 SOLD
  클릭          폴링 대기        AI 검증         선호도 순위       Hold 5분         카카오페이       주문 완료

단계별 핵심 메커니즘

STEP핵심 기술목적
1. 예매 조건 저장Redis seat:booking-options (15분 TTL)추천 ON/OFF · 인원수 · 인접석 토글 보존
2. 대기열 진입Redis Sorted Set queue:wait:{matchId}순서 보장 + 대량 트래픽 흡수
3. 동적 폴링 + VQAAdmission Token (RSA JWT, 15분 TTL) + AI 봇 탐지대기열 우회 차단 + 매크로 방어
4. 블럭 추천선호도 점수 (최대 70점) + 가용 연석 수 랭킹사용자 취향 반영 1순위~10순위
5. 좌석 자동배정Redisson 블럭 분산 락 + 실연석/준연석 알고리즘동시성 제어 + 연석 보장
6. 주문서 확인Hold 검증 (5분 TTL) + 서버사이드 가격 재계산좌석 점유 증명 + 조작 방지
7. 결제 완료Kafka payment-completed → SeatBLOCKED → SOLD 비동기 확정

STEP 1 — 경기 상세 / 예매 옵션 저장

사용자가 추천 ON/OFF, 인원수, 인접석 토글을 설정하고 [예매하기] 버튼을 누르면, Seat 서비스가 예매 옵션을 Redis에 저장하고 Queue 전용 Redis에 PreQueue 마커를 동기화합니다.

POST /seat/matches/{matchId}/booking-options
Body: { recommendationEnabled: true, ticketCount: 4, nearAdjacentToggle: false }

→ Seat Redis: SET seat:booking-options:{matchId}:{userId}     (TTL 900s)
→ Queue Redis: SET queue:precheck:booking-option:{m}:{u}      (TTL 900s)
                                                                (Resilience4j @Retry)

STEP 2 — 대기열 진입

Redis Sorted Set에 타임스탬프 기반으로 순서가 기록됩니다. 스케줄러가 1초마다 100명씩 Ready 상태로 승격하고, 승격된 유저에게 Admission Token(RSA JWT, 15분 TTL)이 발급됩니다. 토큰은 HttpOnly + Secure + SameSite=None 쿠키로 전달됩니다.

STEP 3 — 동적 폴링 + 공잡기 미션 (VQA)

클라이언트는 자신의 순위(rank)에 따라 폴링 간격을 조절합니다.

순위폴링 간격이유
rank ≤ 1001.5초곧 입장할 유저에게 빠른 피드백
rank ≤ 10003초중간 대기
rank > 10005초긴 대기, 서버 부하 절감
READY 상태1초입장 가능 알림

대기 중 VQA(Visual Question Answering) 야구공잡기 게임이 실행되고, AI 방어 서버가 마우스 패턴/클릭 속도를 실시간 수집하여 봇 여부를 판단합니다. 의심 시 챌린지/즉시 차단 처리됩니다.

STEP 4 — 블럭 추천

사용자의 온보딩 선호 블럭 중에서 가용 연석 수와 선호도 점수를 기준으로 1~10순위 블럭을 반환합니다.

GET /seat/matches/{matchId}/recommendations/blocks
→ [
    { rank: 1, blockId: 204, blockName: "오렌지 C", consecutiveCount: 42, score: 65 },
    { rank: 2, blockId: 207, blockName: "옐로 A", consecutiveCount: 38, score: 55 },
    ...
  ]

STEP 5 — 좌석 자동 배정

사용자가 블럭을 선택하면 Redisson 블럭 단위 분산 락을 획득한 뒤, 실연석(같은 열 N석) → 준연석(인접 2열 N석) 순으로 탐색합니다.markBlockedIfAvailable() 조건부 UPDATE로 일반 좌석 선택과의 충돌도 안전하게 감지합니다.

STEP 6 — 주문서 확인 / 주문 생성

Hold 만료 여부 검증, 좌석 가격 서버사이드 재계산(클라이언트 조작 방지), 기존 미결제 주문 자동 취소 후 Order 엔티티를 PAYMENT_PENDING 상태로 생성합니다. 주문당 최대 8매(MAX_TICKETS_PER_ORDER)로 제한됩니다.

STEP 7 — 결제 처리 + 좌석 SOLD 확정

결제 완료 후 Kafka payment-completed 이벤트가 발행되며, Seat 서비스가 이를 소비하여 좌석 상태를 BLOCKED → SOLD로 전환합니다.

Order-Core ──[payment-completed]──→ Kafka ──→ Seat (BLOCKED → SOLD)

토큰 체인

Access Token (로그인)
     │
     ├→ 대기열 진입 → Admission Token 발급 (대기열 → 좌석 진입권, 15분 TTL)
     │                    │
     │                    ├→ 좌석 Hold (5분 TTL)
     │                    │       │
     │                    │       ├→ 주문 생성 (PAYMENT_PENDING)
     │                    │       │       │
     │                    │       │       └→ 결제 → SOLD (Kafka 이벤트)
     │                    │       │
     │                    │       └→ 5분 초과 시 자동 해제 (SeatHoldCleanupScheduler)
     │                    │
     │                    └→ 15분 초과 시 Admission Token 만료
     │
     └→ Refresh Token → Access Token 재발급 (RTR 방식)