Skip to content

feat: LoL 도메인 분리 및 세션/팀밸런싱 기능 구현 (#84)#88

Merged
Sunja-An merged 1 commit intomainfrom
feat/#84-lol-temporal-v2
Apr 6, 2026
Merged

feat: LoL 도메인 분리 및 세션/팀밸런싱 기능 구현 (#84)#88
Sunja-An merged 1 commit intomainfrom
feat/#84-lol-temporal-v2

Conversation

@Sunja-An
Copy link
Copy Markdown
Contributor

@Sunja-An Sunja-An commented Apr 5, 2026

Summary

  • contest 도메인에 혼재되어 있던 LoL 관련 로직을 독립적인 lol 도메인으로 분리
  • LoL 세션 관리 기능 추가 (WebSocket Hub, Redis 어댑터)
  • 포지션 선호도 기반 5:5 팀 밸런싱 알고리즘 구현 및 성능 개선
  • LoL 커스텀 매치 도메인 및 DB 마이그레이션(000026) 추가
  • SSE 기반 notification 도메인 제거 (WebSocket으로 대체)

Changes

New: internal/lol/ 도메인

  • domain/: LolSession, LolCustomMatch 엔티티
  • application/: 팀 밸런싱 서비스, 세션 서비스, port 인터페이스
  • infra/: MySQL 어댑터, Redis 어댑터, WebSocket Hub
  • presentation/: LoL Temporal 컨트롤러, 세션 컨트롤러
  • provider.go: DI 와이어링

Removed: internal/notification/ 도메인

  • SSE 기반 알림 시스템 전체 제거

Modified

  • cmd/server.go: lol 도메인 DI 등록
  • internal/contest/: LoL 관련 로직 제거 및 정리
  • internal/game/application/team_service.go: 팀 밸런싱 로직 lol 도메인으로 이전

DB Migration

  • 000026_create_lol_custom_matches_table: LoL 커스텀 매치 테이블 생성

Tests

  • test/lol/application/: 팀 밸런싱 서비스 단위 테스트 및 벤치마크
  • test/lol/presentation/: LoL Temporal 컨트롤러 단위 테스트

Test plan

  • go test ./test/lol/... — LoL 도메인 단위 테스트 통과
  • go test ./... — 전체 테스트 통과
  • make migrate-up — 마이그레이션 000026 정상 적용
  • WebSocket 세션 연결/종료 정상 동작 확인
  • 팀 밸런싱 API 엔드포인트 응답 확인

🤖 Generated with Claude Code

- contest 도메인에서 lol 도메인으로 LoL 관련 로직 분리
- LoL 세션 관리 (WebSocket Hub, Redis adapter) 추가
- LoL 커스텀 매치 도메인 및 DB 마이그레이션 추가
- notification 도메인 제거 (SSE → WebSocket으로 대체)
- 팀 밸런싱 서비스 및 벤치마크 테스트 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new League of Legends (LoL) module, providing custom match session management and team balancing capabilities. It includes new domain models, services, and WebSocket-based session handling. My feedback focuses on improving system reliability and observability by recommending the inclusion of context.Context in repository interfaces to support proper timeout and cancellation propagation. Additionally, I suggested improving error handling in the WebSocket event builder and refactoring the authentication helper for better consistency across controllers.

Comment on lines +5 to +9
type LolCustomMatchRepositoryPort interface {
Save(match *domain.LolCustomMatch, players []*domain.LolCustomMatchPlayer) error
FindByID(matchID int64) (*domain.LolCustomMatch, error)
UpdateResult(match *domain.LolCustomMatch) error
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

리포지토리 인터페이스의 모든 메서드에 context.Context를 첫 번째 인자로 추가하는 것이 좋습니다.

요청 범위의 컨텍스트를 데이터베이스 작업까지 전달하면 타임아웃, 취소 전파, 트레이싱이 가능해져 시스템의 안정성을 높일 수 있습니다. GORM에서는 db.WithContext(ctx)를 사용하여 컨텍스트를 전달할 수 있습니다.

이 변경은 LolCustomMatchRepositoryPort 인터페이스, LolCustomMatchDatabaseAdapter 구현, 그리고 LolTeamBalanceService에 걸쳐 적용되어야 합니다.

Suggested change
type LolCustomMatchRepositoryPort interface {
Save(match *domain.LolCustomMatch, players []*domain.LolCustomMatchPlayer) error
FindByID(matchID int64) (*domain.LolCustomMatch, error)
UpdateResult(match *domain.LolCustomMatch) error
}
type LolCustomMatchRepositoryPort interface {
Save(ctx context.Context, match *domain.LolCustomMatch, players []*domain.LolCustomMatchPlayer) error
FindByID(ctx context.Context, matchID int64) (*domain.LolCustomMatch, error)
UpdateResult(ctx context.Context, match *domain.LolCustomMatch) error
}

Comment on lines +5 to +9
type LolSessionRepositoryPort interface {
Save(session *domain.LolSession) error
FindByID(sessionID string) (*domain.LolSession, error)
Delete(sessionID string) error
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

리포지토리 인터페이스의 모든 메서드에 context.Context를 첫 번째 인자로 추가하는 것을 권장합니다.

context.Context를 전달하면 요청 타임아웃, 취소, 분산 추적과 같은 기능을 올바르게 전파할 수 있어 애플리케이션의 안정성과 관측 가능성이 향상됩니다. 현재 LolSessionRedisAdapter 구현에서는 context.Background()를 사용하고 있는데, 이는 요청 범위의 컨텍스트를 전달하는 모범 사례에 어긋납니다.

이 변경은 LolSessionRepositoryPort 인터페이스, LolSessionRedisAdapter 구현, 그리고 LolSessionService에 걸쳐 적용되어야 합니다.

Suggested change
type LolSessionRepositoryPort interface {
Save(session *domain.LolSession) error
FindByID(sessionID string) (*domain.LolSession, error)
Delete(sessionID string) error
}
type LolSessionRepositoryPort interface {
Save(ctx context.Context, session *domain.LolSession) error
FindByID(ctx context.Context, sessionID string) (*domain.LolSession, error)
Delete(ctx context.Context, sessionID string) error
}

Comment on lines +306 to +310
func (c *LolSessionController) buildEvent(event string, data interface{}) []byte {
msg := dto.WsServerMessage{Event: event, Data: data}
b, _ := json.Marshal(msg)
return b
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

json.Marshal에서 발생하는 오류를 무시하고 있습니다. WsServerMessage에 포함되는 data의 타입이 제어되므로 실제 오류 발생 가능성은 낮지만, 만약 마샬링할 수 없는 데이터(예: 함수, 채널)가 포함될 경우 예기치 않은 동작을 유발할 수 있습니다.

오류를 로깅하고, 가능하면 클라이언트에게 내부 서버 오류를 알리는 것이 더 안정적인 구현입니다.

func (c *LolSessionController) buildEvent(event string, data interface{}) []byte {
	msg := dto.WsServerMessage{Event: event, Data: data}
	b, err := json.Marshal(msg)
	if err != nil {
		log.Printf("[LolSessionController] error marshaling event %s: %v", event, err)
		// Optionally, create a marshaled error message to send to the client
		errorEvent := dto.WsServerMessage{Event: dto.WsEventError, Data: gin.H{"message": "Internal server error preparing event."}}
		b, _ = json.Marshal(errorEvent)
	}
	return b
}

Comment on lines +312 to +319
func getAuthenticatedUserID(ctx *gin.Context) (int64, bool) {
userID, ok := middleware.GetUserIdFromContext(ctx)
if !ok {
response.JSON(ctx, response.Error(401, "user not authenticated"))
return 0, false
}
return userID, true
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

이 헬퍼 함수의 이름은 getAuthenticatedUserID이지만, 실제로는 사용자 ID를 가져오는 것 외에 인증 실패 시 HTTP 응답을 직접 전송하는 부수 효과(side effect)를 가지고 있습니다. 이는 함수 이름이 암시하는 것 이상의 동작이며, 코드의 명확성을 해칠 수 있습니다.

lol_temporal_controller.go에서 사용된 것처럼 middleware.GetUserIdFromContext를 직접 호출하고 컨트롤러 핸들러 내에서 오류를 처리하는 방식이 더 일관성 있고 명확합니다. 이 컨트롤러에서도 동일한 패턴을 적용하여 이 헬퍼 함수를 제거하는 것을 고려해 보세요.

@Sunja-An Sunja-An merged commit ddb4cda into main Apr 6, 2026
1 check passed
@Sunja-An Sunja-An self-assigned this Apr 6, 2026
@Sunja-An Sunja-An added the 🔨 FEATURE Good for newcomers label Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 FEATURE Good for newcomers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant