영화 정보 api
아래 조건을 사용한 api를 사용함
https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year
import { useEffect, useState } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovie = async () => {
const response = await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
);
const json = await response.json();
// 아래 같이 한줄로 작성해도 됨
// const json = await (
// await fetch(
// "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
// )
// ).json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<div key={movie.id}>
<h2>{movie.title}</h2>
<img src={movie.medium_cover_image}></img>
<p>{movie.summary}</p>
<ul>
{/* hasOwnProperty를 사용하여 movie 내에 genres 프로퍼티가 있는지 없는지 확인할 수 있음 */}
{movie.hasOwnProperty("genres")
? movie.genres.map((genre) => <li key={genre}>{genre}</li>)
: null}
</ul>
</div>
))}
</div>
)}
</div>
);
}
export default App;
컴포넌트 분리
Movie.js 파일 생성
// Movie.js
function Movie({ title, coverImg, summary, genres }) {
return (
<div>
<h2>{title}</h2>
<img src={coverImg} alt={title}></img>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
export default Movie;
App.js에서 Movie 컴포넌트 사용
// App.js
import { useEffect, useState } from "react";
import Movie from "./Movie";
function App() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovie = async () => {
const response = await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
);
const json = await response.json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
title={movie.title}
coverImg={movie.medium_cover_image}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default App;
입력한 props의 타입 검증을 위한 PropTypes 적용
// Movie.js
import PropTypes from "prop-types";
Movie.propTypes = {
title: PropTypes.string.isRequired,
coverImg: PropTypes.string.isRequired,
summary: PropTypes.string,
genres: PropTypes.arrayOf(PropTypes.string),
};
React Router
5.3.0 버전을 사용
react router 설치
npm i react-router-dom@5.3.0
이제 스크린 (Router) 단위로 생각
App.js는 route를 받아서 스크린을 불러옴
스크린(Router) 단위로 파일 생성
src 경로 안의 파일 정리
파일 경로 새로 생성
- components/Movie.js (경로를 옮겼기 때문에 import의 경로 수정)
- routes/Home.js
- routes/Detail.js
기존 App.js에 있던 내용을 Home.js로 옮겨줌
// Home.js
import { useEffect, useState } from "react";
import Movie from "./components/Movie";
function Home() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovie = async () => {
const response = await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
);
const json = await response.json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
title={movie.title}
coverImg={movie.medium_cover_image}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
App에서 router 적용
// App.js
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./routes/Home";
import Movie from "./routes/Detail";
function App() {
return (
<Router>
<Switch>
<Route path="/movie">
<Movie />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
export default App;
- 위 코드에서 "/moive" 가 "/" Route보다 아래에 있으면 "/movie" 주소를 입력해도 "/" Route가 먼저 render된다. 위의 코드처럼 router의 위치를 조종하거나 exact={true}를 적용
- Switch 태그는 Route를 한 번에 하나만 render할 때 사용
페이지 이동
<a> 태그 사용하기
- 사용 가능
- 다만 해당 페이지로 이동했을 경우 전체 페이지가 다시 rendering
<h2>
<a href="/movie">{title}</a>
</h2>
<Link> 사용하기
- 브라우저의 새로고침 없이 다른 페이지로 이동시켜주는 컴포넌트
// Movie.js
import { Link } from "react-router-dom";
<h2>
<Link to="/movie">{title}</Link>
</h2>;
Dynamic URL
- 변화하는 주소를 받을 수 있음
- 변화하는 부분에 ":" 를 사용
// App.js
<Route path="/movie/:id">
<Detail />
</Route>
id 값을 사용하기 위해서 props에서 id 전달
// Home.js
{
movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
coverImg={movie.medium_cover_image}
summary={movie.summary}
genres={movie.genres}
/>
));
}
전달 받은 props 값 Link에 적용
// Movie.js
function Movie({ title, id, coverImg, summary, genres }) {
return (
<div>
<h2>
<Link to={`/movie/${id}`}>{title}</Link>
</h2>
<img src={coverImg} alt={title}></img>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
useParams
url에 있는 값을 반환해주는 함수
// Detail.js
import { useParams } from "react-router-dom";
function Detail() {
const x = useParams();
console.log(x);
// {id: '53703'}
// id: "53703"
// [[Prototype]]: Object
return <h1>Detail</h1>;
}
export default Detail;
Detail.js 내에서 api 받아오기
// Detail.js
import { useParams } from "react-router-dom";
import { useEffect } from "react";
function Detail() {
const { id } = useParams();
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
// console.log(json);
};
useEffect(() => {
getMovie();
}, []);
return <h1>Detail</h1>;
}
export default Detail;
최종 코드
// 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/:id">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
export default App;
// Detail.js
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import MovieDetail from "../components/MovieDetail";
function Detail() {
const { id } = useParams();
const [loading, setLoading] = useState(true);
const [movie, setMovie] = useState("");
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
// console.log(json);
setLoading(false);
setMovie(json.data.movie);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
<h1>Detail</h1>
{loading ? (
<h1>loading</h1>
) : (
<MovieDetail
date_uploaded={movie.date_uploaded}
download_count={movie.download_count}
genres={movie.genres}
language={movie.language}
like_count={movie.like_count}
large_cover_image={movie.large_cover_image}
rating={movie.rating}
runtime={movie.runtime}
title={movie.title}
torrents={movie.torrents}
year={movie.year}
description={movie.description_full}
/>
)}
</div>
);
}
export default Detail;
// MovieDetail.js
import PropTypes from "prop-types";
function MovieDetail({
date_uploaded,
download_count,
genres,
language,
like_count,
large_cover_image,
rating,
runtime,
title,
year,
description,
torrents,
}) {
return (
<div>
<h2>{title}</h2>
<img src={large_cover_image} alt={title}></img>
<p>Release: {year}</p>
<p>Language: {language}</p>
<p>Runtime: {runtime}</p>
<p>Rating: {rating}</p>
<p>Like Count: {like_count}</p>
<p>Genre</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
<p>{description}</p>
<hr />
<h3>Download info</h3>
<p>File Uploaded: {date_uploaded}</p>
<p>Download Count: {download_count}</p>
<ul>
{torrents.map((torrent, i) => (
<li key={torrent.hash}>
<a href={torrent.url}>다운로드 링크 {i + 1}</a>
</li>
))}
</ul>
</div>
);
}
export default MovieDetail;
// Home.js
import { useEffect, useState } from "react";
import Movie from "../components/Movie";
function Home() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovie = async () => {
const response = await fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
);
const json = await response.json();
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => {
getMovie();
}, []);
return (
<div>
{loading ? (
<h1>Loading...</h1>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id}
title={movie.title}
coverImg={movie.medium_cover_image}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
// Movie.js
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
function Movie({ title, id, coverImg, summary, genres }) {
return (
<div>
<h2>
<Link to={`/movie/${id}`}>{title}</Link>
</h2>
<img src={coverImg} alt={title}></img>
{/* summary 길이에 따른 처리 */}
<p>{summary.length > 235 ? `${summary.slice(0, 235)} ...` : summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
Movie.propTypes = {
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
coverImg: PropTypes.string.isRequired,
summary: PropTypes.string,
genres: PropTypes.arrayOf(PropTypes.string),
};
export default Movie;
'React' 카테고리의 다른 글
[React] TypeScript (0) | 2023.08.17 |
---|---|
[React] styled-components (0) | 2023.08.16 |
[React 기초] 07. 예제 - Coin tracker (0) | 2023.08.10 |
[React 기초] 06. 예제 - Todo List (0) | 2023.08.10 |
[React 기초] 05. effects (0) | 2023.08.10 |