Conversation
- 기존 라인별 2명 고정 방식 → 10명 자유 입력 + 포지션 선호도 기반으로 교체 - C(10,5)=252 × 5!×5!=14,400 조합 완전 탐색 (약 360만 케이스) - 3단계 필터링: 팀 MMR 밸런스 → 라인별 맞대결 편차 → 포지션 만족도 - 포지션 보정(Model A): 1순위 100% ~ 5순위 75% (6.25%씩 선형 감소) - 새 에러코드: ErrLolTemporalInvalidPlayerCount(CT048), ErrLolTemporalInvalidPositions(CT049) - 단위 테스트 13개 추가 (서비스 9개, 컨트롤러 8개) - Swagger 문서 갱신 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AcceptApplication에 application 조회 에러/nil/상태 검증 추가 - UploadThumbnail goroutine에서 context race condition 수정 (oldKey 캡처) - balanceLolTeamsV2 조합/순열 테이블 패키지 레벨 precompute로 성능 최적화 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: LoL Temporal 포지션 선호도 기반 5:5 팀 매칭 알고리즘 구현 (#84)
- contest 도메인에서 lol 도메인으로 LoL 관련 로직 분리 - LoL 세션 관리 (WebSocket Hub, Redis adapter) 추가 - LoL 커스텀 매치 도메인 및 DB 마이그레이션 추가 - notification 도메인 제거 (SSE → WebSocket으로 대체) - 팀 밸런싱 서비스 및 벤치마크 테스트 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: LoL 도메인 분리 및 세션/팀밸런싱 기능 구현 (#84)
There was a problem hiding this comment.
Code Review
This pull request introduces a new LoL module, including custom match session management via WebSockets and a team balancing algorithm based on position preferences. It also cleans up the notification system by removing unused SSE-related code and refactors the contest application service. I have provided feedback regarding context usage in goroutines, the removal of dead code, and suggestions for improving code reusability across controllers.
| if contest.BannerKey != nil && *contest.BannerKey != "" { | ||
| oldKey := *contest.BannerKey | ||
| go func() { | ||
| if delErr := c.storagePort.Delete(context.Background(), *contest.BannerKey); delErr != nil { |
There was a problem hiding this comment.
Using context.Background() in a goroutine that's part of a request lifecycle can be problematic as it detaches the operation from the request's context, making it impossible to cancel if the request is cancelled. While you've added a timeout which is a good improvement, it's better to pass the request's context ctx to the goroutine to ensure proper lifecycle management.
| if delErr := c.storagePort.Delete(context.Background(), *contest.BannerKey); delErr != nil { | |
| if delErr := c.storagePort.Delete(ctx, *contest.BannerKey); delErr != nil { |
| // rank 2 → 15/16 = 93.75% | ||
| // rank 5 → 12/16 = 75% | ||
| func adjustedMMRScore(baseMMR, preferenceRank int) int { | ||
| return baseMMR * (17 - preferenceRank) |
There was a problem hiding this comment.
The magic number 17 in this calculation makes the logic obscure. It's better to define it as a named constant with a comment explaining its purpose to improve code clarity and maintainability.
// The multiplier is derived from (MaxMultiplier + 1 - preferenceRank), where MaxMultiplier is 16.
const preferenceMultiplierBase = 17
return baseMMR * (preferenceMultiplierBase - preferenceRank)| func isBetterLolCandidate(a, b lolTeamCandidate) bool { | ||
| if a.teamBalance != b.teamBalance { | ||
| return a.teamBalance < b.teamBalance | ||
| } | ||
| if a.laneDeviation != b.laneDeviation { | ||
| return a.laneDeviation < b.laneDeviation | ||
| } | ||
| return a.posSatisfaction < b.posSatisfaction | ||
| } |
| session, _ := c.sessionService.GetSession(sessionID) | ||
| if session != nil { | ||
| c.broadcastToSession(sessionID, dto.WsEventPlayerJoined, gin.H{"players": session.Players}) | ||
| } |
There was a problem hiding this comment.
The error returned by c.sessionService.GetSession(sessionID) is ignored. While an error might be unlikely after a successful JoinSession, it's safer to handle it. If an error occurs, session will be nil, leading to a broadcast with incomplete data. Please consider logging the error for better diagnostics. A similar issue exists in the handleLeave function.
No description provided.