지이이이잉이이인짜 지이이이이잉이이ㅣㄴ짜
오래 걸렸고 세상 오류 다 만난 부분 ... 😂 인생 진짜 엄청 오류가 많이 났ㅇ었다..
돋보기를 누르면 요렇게 쓩 하고 나오게 된다. 
저 검색창에 검색을 하게 되면

이렇게 Movie, Tv 따로 검색 결과에 대한 작품을 보여주게 된다
<Search onSubmit={handleSubmit(onValid)}>
<motion.svg
onClick={toggleSearch}
animate={{ x: searchOpen ? -180 : 0 }}
transition={{ type: 'linear' }}
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clipRule="evenodd"
></path>
</motion.svg>
<Input
{...register('keyword', {
required: true,
minLength: 2,
})}
placeholder="Search for movie or tv show ..."
animate={{ scaleX: searchOpen ? 1 : 0 }}
transition={{ type: 'linear' }}
/>
</Search>
돋보기 이미지를 svg 형태로 가져온 뒤, motion을 주어서 animation을 주었다.
-- > 사용자가 검색창을 토글할 때 돋보기 아이콘과 검색 입력 상자의 애니메이션을 보여준다.
돋보기 아이콘은 클릭 시 X 축으로 이동하여 왼쪽으로 이동하고, 입력 상자는 나타나거나 숨겨지는 효과를 보여주게 된다.
export function getSearchs(keyword: string) {
return fetch(
`${BASE_PATH}/search/multi?query=${keyword}&include_adult=false&language=ko-KR&api_key=${API_KEY}&page=1`
).then((response) => response.json());
}
Search api는 Keyword를 받아오게 된다.
여기서 keyword는 ? --> movie, tv를 나타낸다
const Search = () => {
const location = useLocation();
...
const keyword = new URLSearchParams(location.search).get('keyword');
const { data, isLoading } = useQuery<IGetSearchResult>(
['search', keyword],
() => getSearchs(keyword || '')
);
const tvData = data?.results.filter(
(item: ISearch) => item.media_type === 'tv'
);
const movieData = data?.results.filter(
(item: ISearch) => item.media_type === 'movie'
);
const [videoData, setVideoData] = useState<IGetVideosResult | null>(null);
const clickedMovie =
bigMovieMatch?.params.movieId &&
data?.results.find(
(movie) => movie.id + '' === bigMovieMatch.params.movieId
);
const clickedTv =
bigTvShowMatch?.params.tvId &&
data?.results.find((tv) => tv.id + '' === bigTvShowMatch.params.tvId);
const onOverlayClicked = () => {
navigate(`/search?keyword=${keyword}`);
};
return (
<>
{isLoading ? (
<Loading />
) : (
<Wrapper>
<SectionTitle>{keyword} 검색 결과를 찾았어요!</SectionTitle>
{data && (
<>
<SearchSlider title="Movie" search={movieData || []} />
<SearchSlider title="Tv" search={tvData || []} />
</>
)}
//다른 BigMovie 코드와 비슷함(설명 생략)
</Wrapper>
)}
</>
);
};
export default Search;
먼저, getSearch() 함수에 keyword를 넘겨주어야 하는데, 어디서 가져와야 하지 ?
-->
const keyword = new URLSearchParams(location.search).get('keyword');를 통해서 URLSearchParams 객체를 이용하여 현재 페이지 URL의 쿼리 파라미터에서 'keyword' 값을 추출한다.
이렇게 추출한 값을 getSearch(keyword) 넘겨주면 된다.
const tvData = data?.results.filter(
(item: ISearch) => item.media_type === 'tv'
);
const movieData = data?.results.filter(
(item: ISearch) => item.media_type === 'movie'
);
를 통해서 알 수 있듯이, getSearch()로 가져온 data 안에는 tv와 movie가 있어 tvData와 movieData를 따로 받아왔으며, 이 정보를 <SearchSlider/>에게 넘겨주었다!
const SearchSlider = ({ title, search }: ISearchSliderProps) => {
const location = useLocation();
const navigate = useNavigate();
const keyword = new URLSearchParams(location.search).get('keyword');
const [index, setIndex] = useState(0);
const [direction, setDirection] = useState(true);
const maxItemsToShow = 6;
const increaseIndex = () => {
if (search) {
const totalMovies = search.length;
const maxIndex = Math.floor(totalMovies / maxItemsToShow) - 1;
if (totalMovies <= maxItemsToShow) {
return;
}
setDirection(true);
setIndex((prevIndex) => (prevIndex === maxIndex ? 0 : prevIndex + 1));
}
};
const decreaseIndex = () => {
if (search) {
const totalMovies = search.length;
const maxIndex = Math.ceil(totalMovies / maxItemsToShow) - 1;
setDirection(false);
setIndex((prevIndex) => (prevIndex === 0 ? maxIndex : prevIndex - 1));
}
};
const onBoxClicked = (itemId: number, mediaType: string) => {
navigate(`/search/${mediaType}/${itemId}?keyword=${keyword}`);
};
return (
<>
<Section>
<TitleArea>
<SectionTitle>{title}</SectionTitle>
<ButtonWrapper>
<Button onClick={decreaseIndex}>이전</Button>
<Button onClick={increaseIndex}>다음</Button>
</ButtonWrapper>
</TitleArea>
<AnimatePresence initial={false} custom={direction}>
<Row
key={index}
variants={rowVariants}
initial="hidden"
animate="visible"
exit="exit"
custom={direction}
transition={{ type: 'tween', duration: 1 }}
>
{search
.slice(
index * maxItemsToShow,
index * maxItemsToShow + maxItemsToShow
)
.map((item: ISearch) => (
<Box
transition={{ type: 'tween' }}
variants={BoxVariants}
whileHover="hover"
initial="normal"
exit="exit"
layoutId={item.id + ''}
key={item.id}
bgPhoto={makeImagePath(item.backdrop_path)}
onClick={() => onBoxClicked(item.id, item.media_type)}
/>
))}
</Row>
</AnimatePresence>
</Section>
</>
);
};
export default SearchSlider;
<SearchSlider title="Movie" search={movieData || []} />
<SearchSlider title="Tv" search={tvData || []} /> 이렇게 넘겨 받은
search 를 통해서 화면에 뿌려주면 된다.
search를 통해서 Box를 구성하는데(movie, tv) 여기서 누른 영화 혹은 tv의 상세 정보를 얻기 위해, onClick 함수를 통해서 누른 item의 id와 media_type을 넘겨준다.
const onBoxClicked = (itemId: number, mediaType: string) => {
navigate(`/search/${mediaType}/${itemId}?keyword=${keyword}`);
};
여기서 살펴볼 수 있듯이 mediaType이 movie라면, movie로 링크가 열릴 것이고, tv라면 tv로 열릴 것이다!
거의 이틀 걸렸던 것 같다
데이터를 불러오는 것부터 조금 고생을 하고,
처음에는 Movie따로 검색을 불러오고, 화면에 뿌려주고, 영화를 보여주고 했는데 그러다보니 코드도 너무 길어지고 진짜 한 600은 넘은 것 같아서
Movie, tv를 동시에 불러오고, mediaType으로 데이터를 나눌 수 (?) 있도록 해보면서 코드를 분리하고 줄여보았다..
사실 엄청 맘에 들진 않지만, 이정도면 어느정도 깔끔해진 것 같아서.. 나름 만족 !! 🤣