영화 소개, 링크 걸어 더 많은 정보 볼 수 있는 Movie App을 만들어 보자.
https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year
import { useState } from "react";
function App() {
const [loading, setLoading] = useState(true);
return <div>{loading ? "loading..." : null}</div>;
}
export default App;
import { useEffect, useState } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [api, setApi] = useState([]);
useEffect(() => {
fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year"
)
.then((response) => response.json())
.then((json) => {
console.log(json);
});
}, []);
return <div>{loading ? "loading..." : null}</div>;
}
export default App;
json을 콘솔로그 해서 보니 json.data.movies
로 받으면 데이타 사용 가능하다.
useEffect(() => {
fetch(
"https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year"
)
.then((response) => response.json())
.then((json) => {
setMovies(json.data.movies);
setLoading(false);
});
}, []);
이렇게 작성하면 된다.
then()
보다 요즘 더 자주 사용하는async-await
을 사용해보자.
async-await을 사용하기 위해 getMovies라는 함수를 만들자.
//async() 함수
const getMovies = async () => {
//api 페치한거에서 response 가져와서
const response = await fetch(
`https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year`
);
// 그 response한거에서 json()을 뽑아내서 json을 가져와서
const json = await response.json();
//setMovies모디파이어에 데이터 넘겨주면 된다.
setMovies(json.data.movies);
setLoading(false);
};
useEffect(() => getMovies(), []);
아니면 더 짧게 await 붙은 애들 ()
로 김밥처럼 더 묶을 수도 있다.
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(), []);
import { useEffect, useState } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
//async() 함수
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(), []);
console.log(movies);
return (
<div>
{loading ? (
"loading..."
) : (
<div>
{movies.map((movie) => (
<div key={movie.id}>
<img src={movie.medium_cover_image} />
<h2>{movie.title}</h2>
<p>{movie.summary}</p>
<ul>
{movie.genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
))}
</div>
)}
</div>
);
}
export default App;
movie.
를 뺀다. movie는 App.js로부터 받을 예정인 정보들이기 때문에 아직 정의되지 않았다.function Movie({ medium_cover_image, title, summary, genres }) {...}
function Movie({ medium_cover_image, title, summary, genres }) {
return (
<div>
<img src={medium_cover_image} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
export default Movie;
key는 React.js에서만, map안에서 component들을 render할 때 사용하는 거다.
import Movie from "./Movie";
//생략
return (
<div>
{loading ? (
"loading..."
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
//생략
일반적으로 자바스크립트에서는
medium_cover_image
이런 식으로 쓰지 않는다.
보통mediumCoverImage
이렇게 카멜백 사용한다.
그런데 지금은 component니까 부르고 싶은대로 불러도 상관은 없지만 카멜백으로coverImg
이런식으로 바꾸자..^ㅇ^~
coverImg
로 바꿔주면 된다.function Movie({ coverImg, title, summary, genres }) {
return (
<div>
<img src={coverImg} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
export default Movie;
이렇게 하면 props으로 component로 넘겨서 data를 사용할 수 있게 된다.
import PropTypes from "prop-types";
function Movie({ coverImg, title, summary, genres }) {
return (
<div>
<img src={coverImg} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
//Movie 컴포넌트 PropTypes 설정
Movie.propTypes = {
coverImg: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
//장르는 어레이가 되어야 한다. 그리고 어레이 안에 오는 엘리먼트는 string으로 작성
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default Movie;
각 영화 별로 movies/movie.id
가 붙은 상세 페이지로 전환하기 위해 React Router를 사용해야 한다.
이제 스크린(= 페이지, = route) 단위로 생각해야 한다. 즉 route 별로 코드를 정리해 줘야 한다.
Home
라우트: 모든 영화 보여주기Detail
라우트: 영화 상세페이지 보여주기src/routes
폴더 생성src/components
폴더를 새로 만들어서 Movie 컴포넌트를 여기에 넣어 주자. 그러면 다행히 App.js에서 Movie.js를 import하는 path가 자동으로 수정해주기 때문에 저장하면 된다.src/routes
폴더에 Home.js
route 생성Home.js
라우트는 모든 영화를 보여주는 페이지 이기 때문에 현재 App()에 적은 코드 싹 다 가지고 오면 된다.import { useEffect, useState } from "react";
import Movie from "../components/Movie";
function Home() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
//async() 함수
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(), []);
console.log(movies);
return (
<div>
{loading ? (
"loading..."
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
src/routes
폴더에 Detail.js
route 생성Detail.js
라우트는 각 영화의 상세 페이지를 보여주는 페이지이다.function Detail() {
return <h1>Detail</h1>;
}
export default Detail;
react-router-dom은 컴포넌트 모음집으로 react-router-dom을 사용하기 위해서 App.js
파일에 몇 가지를 import해야 한다.
<BrowserRouter>
컴포넌트Router에는 두 가지(HashRouter, BrowserRouter)가 있는데 일반적으로 BrowserRouter를 사용한다.
BrowserRouter의 URL은 보통 웹사이트 처럼 https://movie.com/movie
이런 식이다.
HashRouter의 URL은 https://movie.com/#/movie
이런 식이다. URL 앞에 말그대로 해시태그(#)가 붙는다.
<Routes>
컴포넌트<Route>
컴포넌트import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
// import your route components too
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
</Routes>
</BrowserRouter>,
document.getElementById("root")
);
🔥 react-router-dom이 v.6으로 바뀌면서 바뀐 부분
<Switch>
컴포넌트 👉<Routes>
컴포넌트로 대체
v.6 부터는,<Switch>
컴포넌트가<Routes>
컴포넌트로 대체되었다.<Routes>
가 최적의 경로배정을 해주기 때문에<Switch>
를 썼을 때의 고민을 말끔히 해결해 준다.
-<Route>
의 exact 속성도 더이상 쓰이지 않는다.- 또한
<Routes>
컴포넌트 사이에 자식 컴포넌트를 넣지 않고, element prop에 자식 컴포넌트를 할당하도록 바뀌었다.
Route path="/" element={< Home / >}
✅ react-router-dom v.5.3.0
// npm i react-router-dom@5.3.0
// <Router>는 URL을 보고 있는 component이다.
// <Switch>는 Route(URL을 의미함)를 찾는 컴포넌트이다. <Switch> 컴포넌트가 Route를 찾으면 컴포넌트를 렌더한다.
import Home from "./routes/Home";
import Detail from "./routes/Detail";
import { BrowserRouter, Router, Switch, Route } from "react-router-dom";
function App() {
return (
<Router>
<Switch>
<Route path="/">
<Home />
</Route>
<Route path="/movie">
<Detail />
</Route>
</Switch>
</Router>
);
}
export default App;
✅ react-router-dom v.6
// npm i react-router-dom@6
import Home from "./routes/Home";
import Detail from "./routes/Detail";
import { BrowserRouter, Routes, Route } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/movie" element={<Detail />}></Route>
</Routes>
</BrowserRouter>
);
}
export default App;
👉 v.6로 진행하자.
route는 이렇게 구성하면 된다.
/
: loading과 movies 보여주는 컴포넌트 렌더/movie
: Detail이라고 h1 태그 렌더하는 컴포넌트 렌더<Link>
컴포넌트: Route에서 다른 Route로 이동하기유저가 영화 제목을 클릭하면 Detail 페이지로 가게 만들어 보자.
<h2><a href="/movie">{title}</a></h2>
이런식으로 Movie 컴포넌트가 렌더하는 {title}에 a 태그 달아서 이동해도 되지만, 그러면 페이지 전체가 새고된다.
다행히 전체 새고 안되게 이동하는 컴포넌트가 있다.
<Link>
컴포넌트
한 route에서 다른 route로 (다른 URL로) 가고 싶을 때, <Link>
컴포넌트는 브라우저 새고 없이 유저를 다른 페이지로 이동시켜 준다.
예시
import { Link } from "react-router-dom";
function Home() {
return (
<div>
<h1>Home</h1>
<nav>
<Link to="/">Home</Link> |{" "}
<Link to="about">About</Link>
</nav>
</div>
);
}
따라서 Movie 컴포넌트에 Link를 import 해와서 사용하면 된다.
- 📍 Movie.js
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
function Movie({ coverImg, title, summary, genres }) {
return (
<div>
<img src={coverImg} alt={title} />
<h2>
<Link to="/movie">{title}</Link>
</h2>
<p>{summary}</p>
<ul>
{genres.map((genre) => (
<li key={genre}>{genre}</li>
))}
</ul>
</div>
);
}
//생략
페이지 새고는 안되면서 다른 url로 잘 넘어간다!
요렇게 App 컴포넌트에 만들었던 모든 로직을 별개의 장소로 옮겼다.
Home 컴포넌트 따로, Detail 따로, Movie 따로 ~
App에서는 react-router-dom의 컴포넌트(url 바라보는 컴포넌트들)를 가져다 쓰고 있다.
url이 바뀌면 어떤 컴포넌트 보여줄지 넣어 뒀다. 유저가 "/" path에 있으면 Home 컴포넌트 보여줘라 이렇게.
<Route path="/hey" element={<h1>BTS</h1>}></Route>
이런식으로도 사용할 수 있다. 그러면 /hey로 가면 h1태그로 BTS라고 렌더된다.