개발자를 향해...

[ReactJS로 영화 웹 서비스 만들기] #4 MAKING THE MOVIE APP 본문

웹 자바스크립트 공부/ReactJS + node.js

[ReactJS로 영화 웹 서비스 만들기] #4 MAKING THE MOVIE APP

eugeneHwang1124 2021. 2. 8. 16:10
728x90
반응형

본 내용은 노마드코더 - ReactJS로 영화 웹 서비스 만들기 를 수강하며 작성되었습니다.

 

yts라는 불법 영화 사이트에서 api를 참고한다.현재 우리가 사용하려는 api의 주소는 https://yts.lt/api/v2/list_movies.json 이다. 하지만 yts는 항상 그 주소가 바뀐다. 따라서 강의에서 제공하는 api를 사용한다.https://yts-proxy.now.sh/list_movies.json

visual code에서 axios를 설치하고 import 하자. ( npm i axios )  이제 componentDidMount에 axios를 넣어준다.

componentDidMount() {
    const movies= axios.get("https://yts-proxy.now.sh/list_movies.json");
  }

이 코드를 통해 axios는 사이트의 데이터를 가져온다.

axios는 속도가 느리기 때문에 javascript에게 componentDidMount가 끝날 때까지 시간이 걸린다고 말해야한다.

getMovies=async()=>{
    const movies =await axios.get("https://yts-proxy.now.sh/list_movies.json");
  }
  componentDidMount() {
    this.getMovies();
  }

await을 통해 기다리라는 의미를 주었다. await은 async가 있어야 동작한다. 이제 영화 데이터를 가져와서 state의 렌더 함수에 보여주자.

console.log(movies);로 출력해보면 데이터를 잘 가져오는 것을 알 수 있다. 여기의 data 부분을 가져와야한다.

console.log(movies.data.data.movies);

이 코드를 다음과 같이 단축할 수 있다.

getMovies=async()=>{
    const {data: {data :{movies}}} =await axios.get("https://yts-proxy.now.sh/list_movies.json");
    console.log(movies);
  }

이 movies를 state안에 넣자.

getMovies=async()=>{
  const {data: {data :{movies}}} =await axios.get("https://yts-proxy.now.sh/list_movies.json");
  this.setState({movies}); 
}

this.setState({movies});는 this.setState({movies : movies});와 같은 코드이다.

 

이제 Movie.js라는 파일을 생성한다. 그리고 movies 에서 id, year, title, summary, poster을 가져오도록 코드를 작성한다.

<Movie.js>

import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';

function Movie({id, year,title, summary,poster}){
    return<h1>{title}</h1>;
}
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;

App.js에서  map으로 데이터를 Movie.js에 보내준다.

import React from 'react';
import PropTypes from 'prop-types';
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");
    this.setState({movies,isLoading:false});
  }
  componentDidMount() {
    this.getMovies();
  }

  render() {
    const { isLoading,movies}=this.state;
    return (
      <div>
        { isLoading ? "Loading..." : movies.map(movie =>{
          console.log(movie);
          return <Movie id={movie.id} year={movie.year} title={movie.title} summary={movie.summary} poster={movie.medium_cover_image} />
        })}
      </div>
      );
  }
}

export default App;

실행을 시키니 다음과 같은 에러가 떴다. 즉 <Movie/>에서 고유한 key가 필요하다는 것이다. 따라서 key={movie.id}를 추가해준다.

실행 결과

이제 Movie.js의 HTML을 꾸며주자.

먼저 App.js의 render을 다음과 같이 바꾼다.

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 =>{
              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>
        )}
      </section>
      );
  }

Movie.js와 App.js에 각각 Movie.css와 App.css 파일을 만들고 import한다.

그리고 Movie.js에 genres 데이터를 추가로 가져온다.

//<App.js>
return <Movie key={movie.id} id={movie.id} year={movie.year} title={movie.title} summary={movie.summary} poster={movie.medium_cover_image} genres={movie.genres} />

//<Movie.js>
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
}

function Movie({id, year,title, summary,poster, genres}){...

실행을 시키니 class가 아니라 className을 써야한다는 에러가 떴다. 자바스크립트 class안에서는 className을 써야한다. 따라서 해당 부분을 바꾸어준다.

 

 

장르를 가져와서 Movie에서 화면에 출력하도록 한다.

import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import './Movie.css';

function Movie({id, 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>
            <h5 className="movie_year">{year}</h5>
            <ul className="genres">{genres.map((genre,index)=><li key={index} className="genres__genre">{genre}</li> )}</ul>
            <p className="movie_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;

App.css 파일도 코드를 넣어 꾸며주었다.

코드 참고 : github.com/nomadcoders/movie_app_2019/commit/c0a3270f5824c2555e2621190c6307cbaefe0704

 

여기서 텍스트를 다듬고 이미지를 조금 키워보자

.movie img {
  position: relative;
  top: -50px;
  max-width: 180px;
  width:100%;
  margin-right: 30px;
  box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25),
    0 18px 36px -18px rgba(0, 0, 0, 0.3), 0 -12px 36px -8px rgba(0, 0, 0, 0.025);
}

그리고 summary의 크기를 일정하게 잘라서 표시해준다.

//Movie.js 
<p className="movie_summary">{summary.slice(0,180)}</p>

이렇게 바꾸어주자. 

App.css에서 이 부분을 Movie.css로 옮기자.

.movies .movie {
    width: 45%;
    background-color: white;
    margin-bottom: 70px;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    font-weight: 300;
    padding: 20px;
    border-radius: 5px;
    color: #adaeb9;
    box-shadow: 0 13px 27px -5px rgba(50, 50, 93, 0.25),
      0 8px 16px -8px rgba(0, 0, 0, 0.3), 0 -6px 16px -6px rgba(0, 0, 0, 0.025);
  }
  
  .movie img {
    position: relative;
    top: -50px;
    max-width: 180px;
    width:100%;
    margin-right: 30px;
    box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25),
      0 18px 36px -18px rgba(0, 0, 0, 0.3), 0 -12px 36px -8px rgba(0, 0, 0, 0.025);
  }
  
  .movie .movie__title,
  .movie .movie__year {
    margin: 0;
    font-weight: 300;
  }
  
  .movie .movie__title {
    margin-bottom: 5px;
    font-size: 24px;
    color: #2c2c2c;
  }
  
  .movie .movie__genres {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    margin: 5px 0px;
  }
  
  .movie__genres li,
  .movie .movie__year {
    margin-right: 10px;
    font-size: 14px;
  }

 

반응형