본문으로 건너뛰기
프론트엔드 기초집LESSON 24

최종 프로젝트: 게임 출시 정보·랭킹 대시보드

난이도초급
예상 시간35분
선수지식이전 강의

24강. 최종 프로젝트: 게임 출시 정보·랭킹 대시보드

1. 이번 강의에서 해결할 문제​

지금까지 만든 컴포넌트와 기능을 실제 포트폴리오로 정리해야 합니다. 데이터 요청부터 반응형 페이지, 오류 처리, 라우팅까지 하나의 일관된 제품 흐름으로 통합합니다.

2. 학습 목표​

  • 기능별 폴더와 데이터 흐름을 최종 정리한다.
  • 출시 정보, 랭킹, 상세, 404 화면을 완성한다.
  • 접근성·반응형·빌드·Git 품질 기준으로 결과물을 점검한다.

3. 핵심 개념​

최종 앱의 데이터 흐름은 fetchGames → useGames → Page → GameList → GameCard입니다. API 계층은 HTTP와 검증, Hook은 요청 상태, 페이지는 필터와 화면 상태, 컴포넌트는 표현과 사용자 이벤트를 맡습니다.

완성 기준은 “화면이 보임”이 아닙니다. 느린 요청, 빈 배열, 잘못된 URL, 키보드 탐색, 작은 화면, 새로고침, 프로덕션 빌드까지 확인해야 합니다.

가장 작은 수직 조각부터 닫기​

처음부터 네 페이지를 동시에 만들지 않습니다. games.json의 한 항목 → fetchGames 검증 → useGames 상태 → HomePage 한 카드 → 카드 링크가 브라우저에서 끝까지 보이는 조각을 먼저 완성합니다. 이 조각이 정상·로딩·오류를 처리한 뒤 같은 검증된 배열을 랭킹과 상세 페이지에 확장합니다.

계층입력출력·책임
fetchGamesHTTP 응답검증된 Game[] 또는 오류
useGamesAPI 함수loading/success/error, games, retry
페이지요청 상태와 배열빈 결과·오류·페이지별 계산
표현 컴포넌트표시할 Game과 이벤트접근 가능한 카드·버튼·링크

상태를 한 단계씩 통과시키면 오류가 HTTP, 상태 관리, 필터, 렌더 중 어디서 생겼는지 찾을 수 있습니다.

4. 단계별 코드 예시​

시작 전 상태와 구현 순서​

이전 강의의 React·TypeScript 프로젝트가 개발 서버에서 열리고, npm.cmd run lint가 현재 변경의 기준 결과를 내야 합니다. public/data/games.json에는 id가 겹치지 않는 최소 한 항목이 있고 이미지·상세 링크의 실제 경로를 확인합니다.

구현 순서는 다음과 같습니다.

  1. 한 게임의 요청·검증·카드 렌더를 닫습니다.
  2. 로딩, 빈 배열, 잘못된 JSON, 재시도를 재현합니다.
  3. 검색·필터를 추가하고 원본 배열 유지 여부를 확인합니다.
  4. 랭킹과 상세 Route를 연결합니다.
  5. 잘못된 game id와 알 수 없는 경로를 각각 처리합니다.
  6. 모바일·키보드·빌드를 통과시킵니다.

먼저 최종 구조를 정리합니다.

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.cmd run lint
npm.cmd run build
npm.cmd run preview
git status
git add .
git commit -m "feat: complete game release dashboard"

브라우저에서 확인할 구체 결과​

시나리오조작기대 결과
정상 응답/ 새로고침로딩 뒤 JSON 항목 수만큼 카드 표시
검색 없음존재하지 않는 제목 입력빈 결과 안내와 필터 초기화 동작
랭킹/rankings 이동score 내림차순, 동점 정책 일관
없는 게임/games/not-found 직접 입력게임 상세 없음 안내와 목록 링크
없는 경로/unknown 입력사이트 404 페이지
요청 실패JSON 이름을 임시로 잘못 지정오류 문구와 재시도 버튼, Console의 원인
모바일320px에서 Tab 이동가로 넘침 없이 메뉴·카드·버튼에 초점 표시

preview 주소를 연 뒤 Network에서 games.json의 상태 200과 응답 크기를 확인하고, 실패 실험 후 경로를 반드시 원래대로 돌립니다.

5. 코드가 동작하는 이유​

useGames를 App에서 한 번 호출하므로 페이지 이동 때마다 같은 요청 로직을 중복하지 않습니다. Route는 URL에 맞는 페이지를 선택하고 모든 페이지는 검증된 Game 배열을 받습니다. rankings의 toSorted는 새 배열을 반환해 props를 변경하지 않습니다.

한 번 받은 배열은 App에서 페이지로 내려가고, 랭킹 페이지는 복사된 배열만 정렬합니다. 따라서 랭킹 방문 뒤 홈의 원래 순서가 바뀌지 않습니다. URL은 페이지 선택 상태가 되므로 상세 주소를 새로고침해도 같은 gameId로 항목을 다시 찾습니다.

6. 자주 하는 실수와 해결법​

  • 성공 경로만 확인: 로딩, 빈 데이터, 네트워크 오류, 잘못된 id와 경로를 일부러 재현합니다.
  • 배포 base 경로 누락: 데이터 URL과 Vite base 설정을 배포 주소에 맞춥니다.
  • 모바일에서만 메뉴를 숨기고 대체 탐색 없음: 모든 화면 폭에서 키보드로 이동 가능한 메뉴를 둡니다.
  • lint 오류를 무시하고 커밋: lint와 build를 모두 통과한 상태를 기준으로 커밋합니다.
  • README 없이 결과물만 제출: 목적, 기능, 실행 명령, 기술 선택, 개선점을 기록합니다.

7. 직접 실습​

실습 목표​

한 게임의 요청부터 카드·상세까지 닫은 뒤 랭킹과 실패 상태를 확장해 배포 빌드에서 검증합니다.

시작 전 상태​

프로젝트 루트, Node 버전, 현재 lint·build 결과와 games.json 한 항목을 기록합니다. 기존 기능을 한꺼번에 고치지 말고 첫 수직 조각의 파일만 엽니다.

1단계: 따라 하기 — 한 게임 수직 조각​

한 항목을 fetch하고 검증된 Game[]를 Hook과 HomePage를 거쳐 카드 한 개로 표시합니다. 카드 링크로 상세 페이지에 이동하고 새로고침 뒤 같은 제목을 확인합니다.

2단계: 값과 실패 바꿔 보기​

점수를 바꿔 랭킹 순서 변화를 확인합니다. JSON 경로 또는 데이터 타입을 임시로 잘못 만들어 오류·재시도를 확인하고 원상 복구합니다. 빈 배열에서는 빈 결과 안내가 보여야 합니다.

3단계: 직접 확장​

검색·플랫폼 필터, 랭킹, 상세, 두 종류의 404를 연결합니다. 요청 중 페이지를 떠났을 때 취소된 요청이 상태를 덮어쓰지 않는지도 확인합니다.

4단계: 스스로 확인​

  • 랭킹 방문 뒤 홈 배열 순서가 유지되나요?
  • 320px와 키보드 탐색에서 모든 기능을 사용할 수 있나요?
  • Network 404와 Console 오류가 최종 상태에 남지 않나요?
  • lint와 build가 모두 성공하나요?
  • README에 실행 방법·구조·기술 판단 두 가지가 있나요?

막혔을 때​

증상먼저 확인할 위치해결 방향
카드가 계속 로딩Network와 useGames 상태 전이응답 완료·예외·cleanup 경로를 분리합니다.
랭킹 뒤 홈 순서 변경정렬 호출원본 sort 대신 새 배열을 반환하는 정렬을 사용합니다.
상세 새로고침에서 없음Route param과 데이터 준비 시점로딩 완료 뒤 id를 찾고 없음 상태를 구분합니다.
배포에서 JSON 404Vite base와 데이터 URLpreview와 실제 base 기준 URL을 확인합니다.

8. 이해 점검 질문 3개​

  1. 잘못된 JSON 타입이 카드까지 전달됐습니다. API 함수·Hook·페이지 중 어느 경계에서 차단해야 하며 각 계층은 어떤 상태를 전달해야 하나요?
  2. 랭킹 페이지를 방문한 뒤 홈 카드 순서까지 바뀌었습니다. 어떤 배열 연산과 props 규칙을 확인해야 하나요?
  3. 개발 서버는 되지만 배포 preview에서 games.json이 404입니다. 어떤 URL·base·Network 증거를 순서대로 확인하겠나요?

9. 핵심 요약​

React, TypeScript, fetch, CSS, Router를 하나의 데이터 흐름으로 통합했습니다. 정상 화면뿐 아니라 실패·모바일·접근성·빌드까지 검증하면 혼자서 API 기반 반응형 웹사이트를 완성할 수 있습니다.

MINI QUIZ

최종 프로젝트: 게임 출시 정보·랭킹 대시보드 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“최종 프로젝트: 게임 출시 정보·랭킹 대시보드” 내용을 실제 작업에 적용한 설명으로 가장 알맞은 것은 무엇인가요?
  2. 문제 2“최종 프로젝트: 게임 출시 정보·랭킹 대시보드”에서 ‘모바일에서만 메뉴를 숨기고 대체 탐색 없음’ 문제가 생겼습니다. 가장 알맞은 진단 또는 대응은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

24강. 최종 프로젝트: 게임 출시 정보·랭킹 대시보드 미완료 상태