영화 웹 서비스는 영화 api를 가져와 9점대 이상의 평점인 영화를 보여주며 검색 기능과 영화 상세페이지까지 있는 서비스이다.
검색한 영화가 있을 때
검색한 영화가 없을 때
api 정보를 불러오는 중 화면
API 정보 상태 관리
<script>
const [movies, setMovies] = useState([]);
const getMovies = async () => {
const json = await (
await fetch(
`https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year`
)
).json();
setMovies(json.data.movies);
};
useEffect(() => {
getMovies();
}, []);
</script>
api로 받아온 영화 데이터를 useState로 상태를 관리 해주며 , useEffect를 사용하여 브라우저가 렌더링 됐을 때 api를 불러온다.
BrowserRouter를 이용한 페이지 이동
<script>
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/movie/:id" element={<Detail />} />
</Routes>
</BrowserRouter>
</script>
메인페이지인 Home 컴포넌트와 영화 상세페이지인 Detail 컴포넌트를 BrowserRouter를 이용하여
페이지 이동을 시켜주었다.
Detail.js
useParams를 이용한 api id 값 저장
<script>
const [movies, setMovies] = useState([]);
const { id } = useParams();
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
setMovies(json.data.movie);
};
useEffect(() => {
getMovie();
}, []);
</script>
useParams를 이용하여 Detail 페이지로 이동한 해당 영화의 id 값을 가져와 json에 저장 한 후 useState에 담아 상태 관리를 통해 해당 영화의 api 값을 이용할 수 있다.
Loading.js
<script>
const Loading = () => {
return (
<div className="loading">
<h1>Loading...</h1>
<div className="loader">
<span></span>
<span></span>
<span></span>
</div>
</div>
);
};
export default Loading;
</script>
처음 브라우저 렌더링시 api를 불러오는중이면 보여줄 로딩 컴포넌트를 따로 작성했다.
<script>
const [loading, setLoading] = useState(true);
{loading ? (
<Loading />
) : (
<div className="container">
<Header />
<SearchBar search={search} handleSearch={handleSearch} />
<div className="search_movie">{moviesLength()}</div>
<div className="movies">
{filterTitle.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
year={movie.year}
genres={movie.genres}
rating={movie.rating}
/>
))}
</script>
loading state를 만든 후 초기 값은 true로 지정하여 삼항연산자를 이용해 loading이 true 이면 로딩 컴포넌트가 보여지고 api값들이 다 불러와져 false가 되면 페이지가 렌더링 된다.
SearchBar.js
<script>
import React, { useEffect } from "react";
const SearchBar = ({ search, handleSearch }) => {
useEffect(() => {
console.log("rerender");
});
return (
<div className="Search">
<input
placeholder="Search here"
type="text"
value={search}
onChange={handleSearch}
></input>
</div>
);
};
export default React.memo(SearchBar);
</script>
영화 검색에 필요한 검색창 컴포넌트를 따로 만들었다.
<script>
const [search, setSearch] = useState("");
const handleSearch = (e) => {
setSearch(e.target.value);
};
const filterTitle = movies.filter((p) => {
return p.title
.replace(" ", "")
.toLocaleLowerCase()
.includes(search.toLocaleLowerCase().replace(" ", ""));
});
{filterTitle.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
year={movie.year}
genres={movie.genres}
rating={movie.rating}
/>
))}
</script>
검색을 할 때마다 바뀌는 input값의 value를 search state를 만들었다.
그리고 filterTitle이라는 상수에 검색한 영화 제목을 가져올 수 있도록 filter 메서드를 이용하여 필터된 제목을 만들었다.
필터된 filterTitle에 map을 이용하여 컴포넌트에 렌더링 되도록 하였다.
관련 영화의 갯수를 알려주는 로직
<script>
const moviesLength = () => {
if (search === "") {
return;
} else if (filterTitle.length >= 1) {
return (
<span>
Find <b>{filterTitle.length}</b> Movies
</span>
);
} else {
return <span>No movies found</span>;
}
};
<div className="search_movie">{moviesLength()}</div>
</script>
movieLength 라는 상수에 검색창이 빈문자열이면 바로 return을 시키고 , 검색한 제목이 1글자 이상이면 검색한 filterTitle의 갯수가 몇개가 있는지 알려준다.
관련된 영화 제목이 없다면 No movies found 라는 문구를 보여주도록 하여 전달 하였다.
마무리 하며 ..
리액트 프로젝트를 만들어보며 리액트 Hooks의 장점과 , 컴포넌트별 관리의 장점 등 리액트의 편리성을 느꼇고 , 결국은 react도 역시 자바스크립트를 기반으로 하기때문에 자바스크립트의 중요성을 한번 더 느꼈다. 더욱더 기초를 탄탄하게 잡아야겠다.