api - themoviedb
우선 apis 폴더를 만들어서,
그 안에 movie + serires = Movi_Ser_Api.ts 파일을 하나 만들고, 검색 기능 전용인 SearchApi.ts 를 만들었다
movie 와 tv(series) api 의 interface가 거의 한 두개 차이로 다 똑같았기 때문에 영화와 TV(시리즈)를 한데 묶음.
그런데 movie 나 tv 둘다 한 개가 아닌 현재 상영중인, 유명한, 상영예정 등인 등등 카테고리가 여러개를 불러와야했다.
Movie
Tv(Serise)
고민하다가, fetch
시 Popular 나 upcoming 이 들어가야하는 자리에,
대신 category 로 변수를 부여하고 useQuery
사용 시 인자에 카테고리 넣어주기로함
// apis/Movi_Ser_Api.ts
interface IResult { <<<--- 영화와 tv에 공통으로 사용해주었다
// 공통
id: number;
backdrop_path: string;
overview: string;
release_date: string;
poster_path: string;
vote_average: string;
// ------ seires 정보
name: string;
// ------ movies 정보
title?: string;
}
export function getMovies(category: string) {
return fetch(
`${BASE_PATH}/movie/${category}?api_key=${API_KEY}&language=en-US&page=1`
).then(response => {
return response.json();
});
}
.
.
.
// -Movie.tsx
function Home() {
// Nowplaying API
const { data: now_data, isLoading: now_Loading } = useQuery<IGetResult>(
["movies", "now"],
() => getMovies("now_playing")
);
// Popular(Trending Now) API
const { data: pop_data, isLoading: pop_Loading } = useQuery<IGetResult>(
["movies", "popular"],
() => getMovies("popular")
);
.
.
.
data, title, category
를 받아오면된다.// Movie.tsx
{/* -- 슬라이드 영역 -- */}
<MovieSlider
category="now_playing"
title="Now Playing"
data={now_data}
/>
<MovieSlider
category="top_rated"
title="High Rated"
data={top_data}
/>
<MovieSlider
category="popular"
title="Trending Now"
data={pop_data}
/>
<MovieSlider category="upcoming" title="Coming soon" data={up_data} />
</>
// MovieSlider.tsx
const MovieSlider: React.FC<IBannerProps> = React.memo(
({ category, data, title }) => {
...
}
useState
를 사용하여 슬라이더에 애니메이션을 넣어줬다.index
값으로 없어졌다 사라지니, AnimatePresence
도 넣어줬다!const MovieSlider: React.FC<IBannerProps> = React.memo(
({ category, data, title }) => {
// - 슬라이더 내에 한번에 보여주고싶은 영화의 개수
const offset = 6;
// - 슬라이드 다음, 이전으로 넘기기위한 인덱스
const [index, setIndex] = useState(0);
// 슬라이드 애니메이션 방향 설정
const [isNext, setIsNext] = useState(true);
// - leaving : 슬라이드 내에 이동중인 애니메이션이 끝났는지 확인
const [leaving, setLeaving] = useState(false);
const toggleLeaving = () => setLeaving(prev => !prev);
return (
...
<H.Slider>
<H.Slider_Title>{title}</H.Slider_Title>
<AnimatePresence
custom={isNext}
initial={false}
onExitComplete={toggleLeaving}
>
<H.Row
key={category + index}
variants={RowVariants}
initial="hidden"
animate="visible"
exit="exit"
custom={isNext}
transition={{ type: "tween", duration: 1 }}
>
{resultsData &&
resultsData.map(movie => (
<H.RowBox
onClick={() => onBoxClicked(movie.id)}
key={category + movie.id}
variants={BoxHoverVariants}
initial="initial"
whileHover="hover"
transition={{ type: "tween" }}
bgphoto={makeImagePath(...)} >
<H.RowBox_Info variants={H.infoVariants}>
<h4>{movie.title}</h4>
</H.RowBox_Info>
</H.RowBox>
))}
</H.Row>
</AnimatePresence>
<H.prevBtn onClick={prevIndex}>
<svg xmlns="...">
</svg>
</H.prevBtn>
<H.nextBtn onClick={nextIndex}>
<svg xmlns="..." />
</svg>
</H.nextBtn>
</H.Slider>
const navigate = useNavigate();
const onBoxClicked = (movieId: number) => {
navigate(`/movies/${movieId}`);
};
const bigMovieMatch: PathMatch<string> | null =
useMatch("/movies/:movieId");
useNavigate
로 movieId
값을 담아 보낸 특정 URL 로 이동시킨 뒤에,
그 URL 로 잘 이동되었는지 확인하기 위한 useMatch
!
{/* -- 모달 영역 -- */}
{bigMovieMatch ? (
<>
<MovieDetail
id={bigMovieMatch.params.movieId!}
category={category}
/>
</>
) : null}
// -MovieDetail.tsx
interface IDetailProps {
category?: string;
id: string;
}
function MovieDetail({ category, id }: IDetailProps) {
const navigate = useNavigate();
// movie detail API
const { data: detailData, isLoading: detailLoading } = useQuery<IGetDetail>(
["movie", `${category}_detail`],
() => getMovieDetail(id)
);
// movie credit API
const { data: creditData, isLoading: creditLoading } = useQuery<IGetCredit>(
["movie", `${category}_credit`],
() => getMovieCredit(id)
);
const navigate = useNavigate();
이거는 x 버튼 클릭하면 뒤로가기 되는 기능때문에 넣어놨다.<M.Poster_prevBtn onClick={() => navigate(-1)}>✕</M.Poster_prevBtn>
이런식으로 사용해줬음.
{detailData?.genres.slice(0, 3).map((genre, index) => (
<p id="genrs" key={genre.id}>
{genre.name}
{index !== detailData.genres.length - 1 && " · "}
</p>
))}
아무튼 완성!
넷플릭스랑 최대한 비슷하게 만들고싶어서 엄청 심혈을 기울였다능,,!