10. Category, ErrorPage 완성하기
카테고리 페이지네이션, 에러 페이지를 완성하고 프로젝트를 점검한다
코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.
구문
💡TIP
Gemini CLI로 구현하기 — Category 페이지네이션 & ErrorPage
- 프롬프트:
gemini "src/components/Category.jsx와 ErrorPage.jsx를 작성해줘. Category는 useParams로 type(now_playing, popular, top_rated)을 받아서 TMDB API movie/[type]?page=[page]를 호출하고, 이전/다음 버튼으로 페이지네이션을 구현해줘. 최대 페이지는 [최대 페이지 수]으로 제한해줘. ErrorPage는 404 메시지와 홈으로 돌아가기 Link를 포함해줘."- 사용 가이드:
[최대 페이지 수]를 원하는 숫자(기본: 20)로 바꾼다. 카테고리 한글 이름이 필요하면 프롬프트에 "카테고리 키를 한글로 변환하는 매핑 객체도 추가해줘"를 덧붙인다.
- 사용 가이드:
1. 이번 편에서 만들 것
| 컴포넌트 | 기능 |
|---|---|
ErrorPage | 404 등 존재하지 않는 URL 처리 |
Category | 카테고리별 영화 목록 + 페이지네이션 |
2. ErrorPage — 에러 페이지
src/components/ErrorPage.jsx 파일을 열고 임시 코드를 모두 지운 뒤 아래로 교체한다. 존재하지 않는 URL로 접속했을 때 보여주는 페이지이다.
import { Link } from "react-router";
export function ErrorPage() {
return (
<div className="bg-black min-h-screen flex flex-col items-center justify-center text-center px-6">
<h1 className="text-8xl font-bold text-yellow-400">404</h1>
<p className="text-white text-2xl mt-4">페이지를 찾을 수 없습니다</p>
<p className="text-gray-400 mt-2">요청하신 페이지가 존재하지 않거나 이동되었습니다.</p>
<Link
to="/"
className="mt-8 bg-yellow-400 text-black px-6 py-3 rounded-lg font-bold hover:bg-yellow-300 transition-colors"
>
홈으로 돌아가기
</Link>
</div>
);
}
| 줄 | 설명 |
|---|---|
| 1 | Link로 홈 페이지 이동 버튼을 만든다. |
| 6 | 상태 코드 404를 고정으로 표시한다. |
| 11-15 | to="/" — 버튼 클릭 시 홈으로 이동한다. |
3. Category — 카테고리 + 페이지네이션
src/components/Category.jsx 파일을 열고 임시 코드를 모두 지운 뒤 작성한다. 이 컴포넌트는 "인기 영화", "현재 상영작" 등 카테고리별 영화 목록을 보여주는 페이지이다. 한 페이지에 20편씩 표시하고, "이전"/"다음" 버튼으로 책장을 넘기듯 다른 페이지를 볼 수 있다.
import { useState, useEffect } from "react";
import { useParams } from "react-router";
import { Card } from "./Card.jsx";
import api from "../api/axios";
import { Spinner, Container, Button } from "./UI.jsx";
const TITLES = {
now_playing: "현재 상영작",
popular: "인기 영화",
top_rated: "최고 평점",
};
export function Category() {
const { type } = useParams();
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [data, setData] = useState({ type: "", page: 0, movies: [] });
useEffect(() => {
api
.get(`movie/${type}`, { params: { page: page } })
.then((res) => {
let pages = res.data.total_pages;
if (pages > 20) {
pages = 20;
}
setTotalPages(pages);
setData({ type: type, page: page, movies: res.data.results.filter((m) => m.poster_path) });
})
.catch(() => {
setData({ type: type, page: page, movies: [] });
});
}, [type, page]);
const loading = data.type !== type || data.page !== page;
| 줄 | 설명 |
|---|---|
| 7-11 | TITLES 객체로 영어 카테고리 키를 한글 제목으로 변환한다. |
| 14 | useParams()로 URL에서 :type 값(popular, now_playing, top_rated)을 꺼낸다. |
| 15 | page — 현재 페이지 번호이다. "이전"/"다음" 버튼으로 변경한다. |
| 21 | { params: { page: page } }로 TMDB에 페이지 번호를 전달한다. |
| 24-25 | TMDB가 반환하는 total_pages가 너무 크면 20페이지로 제한한다. |
| 33 | [type, page] — type이나 page가 바뀔 때마다 API를 다시 호출한다. |
| 35 | 현재 type+page와 data의 type+page가 다르면 로딩 중이다. |
4. 전체 코드
import { Link } from "react-router";
export function ErrorPage() {
return (
<div className="bg-black min-h-screen flex flex-col items-center justify-center text-center px-6">
<h1 className="text-8xl font-bold text-yellow-400">404</h1>
<p className="text-white text-2xl mt-4">페이지를 찾을 수 없습니다</p>
<p className="text-gray-400 mt-2">요청하신 페이지가 존재하지 않거나 이동되었습니다.</p>
<Link
to="/"
className="mt-8 bg-yellow-400 text-black px-6 py-3 rounded-lg font-bold hover:bg-yellow-300 transition-colors"
>
홈으로 돌아가기
</Link>
</div>
);
}