diff --git a/internal/bossraid/service.go b/internal/bossraid/service.go index 97e5d96..5418834 100644 --- a/internal/bossraid/service.go +++ b/internal/bossraid/service.go @@ -64,17 +64,6 @@ func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error seen[u] = true } - // Check if any player is already in an active room - for _, username := range usernames { - count, err := s.repo.CountActiveByUsername(username) - if err != nil { - return nil, fmt.Errorf("플레이어 상태 확인 실패: %w", err) - } - if count > 0 { - return nil, fmt.Errorf("플레이어 %s가 이미 보스 레이드 중입니다", username) - } - } - playersJSON, err := json.Marshal(usernames) if err != nil { return nil, fmt.Errorf("플레이어 목록 직렬화 실패: %w", err) @@ -90,8 +79,24 @@ func (s *Service) RequestEntry(usernames []string, bossID int) (*BossRoom, error Players: string(playersJSON), } - if err := s.repo.Create(room); err != nil { - return nil, fmt.Errorf("방 생성 실패: %w", err) + // Wrap active-room check + creation in a transaction to prevent TOCTOU race. + err = s.repo.Transaction(func(txRepo *Repository) error { + for _, username := range usernames { + count, err := txRepo.CountActiveByUsername(username) + if err != nil { + return fmt.Errorf("플레이어 상태 확인 실패: %w", err) + } + if count > 0 { + return fmt.Errorf("플레이어 %s가 이미 보스 레이드 중입니다", username) + } + } + if err := txRepo.Create(room); err != nil { + return fmt.Errorf("방 생성 실패: %w", err) + } + return nil + }) + if err != nil { + return nil, err } return room, nil