이번엔 기존 기능에서 title을 클릭 시 세부 정보를 알 수 있는 기능을 추가하려고 한다.
이를 위해서는 react-router-dom 설치가 필요하다. (React JS에서 router기능을 수행하게 만들어준다.)
routes/Home.js ↓
import { useEffect, useState } from "react";
import Movie from "../components/Movie";
const Home = () => {
const [loading, setLoading] = useState(true);
/* [1,2] 1이 배열의 데이터, 2가 배열 데이터를 수정하는 함수 */
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);
setLoading(false);
};
useEffect(() => {
getMovies();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImage={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
};
export default Home;
routes/Detail.js ↓
const Detail = () => {
return <h1>Detail</h1>;
};
export default Detail;
components/Movie.js ↓
import propTypes from "prop-types";
const Movie = ({ coverImage, title, summary, genres }) => {
return (
<div>
<img src={coverImage} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>{genres == null ? "" : genres.map((i) => <li key={i}>{i}</li>)}</ul>
</div>
);
};
Movie.propTypes = {
coverImage: propTypes.string.isRequired,
title: propTypes.string.isRequired,
summary: propTypes.string.isRequired,
genres: propTypes.arrayOf(propTypes.string).isRequired,
/* genres는 배열이기 때문에 arrayOf를 넣어준다 */
};
export default Movie;
로 설정하였다. 이제 router을 App.js에서 사용하려고 한다.
App.js ↓
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
return (
<Router>
<Switch>
<Route path="/movie">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
export default App;
먼저 Router, Switch, Route를 import해준 다음, 상기 구문처럼 작성한다. Switch는 현재 버전에서는 Routes로 변경되었다고 한다.
Route옆에 path는 해당 주소를 나타내며, "/"는 기본 주소이기 때문에 Home.js로 연결시켜준다.(당연히 import 필요)
또 다른 path에는 "/movie"를 설정해서 title을 클릭 시 해당 Url(Detail.js)로 이동시켜준다.
이제 home과 detail을 연결시켜줘야 하는데 기존 html 같이 <a href...>를 사용하면 페이지 전체가 새로고침이 되기 때문에 사용하지 않고, 새로고침 되지 않는 상태에서 화면만 전환시키는 react-router-dom의 link를 사용할 것이다.
import { Link } from "react-router-dom";
const Movie = ({ coverImage, title, summary, genres }) => {
return (
<div>
<img src={coverImage} alt={title} />
<h2>
<Link to="/movie">{title}</Link>
</h2>
<p>{summary}</p>
<ul>{genres == null ? "" : genres.map((i) => <li key={i}>{i}</li>)}</ul>
</div>
);
};
다음과 같이 Link를 import 한 다음, <Link to=""></Link>를 이용해서 작성한다.
링크를 통해서 각각 다른 movie로 들어가게 되는데, 그것을 구분하기 위해 ~~/movie/숫자 와 같이 서로 다른 숫자(id)가 나오게 설정해야 한다. 그래서 Home.js에 key값인 movie.id를 id라는 prop으로 새로 추가한다. ↓
<Movie
key={movie.id}
id={movie.id}
coverImage={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
그다음 Movie.js에서 id를 import 한 다음 ↓
<Link to={`/movie/${id}`}>{title}</Link>
이렇게 바꿔준다. 그러면 개별적인 영화의 id 숫자로 들어가지게 된다.
해당 영화 title을 클릭하면
movie/37384라는 해당 movie의 id넘버로 url이 변경된다.
이제 :id의 값을 인지하고 URL을 변환해주는 것은 react-router-dom의 useParams이다. 이것을 이용해서 url의 변숫값을 알 수 있게 만들기로 한다.
Detail.js에서 다음과 같이 작성한다. ↓
import { useParams } from "react-router-dom";
const Detail = () => {
const { id } = useParams();
console.log(id);
return <h1>Detail</h1>;
};
export default Detail;
const명을 id로 정한다음 useParams함수를 사용하면
이렇게 해당 url의 id값이 나오게된다.
이제 fetch를 불러와서 해당 값에 적용시켜보도록 한다. ↓
const { id } = useParams();
const getMovie = useCallback(async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
}, [id]);
useEffect(() => {
getMovie();
}, [getMovie]);
위와 같이 만들어준다. useCallback을 하지 않으면 warning이 나오게 되므로 사용한다.
https://kyounghwan01.github.io/blog/React/exhaustive-deps-warning/#_1-useeffect내-state를-넣어줌
해당 링크는 앞서 언급한 경고가 나오게 되었을 때 해결방안이다. 필자는 useCallback을 사용하였다.
Detail.js ↓
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const { id } = useParams();
const getMovie = useCallback(async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
console.log([json.data.movie]);
/* 그냥 json.data.movie로 하면 object이기 때문에 map함수가 적용안됨. map은 arrary에서 적용 */
setMovies([json.data.movie]);
setLoading(false);
}, [id]);
useEffect(() => {
getMovie();
}, [getMovie]);
Callback함수, 영화의 상세내용을 담고있는 api url, Home.js와 같이 loading 및 data arrary 생성.
위에도 작성하였지만, 이번에 불러온 detail data는 arrary가 아닌 string형태이기 때문에 []로 감싸줘야 배열 형태가 된다.
이후 내용들은
component/MovieDetail.js를 만들어서 data를 보냈다.
Detail.js ↓
return loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<MovieDetail
key={movie.id}
title={movie.title}
bkImage={movie.background_image_original}
longTitle={movie.title_long}
summary={movie.description_full}
rating={movie.rating}
runtime={movie.runtime}
/>
))}
</div>
);
component/MovieDetail.js ↓
import propTypes from "prop-types";
import { Link } from "react-router-dom";
const MovieDetail = ({
title,
bkImage,
longTitle,
summary,
rating,
runtime,
}) => {
return (
<div>
<div>
<h2>
<Link to={`/`}>Home</Link>
</h2>
</div>
<img alt={title} src={bkImage}></img>
<h1>{longTitle}</h1>
<p>{summary}</p>
<div>Rating : {rating}/10</div>
<div>Runtime : {runtime}min</div>
</div>
);
};
MovieDetail.propTypes = {
bkImage: propTypes.string.isRequired,
title: propTypes.string.isRequired,
longTitle: propTypes.string.isRequired,
summary: propTypes.string.isRequired,
rating: propTypes.number.isRequired,
runtime: propTypes.number,
};
export default MovieDetail;
이제 css를 적용할건데, React JS에서 css를 적용하기 좋은 방법이 있다.
다음과 같이 Movie.module.css파일을 만들어준다.
해당 css적용이 될 Movie.js에서는
import styles from "../css/Movie.module.css";
로 적용시킨다.
css 파일에서 title의 back ground color을 변경하려고 한다.
.title {
background-color: tomato;
}
다음과 같이 작성한 후
다시 Movie.js에서
<h2>
<Link to={`/movie/${id}`} className={styles.title}>
{title}
</Link>
</h2>
이렇게 작성하는데, 우리는 import할 때 styles라고 했으므로 className에 styles.title 로 작성하면
css에서 .title로 작성한 부분이 적용된다.
'JavaScript > React JS' 카테고리의 다른 글
[React JS] React에서 css reset하기 (0) | 2022.02.16 |
---|---|
[React JS] React에서 Font Awesome icon 적용하기 (0) | 2022.02.16 |
[React JS] 간단한 Movie App 만들기1 (0) | 2022.02.09 |
[React JS] Coin price tracker 만들기 (1) | 2022.02.07 |
[React JS] 간단한 ToDo리스트 만들기 (0) | 2022.02.03 |