9시 24분
Movie App을 만들어보자! 본문
영화 컴포넌트 계획 짜기
복습복습! 컴포넌트가 처음 생성될 때 componentDidMount()가 실행된다.
우리는 componentDidMount()에서 API로부터 data를 fetch하고
> movie array를 render하고 map을 만들어서 movie 객체를 render한다
다음 강의에 들어가기 전에 일단 movie state를 만들어두자.
import React from "react";
// import propTypes from "prop-types";
class App extends React.Component{
state = {
isLoading: true,
movie: []
};
render(){
const { isLoading } = this.isLoading;
return (
<div>
{isLoading ? "Loading" : "We are ready"}
</div>
);
}
}
export default App;
isLoading은 실습하지 않았지만 삼항 연산자를 통해 웹사이트가 로딩중일 때 로딩중이라는 메시지를 띄우는 것이다.
API로부터 영화 데이터 가져오기
API로부터 데이터를 fetch(가져오다)할 때, fetch() 함수를 쓰거나 axios 모듈을 사용하는 방법이 있다. 강의에서는 axios를 이용한다. 우선 npm을 이용하여 axios를 설치하자.
npm install -s axios
그리고, 수업에서는 YTS에서 만든 API를 사용한다.
( 사실 나는 다른 영화 API 사용하고 싶었는데 !! API 사용법 아직 잘 모르고, 그렇기에 좋은 API를 찾기 어려웠다 ,,, 나중에 완강하고 다른 API로 바꿀거야 ..! )
링크로 들어가면 여러가지 API가 있는 것을 볼 수 있는데, 우리는 이 중 List Movies를 사용할 것이다.
사이트를 살짝 내려서
첫 번째 링크로 들어가보면 아래와 같이 json을 확인할 수 있다. ** 다른 곳 클릭하면 vpn 어쩌구 수상해보이는 사이트로 계속 들어감 ㅠ
(JSON view라는 크롬 확장 앱을 사용하면 아래처럼 깔끔하게 확인 가능 )
+ 형광펜 표시한 부분은 우리가 fetch하고 싶은 곳인 movies array(movie 객체로 이루어진)이다.
하지만...! YTS는 토렌트 어쩌구 회사이기 때문에 API 링크가 매번 바뀐다..!! 그러므로 YTS에 게시된 링크 말고 니꼬가 만든 https://yts-proxy.now.sh/list_movies.json를 사용하자!
import React from "react";
import axios from "axios";
class App extends React.Component{
state = {
isLoading: true,
movies: []
};
componentDidMount(){
axios.get("https://yts-proxy.now.sh/list_movies.json");
}
render(){
const { isLoading } = this.state.isLoading;
return (
<div>
{isLoading ? "Loading" : "We are ready"}
</div>
);
}
}
export default App;
웹사이트로 들어가 개발자 도구의 Network 탭을 확인하면 axios가 list_movies.json을 요청하는 것을 볼 수 있다.
그러나 axios는 데이터를 get해오는 것이 느릴 수 있기 때문에 우리는 js에게 'componentDidMount 함수가 끝날 때까지 약간 시간이 걸릴 수 있어~'라고 말해주고 기다려야 한다. 아래와 같이 코드를 수정해주면 된다.
getMovies = async () => {
const movies = await axios.get("https://yts-proxy.now.sh/list_movies.json");
}
async componentDidMount(){
this.getMovies();
}
이러한 것을 비동기 방식이라고 한다. async로 '기다려야 해'라는 사실을 알려주고, awiat으로 기다려야 할 대상을 말해준다. async와 await은 한 세트!
Movies rendering 하기
이제 우리는 API로부터 Movies data를 가져오는 것까지 했다. 콘솔 출력으로 data가 어떻게 생겼는지 봐보자.
우리가 원하는 영화 데이터는 movies > data > data > movies 안에 있다.
const movies = await axios.get("https://yts-proxy.now.sh/list_movies.json");
console.log(movies.data.data.movies);
이렇게 하면 우리가 원하는 영화 데이터를 가져올 수 있다. 하지만 코드가 깔끔하지 않다! es6의 문법을 사용하여 아래와 같이 코드를 한 번 더 수정하자. ( es6 헷갈리니까 나중에 따로 꼭 복습하자 ㅠ.ㅠ )
getMovies = async () => {
const {
data:
{
data: {movies}
}
} = await axios.get("https://yts-proxy.now.sh/list_movies.json");
console.log(movies);
}
이제 moives를 가져왔으니 state에 넣자.
this.setState({movies});
// this.setState({movies: movies});
주석에 작성한 것이 이전 문법, 주석 없이 작성한 것이 es6 문법이다.
이전 문법을 기준으로, : 앞의 movies가 state이고, 뒤의 movies가 axios로부터 가져온 movies 데이터이다.
es6에서는 주석 없이 작성한 것처럼 movies만 적어도 된다.
그리고 영화 데이터를 다 받아오면 로딩도 끝난 것이니 isLoading도 false로 바꿔주자.
this.setState({movies, isLoading: false});
이제 src 폴더에 Movie.js 파일을 생성하자. moives 컴포넌트는 state를 필요로 하지 않는다. 그렇기 때문에 우리가 원래 했던 function 컴포넌트의 형태를 다시 사용해도 된다.
import React from "react";
import propTypes from "prop-types";
function Movie({id, year, title, summary, poster}){
return (
<h5>{title}</h5>
);
}
Movie.propTypes = {
id: propTypes.number.isRequired,
year: propTypes.number.isRequired,
title: propTypes.string.isRequired,
summary: propTypes.string.isRequired,
poster: propTypes.string.isRequired
}
export default Movie;
- 변수명이 꼭 propTypes가 되어야 한다는 것을 리마인드하자 !
그리고 rating에 따라 영화들을 정렬하고 싶기 때문에, App.js에서 API를 불러오는 링크를
https://yts-proxy.now.sh/list_movies.json?sort_by=rating
로 수정해주자.
render(){
const { isLoading, movies } = this.state;
return (
<div>
{isLoading ? "Loading" : movies.map(movie => {
console.log(movie);
return (
<Movie
key= {movie.id}
id= {movie.id}
year= {movie.year}
title= {movie.title}
summary= {movie.summary}
poster= {movie.medium_cover_image}
/>
);
})
}
</div>
);
}
그리고 Movie.js를 이용해 콘솔과 화면에 데이터를 띄우기 위해 다음과 같이 App.js의 render() 부분을 수정하자.
** { } 안에 값은 API의 정보이기 때문에 API에 적힌 이름(medium_cover_image와 같이)을 사용해준다.
HTML & CSS, 장르 태그 추가
HTML을 적용한 코드
< App.js >
import React from "react";
import axios from "axios";
import Movie from "./Movie";
class App extends React.Component{
state = {
isLoading: true,
movies: []
};
getMovies = async () => {
const {
data:
{
data: {movies}
}
} = await axios.get("https://yts-proxy.now.sh/list_movies.json?sort_by=rating");
this.setState({movies, isLoading: false});
}
async componentDidMount(){
this.getMovies();
}
render(){
const { isLoading, movies } = this.state;
return (
<section class="container">
{isLoading ? // 삼항 연산자, ':'을 기준으로 구분
<div class="loader">
<span class="loader_text">Loading...</span>
</div>
:
<div class="movies">
{movies.map(movie => (
<Movie
key={movie.id}
id={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
/>
))}
</div>
}
</section>
);
}
}
export default App;
< Movie.js >
import React from "react";
import propTypes from "prop-types";
function Movie({year, title, summary, poster}){
return (
<div class="movie">
<img src={poster} alt={title} title={title} />
<div class="movie_data">
<h3 class="movie_title">{title}</h3>
<h5 class="movie_year">{year}</h5>
<p class="summary">{summary}</p>
</div>
</div>
);
}
Movie.propTypes = {
id: propTypes.number.isRequired,
year: propTypes.number.isRequired,
title: propTypes.string.isRequired,
summary: propTypes.string.isRequired,
poster: propTypes.string.isRequired
}
export default Movie;
여기까지가 기본적인 HTML이다.
CSS 적용 전에! 장르 태그를 웹사이트에 추가하자.
++ CSS는 개인적으로 하지 않고, https://github.com/nomadcoders/movie_app_2019/commit/c0a3270f5824c2555e2621190c6307cbaefe0704을 복붙한 뒤 입맛에 맞게 아주 살짝씩 수정하였다.
< App.js >
import React from "react";
import axios from "axios";
import Movie from "./Movie";
import "./App.css";
class App extends React.Component{
state = {
isLoading: true,
movies: []
};
getMovies = async () => {
const {
data:
{
data: {movies}
}
} = await axios.get("https://yts-proxy.now.sh/list_movies.json?sort_by=rating");
this.setState({movies, isLoading: false});
}
async componentDidMount(){
this.getMovies();
}
render(){
const { isLoading, movies } = this.state;
return (
<section className="container">
{isLoading ? // 삼항 연산자, ':'을 기준으로 구분
<div className="loader">
<span className="loader_text">Loading...</span>
</div>
:
<div className="movies">
{movies.map(movie => (
<Movie
key={movie.id}
id={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
genres={movie.genres}
/>
))}
</div>
}
</section>
);
}
}
export default App;
- genres 추가
- class 속성을 className으로 고쳐줌 ( 리액트가 헷갈려해서 ) ( 개발자도구로 확인하면 class라고 표시됨 )
< Movie.js >
import React from "react";
import propTypes from "prop-types";
import "./Movie.css";
function Movie({year, title, summary, poster, genres}){
return (
<div className="movie">
<img src={poster} alt={title} title={title} />
<div className="movie_data">
<h3 className="movie_title">{title}</h3>
<ul className="genres">
{genres.map((genre, index) =>
<li key={index} className="genres_genre">{genre}</li>
)}
</ul>
<h5 className="movie_year">{year}</h5>
<p className="summary">{summary}</p>
</div>
</div>
);
}
Movie.propTypes = {
id: propTypes.number.isRequired,
year: propTypes.number.isRequired,
title: propTypes.string.isRequired,
summary: propTypes.string.isRequired,
poster: propTypes.string.isRequired,
genres: propTypes.arrayOf(propTypes.string).isRequired
}
export default Movie;
- props로 genres 추가
- genres 관련 태그 추가
- geners propType 추가
genres에서 map을 했을 때 unique 키가 없다는 에러 메시지 떴다. 하지만 genres는 array이기 때문에 id 같은 특별한 값이 없다. 여기서 주목할 것은 map 함수의 두 번째 파라미터 !
map 함수의 두 번째 인자는 index를 주기 때문에 이를 활용하여 에러를 없앴다.
영화마다 줄거리의 길이가 달라 박스 크기가 들쭉날쭉하다.
줄거리의 뒷부분을 cut하여 길이가 같도록 만들어주자. 그러기 위해 array의 slice() 함수를 이용해주었다.
<p className="summary">{summary.slice(0, 180)} ...</p>
summary에 해당하는 부분을 위와 같이 바꾸면 된다!
'Javascript > React' 카테고리의 다른 글
실전형 리액트 Hooks 10개 (1) - useState (0) | 2021.07.30 |
---|---|
ThemoviedDB 참고 사이트 (0) | 2021.07.27 |
Movie App 배포 및 라우팅 (0) | 2021.07.25 |
JSX & Props & State (0) | 2021.07.22 |
인트로, 개발환경 셋팅, 리액트 동작 원리 (0) | 2021.07.22 |