카테고리 화면
카테고리에는 로맨스, 판타지/SF, 일상 장르 3가지가 있다.
여기 있는 작품들은 다 외부 API가 아니라 내가 DB로 등록해둔 것을 가져왔다.
약간 노가다긴 했는데 아는 작품을 슬쩍슬쩍 끼워넣으면서 재미있었다. 🤤
이래서 관심 있는 분야로 개발하는 게 재밌다는 거군...
작품 DB에서 특정 장르 작품만 필터링 해서 캐러셀로 보여주기
이전 게시물과 똑같이 Slick 라이브러리를 사용했기 때문에 그냥 filter로 장르만 걸러주면 된다.
슬라이드, 슬라이드 안에 들어갈 이미지들, 양쪽 화살표, 슬라이드의 세팅을 만들어준다.
특히 슬라이드 안에 들어갈 이미지는 useState로 빈 배열을 만든 후 각 장르별로 번호를 붙여 넣어줬다.
아래와 같이 모든 영화를 요청해 데이터를 받아오고, 장르가 로맨스인 것만 추려서 배열 안에 넣어준다.
다른 장르들도 이렇게 걸러주면 된다.
useEffect(() => {
axios
.get(
$`{serverURL/movies/all}`
)
.then((response) => {
const slideData = response.data.map((item) => ({
id: item.id,
imageSrc: item.accessKey,
title: item.title,
genre: item.genre,
createdDate: item.createdDate,
explanation:
item.explanation.length > 238
? item.explanation.slice(0, 238) + '...'
: item.explanation,
}));
// "로맨스" 장르인 영화만 필터링하여 slide1에 설정
const RomanceMovies = slideData.filter(
(movie) => movie.genre === '로맨스'
);
setSlide1(RomanceMovies);
})
.catch((error) => {
console.error('Error:', error);
});
}, []);
장르별로 slide1, slide2, slide3 이렇게 이름을 붙여놨는데 (좋은 변수명은 아니다.) 각 캐러셀마다 이 배열을 map 해주면 된다.
<Slider {...settings}>
{slide1.map((slide) => (
<div key={slide.id}>
<Slide
id={slide.id}
imageSrc={slide.imageSrc}
title={slide.title}
genre={slide.genre}
createdDate={slide.createdDate}
explanation={slide.explanation}
/>
</div>
))}
</Slider>
검색 화면
이런 식으로 검색어 input에 콘텐츠 제목의 일부를 입력하면 실시간으로 검색이 된다.
해당하는 콘텐츠가 있을 경우 이렇게 하나씩 포스터가 노출된다. (크기가 너무 아쉽다...)
포스터에 hover 하면 밝기가 50%로 줄어들면서 제목이 노출된다.
포스터를 클릭하면 모달이 나온다.
검색 결과에 해당되는 콘텐츠가 없으면 결과가 없다는 문구가 뜬다.
찾고 싶은 작품 제목을 input에 입력하면 useDebounce로 실시간 검색
import axios from 'axios';
import React, { useState, useEffect, useRef } from 'react';
import Modal from './Modal';
const Search = () => {
const [searchTerm, setSearchTerm] = useState('');
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('');
const [searchResults, setSearchResults] = useState([]);
const [hoveredMovie, setHoveredMovie] = useState(null);
const debounceTimerRef = useRef(null);
useEffect(() => {
clearTimeout(debounceTimerRef.current);
debounceTimerRef.current = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500);
}, [searchTerm]);
useEffect(() => {
if (debouncedSearchTerm) {
axios
.get(
`https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/movies/search?title=${debouncedSearchTerm}`
)
.then((response) => {
setSearchResults(response.data);
})
.catch((error) => {
console.error('Error:', error);
});
} else {
setSearchResults([]);
}
}, [debouncedSearchTerm]);
검색 input에 입력하는 값을 searchTerm 으로 받아준다.
debouncedSearchTerm은 검색어의 디바운스된 버전이다.
searchResults는 검색 결과를 저장하고, hoveredMovie는 유저가 호버한 영화를 추적한다.
debounceTimerRef는 디바운스에 사용된 타임아웃 함수에 대한 참조를 유지한다.
useEffect로 searchTerm이 변경될 때마다 타임아웃을 재설정한다.
500ms 후에 debouncedSearchTerm을 현재 searchTerm으로 업데이트해준다.
두 번째 useEffect는 debouncedSearchTerm이 변경될 때마다 리렌더링 된다.
axios로 디바운스된 검색어를 쿼리 매개변수로 사용해 비동기 요청을 수행한다.
이 때 값이 있으면 받은 검색 결과를 보여주고 없으면 빈 배열을 보여준다.
디바운스 부분 코드가 좀 더럽고 알아보기 힘든 거 같다;
이런 식으로 리팩토링 할 수 있을 것 같은데 디바운스는 좀 더 써봐야 익숙해질 것 같다...
useEffect(() => {
const debounceSearch = setTimeout(() => {
setSearchResults([]);
if (searchTerm) {
axios
.get(`https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/movies/search?title=${searchTerm}`)
.then((response) => {
setSearchResults(response.data);
})
.catch((error) => {
console.error('Error:', error);
});
}
}, 500);
return () => clearTimeout(debounceSearch);
}, [searchTerm]);
그렇게 검색 결과인 searchResults를 map으로 전개해 보여준다.
이 때 hover...라기보단 onMouseEnter, onMouseLeave에 따라서 해당 영화를 값으로 넣어준다.
{searchResults.length > 0 ? (
<div className="mt-3">
{searchResults.map((movie) => (
<div key={movie.id} className="flex space-x-3 my-3 relative">
<div
className="relative sm:w-full md:w-1/5 cursor-pointer"
onClick={() => openModal(movie)}
onMouseEnter={() => setHoveredMovie(movie)}
onMouseLeave={() => setHoveredMovie(null)}
>
<img
src={movie.accessKey}
alt={movie.title}
className="hover:brightness-50 relative md:w-full"
/>
{hoveredMovie === movie && (
<span className="absolute text-white top-2/4 left-1/2 transform -translate-x-1/2 -translate-y-1/2 font-semibold text-sm">
{movie.title}
</span>
)}
</div>
{modal && (
<Modal
modalId={movie.id}
modalImage={movie.accessKey}
modalTitle={movie.title}
modalGenre={movie.genre}
modalCreatedDate={movie.createdDate}
modalExplanation={movie.explanation}
closeModal={closeModal}
/>
)}
</div>
))}
</div>
) : (
<p className="mt-3">검색 결과가 없습니다.</p>
)}
이런 식... 이 때 모달을 전역으로 쓸 줄 몰라서 그냥 매번 갖다 박는 개노답 하드코딩을 했다;
hoverdMovie가 해당 movie라면 그 타이틀이 나오게 해주면 되고, 이미지의 밝기를 50%로 줄여주면 된다.
완성본은 이렇다.
'🛠️ 프로젝트 > ☁️ 구름톤 Hongflix' 카테고리의 다른 글
[Hongflix] 모달 & 마이 페이지 & 카카오 결제하기 API (5) | 2023.10.28 |
---|---|
[Hongflix] 조건부 렌더링 헤더 & Slick으로 배너와 캐러셀 만들기 (0) | 2023.10.18 |
[HongsamSNS] 로그인/회원가입 구현하기 (1) | 2023.10.18 |