Skip to content

feat: 스코어 보드 추가#489

Merged
seongminn merged 5 commits intodevfrom
basketball-score
Apr 15, 2026
Merged

feat: 스코어 보드 추가#489
seongminn merged 5 commits intodevfrom
basketball-score

Conversation

@seongminn
Copy link
Copy Markdown
Member

✅ 작업 내용

  • 시안에 맞게 스코어보드 UI를 추가했습니다.
  • 이제 농구 게임으로 넘어갈 때 URL을 분리해야 하는데, 농구/축구에 따라서 은근 분기가 많이 생길거 같기도 하고 종목에 따라 진입점이 명확하게 변경되는 요소이기 때문에 쿼리스트링이 아니라 아예 라우팅 구조를 변경했습니다.
  • 그래서 routes.ts를 의존하던 다른 파일도 일부 수정했습니다.

UI

스크린샷 2026-04-14 오전 12 37 22

URL

스크린샷 2026-04-14 오전 12 40 40
  • 더 좋은 건 games 보다 이전에 종목을 두는 게 좋을 거 같은데 그러면 모든 페이지를 변경해야 해서 일단은 이렇게 했습니다..

📝 참고 자료

  • 작업한 내용에 대한 부연 설명

♾️ 기타

  • 추가로 필요한 작업 내용

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spectator Ready Ready Preview, Comment Apr 15, 2026 0:20am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
manager Skipped Skipped Apr 15, 2026 0:20am

Copy link
Copy Markdown
Contributor

@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 basketball-specific game pages and a new ScoreBoard component to display scores by quarter. It refactors the routing system to include the sport type in game URLs (e.g., /games/basketball/:id) and adds the necessary API hooks and types for fetching quarter-level data. The review feedback focuses on improving the ScoreBoard component by using dynamic labels from the API, mapping scores more safely using team IDs, and consolidating the layout into a single table for better accessibility. Additionally, it is recommended to use absolute paths for routing consistency across the application.


const [homeTeam, awayTeam] = gameInfo.gameTeams;

const labels = quarterScores.length === 5 ? [...BASE_LABELS, 'OT'] : BASE_LABELS;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

쿼터 레이블을 하드코딩하는 대신 API 응답 데이터에 포함된 displayName을 사용하는 것이 더 안전하고 유연합니다. 연장전이 여러 번 발생하거나 쿼터 명칭이 변경되는 경우에도 대응할 수 있습니다.

Suggested change
const labels = quarterScores.length === 5 ? [...BASE_LABELS, 'OT'] : BASE_LABELS;
const labels = quarterScores.map(q => q.displayName);

Comment on lines +19 to +28
const scores = quarterScores.reduce(
(acc, { scores }, index) => {
const [home, away] = scores;
acc.home[index] = home.score;
acc.away[index] = away.score;

return acc;
},
{ home: Array(labels.length).fill(null), away: Array(labels.length).fill(null) },
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

배열 구조 분해 할당([home, away] = scores)은 API에서 반환하는 팀의 순서에 의존하므로 위험할 수 있습니다. homeTeamawayTeamgameTeamId를 사용하여 명확하게 매칭하는 것이 좋습니다.

  const homeScores = quarterScores.map(q => q.scores.find(s => s.gameTeamId === homeTeam.gameTeamId)?.score ?? '-');
  const awayScores = quarterScores.map(q => q.scores.find(s => s.gameTeamId === awayTeam.gameTeamId)?.score ?? '-');

Comment on lines +31 to +67
<div className="mb-4 flex justify-center px-4 text-sm">
<div className="grid w-1/5 shrink-0 grid-rows-[auto_auto_auto]">
<div className="font-semibold text-greyscale-500">팀명</div>
<div className="pt-2">{homeTeam.gameTeamName}</div>
<div>{awayTeam.gameTeamName}</div>
</div>

<table className="w-3/5 border-collapse text-center">
<thead>
<tr className="text-greyscale-500">
{labels.map((label) => (
<th className="font-semibold" key={label}>
{label}
</th>
))}
<th className="text-blue-600">총점</th>
</tr>
</thead>
<tbody>
<tr>
{labels.map((label, index) => (
<td className="pt-2" key={`home-${label}`}>
{scores.home[index] ?? '-'}
</td>
))}
<td className="pt-2 text-blue-600">{homeTeam.score}</td>
</tr>
<tr>
{labels.map((label, index) => (
<td key={`away-${label}`}>{scores.away[index] ?? '-'}</td>
))}
<td className="text-blue-600">{awayTeam.score}</td>
</tr>
</tbody>
<caption className="sr-only">스코어보드 경기현황</caption>
</table>
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

팀 이름과 점수표가 별도의 divtable로 분리되어 있어, 행 높이가 달라질 경우 정렬이 어긋날 수 있고 스크린 리더 사용자의 접근성 측면에서도 좋지 않습니다. 하나의 table 구조로 통합하여 관리하는 것을 권장합니다.

Comment on lines +55 to +56
onBroadcastClick={() => router.push(link)}
onCheerClick={() => router.push(`/${link}?cheer=1`)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

routes.game이 반환하는 경로는 /로 시작하지 않으므로, router.push 시 절대 경로로 동작하게 하려면 앞에 /를 붙여주는 것이 안전합니다. 다른 파일들과의 일관성을 유지하세요.

                <GameCard.Actions
                  onBroadcastClick={() => router.push(`/${link}`)}
                  onCheerClick={() => router.push(`/${link}?cheer=1`)}
                />

@vercel vercel bot temporarily deployed to Preview – manager April 15, 2026 12:19 Inactive
@seongminn seongminn merged commit eb56ba2 into dev Apr 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants