Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

9시 24분

Movie App을 만들어보자! 본문

Javascript/React

Movie App을 만들어보자!

leeeee.yeon 2021. 7. 23. 16:56
영화 컴포넌트 계획 짜기

 

복습복습! 컴포넌트가 처음 생성될 때 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에 해당하는 부분을 위와 같이 바꾸면 된다!

 

CSS 수정 후

 

'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