목차 열기
추천 알고리즘
PlayBall의 추천 서비스는 일반 티켓팅 사이트와 차별화된 핵심 기능입니다. 유저가 직접 좌석맵에서 좌석을 고르는 대신, 온보딩 시 입력한 선호 데이터를 기반으로 최적의 블럭을 1순위~10순위로 추천하고, 블럭을 선택하면 서버가 자동으로 최적 연석을 배정합니다. 추천 시스템은 블록 추천(Phase 1)과 좌석 배정(Phase 2) 두 단계로 나뉩니다.
온보딩 선호 데이터
회원가입 직후 온보딩 과정에서 다음 데이터를 수집합니다.
| 데이터 | 엔티티 | 추천 알고리즘 사용 |
|---|---|---|
| 선호 구단 | OnboardingPreference.favoriteClub | 1루/3루 블럭 방향 결정 |
| 응원석 인접 선호 | cheerProximityPref (NEAR/FAR/ANY) | 응원석 근접 블럭 가산점 |
| 뷰포인트 우선순위 | OnboardingViewpointPriority (1~3순위) | 뷰포인트 매칭 가산점 |
| 선호 블럭 목록 | OnboardingPreferredBlock | 추천 대상 블럭 필터링 |
구단 기반 1루/3루 자동 판정
경기: 삼성 라이온즈 vs 한화 이글스
유저 선호 구단: 삼성 라이온즈
Case 1: 삼성이 홈(1루)일 때 → 1루(홈) 블럭 쪽 추천 가산점 +25점
Case 2: 삼성이 어웨이(3루)일 때 → 3루(어웨이) 블럭 쪽 추천 가산점 +25점
Case 3: 삼성이 참여하지 않는 경기 → 뷰포인트 우선순위로 판정Phase 1 — 블록 추천
선호 블록(최대 10개)만 탐색하여 실제 N연석이 가능한 묶음 수를 계산하고, 취향 점수를 매깁니다.
취향 점수 계산 (최대 70점)
| 항목 | 조건 | 점수 |
|---|---|---|
| 뷰포인트 우선순위 | 1순위 블록 | 30점 |
| 뷰포인트 우선순위 | 2순위 블록 | 20점 |
| 뷰포인트 우선순위 | 3순위 블록 | 10점 |
| 응원구단 매칭 | 경기 참가 구단 선호 + 블럭 진영 일치 | +25점 |
| 응원석 근접 (NEAR) | NEAR 선호 + cheerRank ≤ 3 | +15점 |
| 응원석 원거리 (FAR) | FAR 선호 + cheerRank > 3 | +15점 |
정렬 기준
- 1차: 가용 연석 수 (차이가 10석 초과 시 바로 결정)
- 2차: 연석 수 차이 ≤ 10 → 취향 점수 높은 순
- 3차: 동점 시 → 연석 개수 순
예시 (N=4명 요청):
블럭 A: 연석 42개, 선호도 65점
블럭 B: 연석 38개, 선호도 55점
블럭 C: 연석 40개, 선호도 70점
A vs C: |42-40| = 2 ≤ 10 → 선호도 비교 → C(70) > A(65) → C가 상위
A vs B: |42-38| = 4 ≤ 10 → 선호도 비교 → A(65) > B(55) → A가 상위
최종: C(1순위) → A(2순위) → B(3순위)
만약 블럭 D: 연석 5개, 선호도 70점이라면
A vs D: |42-5| = 37 > 10 → 연석 수 비교 → A가 상위
(선호도가 아무리 높아도 좌석이 너무 적으면 하위 랭크)Phase 2 — 좌석 배정
사용자가 블록을 선택하면 Redisson 블럭 단위 분산 락을 획득하고, 실연석(같은 열 연속 N석) → 준연석(인접 2열 N석) 순으로 탐색합니다.
배정 우선순위
- 실연석 (Real Consecutive) — 같은 행의 연속 좌석
- Priority 1: 앞줄 (rowNo 오름차순)
- Priority 2: 통로 근접 (aisleDistance 오름차순)
- Priority 3: 왼쪽 (startCol 오름차순)
- 준연석 (Semi-Consecutive) — 인접 2행 겹침 (인접석 토글 ON 시)
- 두 행이 물리적으로 인접 (rowNo 차이 = 1)
- 수평 겹침(overlap) > 0 필수
- Priority: 앞줄 합 → 겹침 수 최대 → 평균 통로 근접
- 준연석도 없는 경우 — 추천 좌석 없음 반환
통로 근접도 (Aisle Distance) 계산
rowStartCol = 해당 열의 첫 번째 좌석 열번호
rowEndCol = 해당 열의 마지막 좌석 열번호
leftDistance = groupStartCol - rowStartCol (왼쪽 통로까지 거리)
rightDistance = rowEndCol - groupEndCol (오른쪽 통로까지 거리)
aisleDistance = min(leftDistance, rightDistance)동시성 제어 (조건부 UPDATE)
좌석 상태 변경 시 markBlockedIfAvailable() 조건부 UPDATE로 일반 좌석 선택 모드와의 충돌까지 안전하게 감지합니다. 실연석/준연석 각각 최대 3회 재시도로 다른 연석 구간을 탐색합니다.
UPDATE match_seats
SET sale_status = 'BLOCKED'
WHERE id = :matchSeatId
AND sale_status = 'AVAILABLE'
-- return 0이면 이미 다른 유저가 선점 → 롤백 후 재시도배정된 좌석은 5분간 Hold 상태가 되며, 만료 시 SeatHoldCleanupScheduler에 의해 60초 간격으로 자동 해제됩니다.
추천 ON vs 추천 OFF
| 항목 | 추천 ON | 추천 OFF (일반 선택) |
|---|---|---|
| 블럭 선택 | 서버가 추천 (1~10순위) | 유저가 직접 선택 |
| 좌석 배정 | 서버가 자동 배정 (연석) | 유저가 좌석맵에서 직접 클릭 |
| 락 단위 | 블럭 단위 Redisson RLock (Watch Dog) | 좌석 단위 Redisson RLock (500ms wait) |
| 인원수 | 예매 옵션에서 사전 설정 | 클릭한 좌석 수 = 인원수 |