리액트에서 데이터 통신을 할 때는 axios 라이브러리나 fetch API를 사용하여 처리한다.
여러가지 편의 기능은 axios 라이브러리가 더 뛰어나지만, 별도의 라이브러리를 설치해서 사용해야 한다는 단점이 있다.
React에서 fetch API를 사용하여 데이터를 불러오는 것은 일반적인 비동기 작업이다.
--> 데이터를 불러오는 과정에서 React의 useEffect 훅을 자주 사용하여 컴포넌트가 렌더링되었을 때 데이터를 가져온다.
fetch API를 활용해서 TMBD에 있는 데이터를 받아와서 MOVIE LIST를 불러오는 Simple Movie 사이트를 생성해보자
movieAPI
export const API_KEY = '5f90a5ae964c1e681e56c236101a5b46';
export const BASE_PATH = 'https://api.themoviedb.org/3';
export interface IMovie {
id: number;
backdrop_path: string;
poster_path: string;
title: string;
overview: string;
vote_average: string;
release_date: string;
genre_ids: [];
}
export interface IGetMoviesResult {
dates: {
maximum: string;
minimum: string;
};
page: number;
total_pages: number;
total_results: number;
results: IMovie[];
}
export async function getMovies(): Promise<IGetMoviesResult> {
const response = await fetch(
`${BASE_PATH}/movie/now_playing?api_key=${API_KEY}&language=ko-KR`
);
if (!response.ok) {
throw new Error('현재 상영 중인 영화를 가져오지 못했습니다.');
}
return response.json();
}
export async function getTopRatedMovies(): Promise<IGetMoviesResult> {
const response = await fetch(
`${BASE_PATH}/movie/top_rated?api_key=${API_KEY}&language=ko-KR`
);
if (!response.ok) {
throw new Error('Top Rated 영화를 가져오지 못했습니다.');
}
return response.json();
}
우선 나는 interface로 받아올 수 있는 데이터의 타입을 지정을 해준뒤,
그 타입을 fetch 함수에 연결해서, 데이터를 가져왔을 때, 쉽게 사용할 수 있도록 해주었다.
이렇게 fetch api로 불러온 getMovies(), getTopRatedMovies()를 가져와
MovieCopoment
import { useEffect, useState } from 'react';
import { IMovie } from '../api/movieApi';
interface NowPlayMovieProps {
fetchFunction: () => Promise<{ results: IMovie[] }>; // fetchFunction은 비동기 함수로 정의
}
export default function MovieComponent({ fetchFunction }: NowPlayMovieProps) {
const [movies, setMovies] = useState<IMovie[]>([]);
useEffect(() => {
const fetchMovies = async () => {
try {
const data = await fetchFunction();
setMovies(data.results);
} catch (error) {
console.error('Failed to fetch movies:', error);
}
};
fetchMovies();
}, [fetchFunction]);
return (
<>
<div className="flex flex-wrap -mx-2 w-full">
{movies.map((movie) => (
<div
key={movie.id}
className="w-1/4 px-2 mb-6 hover:scale-105 duration-200 transition-transform ease-in-out"
>
<img
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
alt={movie.title}
className="rounded-md w-full"
/>
<div className="flex justify-between items-center font-bold mt-4 mb-2 text-lg">
<h4 className="truncate">{movie.title}</h4>
</div>
<div className="flex justify-between items-center text-sm text-gray-200">
<div className="flex items-center gap-2 font-bold">
<h1>⭐️</h1>
<span className="text-yellow-500">{movie.vote_average}</span>
</div>
<span className="text-yellow-500 font-bold">
{movie.release_date.slice(0, 4)}
</span>
</div>
</div>
))}
</div>
</>
);
}
를 생성해서
fetch API를 불러와서 데이터를 뿌려줄 수 있는 movie component를 생성해 주었다.
여기서 확인할 수 있는 것은 ?
-> 어떠한 movie 데이터를 불러와도 컴포넌트를 재사용할 수 있다는 점이다!
App.tsx
...
<h2 className="text-[36px] font-bold mb-8">Now Play Movie</h2>
<div className="">
<MovieComponent fetchFunction={getMovies} />
</div>
<h2 className="text-[36px] font-bold mb-8">Top Rated Movie</h2>
<div className="">
<MovieComponent fetchFunction={getTopRatedMovies} />
</div>
...
그리고 App.tsx 에서 컴포넌트를 불러서 사용하면 끝!
강사님은
Api.tsx
const apiUrl = "https://api.themoviedb.org/3/movie";
export const nowPlayingUrl = `${apiUrl}/now_playing`;
export const popularUrl = `${apiUrl}/popular`;
export const upComingUrl = `${apiUrl}/upcoming`;
export const initailData = {
results: [],
};
export const options = {
method: "GET",
headers: {
accept: "application/json",
Authorization:
"Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIzZDZjODUwZmVkZDY0YTUwN2U1MWNmYjIzMzVmMzA1YyIsIm5iZiI6MTcyNzQwOTg0Ny42NjgwOTgsInN1YiI6IjYxMjViZDU5OGMzMTU5MDA2Mjk4OTUzNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.5_O-IqktGsssq312RHhJMeJhITPtb-FXYalzHTLrgZU",
},
};
이렇게 options를 통해서 데이터를 불러온 뒤,
App.tsx에서 각 movie의 결과값과, 타입을 지정해 준 뒤
const {
data: { results: nowPlaying },
isLoading: isLoadingNowplaying,
isError: isErrorNowPlaying,
} = useFetch<MovieDataType>(nowPlayingUrl, initailData, options);
import MovieItem from "./MovieItem";
요런 식으로 movie list를 뿌려주었다.
export default function MovieArea({
movies,
title,
}: {
movies: MovieType[];
title: string;
}) {
return (
<>
{/* 무비 에어리어 */}
<article className="bg-black py-10 px-4 xs:px-0">
<section className="container mx-auto text-white">
<span className="text-yellow-600">ONLINE STREAMING</span>
<h2 className="text-[36px] font-bold mb-8">{title}</h2>
<div className="grid grid-cols-1 xs:grid-cols-2 md:grid-cols-4 gap-6 sm:px-0">
{movies.map((movie) => (
<MovieItem key={movie.id} {...movie} />
))}
</div>
</section>
</article>
</>
);
}
리액트에서 라우터는 react-router-dom 패키지를 사용해서 처리한다.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./assets/css/index.css";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
]);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
npm init -y
npm install react@latest next@latest react-dom@latest
"start": "next dev",
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 3기 과정(B-log) 리뷰로 작성 되었습니다.
#유데미 #udemy #웅진씽크빅 #스나이퍼팩토리 #인사이드아웃 #미래내일일경험 #프로젝트캠프 #부트캠프 #React #리액트프로젝트 #프론트엔드개발자양성과정 #개발자교육과정