Web

영화 웹사이트 - 2단계 (Router, Pagination)

sungjae0309 2026. 4. 3. 15:09

구조 수정

1단계에서 만든 구조를 벗어나 관심사 분리를 위해 구조 재설계

  • src/pages/ : 각 메뉴(인기, 상영 중 등)에 맞는 페이지 컴포넌트 배치
  • src/components/ : 공통 레이아웃(Layout), 네비게이션(Navbar), 로딩 상태(LoadingsPinner) 등을 재사용 가능한 컴포넌트 단위로 분리
  • src/api/ : Axios인스턴스 통해 API통신 모듈화 

 

구현 내용 

1. React Router 이용하여 멀티 페이지 구현 

  • 적용코드: src/main.tsx
  • 흐름: 사용자가 상단에 있는 메뉴 클릭 > URL 변경 > Layout 컴포넌트 내부의 <Outlet /> 자리에 해당 페이지 컴포넌트가 동적으로 변함
const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />, // 공통 레이아웃 적용
    children: [
      { index: true, element: <HomePage /> }, // 주소가 '/' 일 때
      { path: 'movies/popular', element: <PopularPage /> },
      { path: 'movies/now-playing', element: <NowPlayingPage /> },
      { path: 'movies/:movieId', element: <MovieDetailPage /> },
      { path: 'movies/top-rated', element: <TopRatedPage /> },
      { path: 'movies/upcoming', element: <UpcomingPage /> },
    ],
  },
]);

 

 

2. 로딩 및 에러 처리

  • 상단 바 누른 후 데이터 로딩되는 동안 사용자에게 로딩하는 것을 알리기 위해 isLoading상태와 스피너 사용
  • 적용코드: src/components/LoadingSpinner.tsx
  • useEffect 내에서 API 호출 직전 setIsLoading(true)를 설정
const LoadingSpinner = () => {
  return (
    <div className="flex justify-center items-center min-h-[50vh]">
      <div className="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-pink-500"></div>
    </div>
  );
};

export default LoadingSpinner;

 

 

 

3. 페이지네이션

  • TMDB API의 page 파라미터 활용해서 대량의 데이터를 페이지 단위로 불러오도록 구현 
const [page, setPage] = useState(1);

useEffect(() => {
  const fetchMovies = async () => {
    // API 요청 시 page 번호 전달
    const response = await api.get(`/movie/popular?language=ko-KR&page=${page}`);
    setMovies(response.data.results);
  };
  fetchMovies();
}, [page]); // page 상태가 바뀔 때마다 useEffect가 다시 실행

 

 

구현 화면