최종 프로젝트: 게임 출시 정보·랭킹 대시보드
24강. 최종 프로젝트: 게임 출시 정보·랭킹 대시보드
1. 이번 강의에서 해결할 문제
지금까지 만든 컴포넌트와 기능을 실제 포트폴리오로 정리해야 합니다. 데이터 요청부터 반응형 페이지, 오류 처리, 라우팅까지 하나의 일관된 제품 흐름으로 통합합니다.
2. 학습 목표
- 기능별 폴더와 데이터 흐름을 최종 정리한다.
- 출시 정보, 랭킹, 상세, 404 화면을 완성한다.
- 접근성·반응형·빌드·Git 품질 기준으로 결과물을 점검한다.
3. 핵심 개념
최종 앱의 데이터 흐름은 fetchGames → useGames → Page → GameList → GameCard입니다. API 계층은 HTTP와 검증, Hook은 요청 상태, 페이지는 필터와 화면 상태, 컴포넌트는 표현과 사용자 이벤트를 맡습니다.
완성 기준은 “화면이 보임”이 아닙니다. 느린 요청, 빈 배열, 잘못된 URL, 키보드 탐색, 작은 화면, 새로고침, 프로덕션 빌드까지 확인해야 합니다.
4. 단계별 코드 예시
먼저 최종 구조를 정리합니다.
game-release-dashboard/
├─ public/data/games.json
├─ src/
│ ├─ api/games.ts
│ ├─ components/
│ │ ├─ FilterBar.tsx
│ │ ├─ GameCard.tsx
│ │ ├─ GameCard.module.css
│ │ └─ GameList.tsx
│ ├─ hooks/useGames.ts
│ ├─ pages/
│ │ ├─ HomePage.tsx
│ │ ├─ RankingsPage.tsx
│ │ ├─ GameDetailPage.tsx
│ │ └─ NotFoundPage.tsx
│ ├─ types/game.ts
│ ├─ App.tsx
│ ├─ App.module.css
│ └─ main.tsx
└─ package.json
요청은 App에서 한 번 수행해 여러 페이지에 전달합니다.
파일 경로: game-release-dashboard/src/App.tsx
import { NavLink, Route, Routes } from 'react-router';
import { useGames } from './hooks/useGames';
import { GameDetailPage } from './pages/GameDetailPage';
import { HomePage } from './pages/HomePage';
import { NotFoundPage } from './pages/NotFoundPage';
import { RankingsPage } from './pages/RankingsPage';
export default function App() {
const { games, status, retry } = useGames();
return (
<>
<header>
<strong>Release Radar</strong>
<nav aria-label="주요 메뉴">
<NavLink to="/">출시 정보</NavLink>
<NavLink to="/rankings">랭킹</NavLink>
</nav>
</header>
<Routes>
<Route
path="/"
element={<HomePage games={games} status={status} retry={retry} />}
/>
<Route path="/rankings" element={<RankingsPage games={games} />} />
<Route path="/games/:gameId" element={<GameDetailPage games={games} />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</>
);
}
랭킹은 원본을 변경하지 않고 계산합니다.
파일 경로: game-release-dashboard/src/pages/RankingsPage.tsx
import type { Game } from '../types/game';
import { GameList } from '../components/GameList';
export function RankingsPage({ games }: { games: Game[] }) {
const rankedGames = games.toSorted((a, b) => b.score - a.score);
return (
<main>
<h1>게임 랭킹</h1>
<GameList games={rankedGames} />
</main>
);
}
마지막으로 품질 명령을 실행합니다.
실행 위치: game-release-dashboard/
npm run lint
npm run build
npm run preview
git status
git add .
git commit -m "feat: complete game release dashboard"
5. 코드가 동작하는 이유
useGames를 App에서 한 번 호출하므로 페이지 이동 때마다 같은 요청 로직을 중복하지 않습니다. Route는 URL에 맞는 페이지를 선택하고 모든 페이지는 검증된 Game 배열을 받습니다. rankings의 toSorted는 새 배열을 반환해 props를 변경하지 않습니다.
6. 자주 하는 실수와 해결법
- 성공 경로만 확인: 로딩, 빈 데이터, 네트워크 오류, 잘못된 id와 경로를 일부러 재현합니다.
- 배포 base 경로 누락: 데이터 URL과 Vite base 설정을 배포 주소에 맞춥니다.
- 모바일에서만 메뉴를 숨기고 대체 탐색 없음: 모든 화면 폭에서 키보드로 이동 가능한 메뉴를 둡니다.
- lint 오류를 무시하고 커밋: lint와 build를 모두 통과한 상태를 기준으로 커밋합니다.
- README 없이 결과물만 제출: 목적, 기능, 실행 명령, 기술 선택, 개선점을 기록합니다.
7. 직접 실습
다음 완료 조건을 모두 충족한 저장소를 만드세요.
- 출시 정보 검색·플랫폼 필터·빈 결과가 동작한다.
- 랭킹이 점수 내림차순으로 표시된다.
- 상세 URL과 두 종류의 404가 동작한다.
- 로딩·오류·재시도 UI가 있으며 요청 취소를 처리한다.
- 320px부터 데스크톱까지 가로 넘침이 없고 키보드로 모든 기능을 쓸 수 있다.
- README에 스크린샷, 실행 방법, 폴더 구조, 기술적 판단 두 가지를 기록한다.
8. 이해 점검 질문 3개
- API 함수, 커스텀 Hook, 페이지, 표현 컴포넌트는 각각 어떤 책임을 가지나요?
- 최종 앱에서 반드시 수동으로 재현해야 할 실패 상태는 무엇인가요?
- 포트폴리오 README에 결과 화면 외에 어떤 판단 근거를 기록해야 하나요?
9. 핵심 요약
React, TypeScript, fetch, CSS, Router를 하나의 데이터 흐름으로 통합했습니다. 정상 화면뿐 아니라 실패·모바일·접근성·빌드까지 검증하면 혼자서 API 기반 반응형 웹사이트를 완성할 수 있습니다.
최종 프로젝트: 게임 출시 정보·랭킹 대시보드 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.