노마드코더 ReactJS로 영화 웹 서비스 만들기 8 (영화 웹 서비스)

딩쓰·2022년 12월 17일
1
post-thumbnail

#7.3 ~ 7.10

지금까지 배운 것들을 응용해서 영화정보를 보여주는 웹 서비스를 만들어 볼거임.

일단 많은 영화정보들을 받을 수 있는 API를 가져오자!

movie app 초기 세팅(fetch포함)

import { useEffect, useState } from 'react';

function App() {
  const [loading, setLoading] = useState(true);
  
  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 ? <h1>Loading...</h1> : null}</div>;
}

export default App;
  1. 로딩 중이라는 걸 보여주도록 useState를 사용해 기본값을 true로 줌.
  2. return <div>{loading ? <h1>Loading...</h1> : null}</div>;
    : Loading이 아니라면 영화들을 return해줘야 하는데 아직 없으니 null로 해줌.
  3. useEffect(() => {}, []); 빈배열을 주면 처음 페이지가 로딩되었을 때만 코드가 실행됨.
  4. useEffect에 fetch를 넣어 API를 받아옴.

.then() 사용

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  
  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);//변경함수에 fetch 데이터를 넣어줌
        setLoading(false); //데이터를 받아 온 후에는 로딩이 끝났기 때문에 false로 바꿔야함
      });
  }, []);
  console.log(movies);
  
  return <div>{loading ? <h1>Loading...</h1> : null}</div>;
}
  • 위의 코드처럼 movies를 console.log에 넣어서 콘솔창에서 확인해보자!

  • 페이지가 처음 렌더링됐을때는 movies는 비어있는 array이고, data가 온 후에는 array에 data가 담겨있는걸 확인함.

.then()을 async-await로 바꾸기(1번째 방법)


function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  
  const getMovies = async () => {
    const response = await fetch(`https://yts.mx/api/v2/list_movies.json?minimum_rating=9&sort_by=year`);
    const json = await response.json();
    setMovies(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovies();
  }, []);
  
  return <div>{loading ? <h1>Loading...</h1> : null}</div>;
}

.then()을 async-await로 바꾸기(2번째 방법)

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  
  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 ? <h1>Loading...</h1> : null}</div>;
}
  • 1번째 방법을 더 간단하게 한 코드임.

data 화면에 출력하기

map함수를 이용해 화면에 출력하자!

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);

  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 ? (
        <h1>Loading...</h1>
      ) : (
        <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((g) => (
                  <li key={g}>{g}</li>
                ))}
              </ul>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

  • 콘솔창에서 데이터를 분석해서 원하는 데이터만 뽑아서 화면에 출력할 수있음
    ex) movie.title

  • 데이터를 화면에 출력한 모습

Movie 컴포넌트 만들기

이번엔 리액트앱에서 페이지를 전환하기 위한 방법을 배워볼거임.
App.js파일안의 화면을 출력하는 코드를 그대로 가져와서 Movie.js파일을 생성해서 옮기자!

//App.js
import { useEffect, useState } from 'react';
import Movie from './Movie';

function App() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);
  
  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();
  }, []);
  
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>
          {movies.map((movie) => (
            <Movie 
              key={movie.id}
              coverImg={movie.medium_cover_image} 
              title={movie.title} 
              summary={movie.summary} 
              genres={movie.genres} 
            />
          ))}
        </div>
      )}
    </div>
  );
}
  • App.jsMovie.js를 import 해줌.
  • <Movie/>컴포넌트에 props로 속성을 보내줘야함

Movie.js

//Movie.js
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((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  );
}

Movie.propTypes = { //PropTypes이라고해서 에러났음. propTypes라고 쓰기 (소문자로!)
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Movie;
  • Movie 컴포넌트를 생성함.
  • { coverImg, title, summary, genres }
    => 이런식으로 App.js에서 props로 받아옴.
  • <img src={coverImg} alt={title} />
    => alt속성을 안쓰면 경고가 뜸. 모든 img 엘리먼트들은 alt 속성을 가지기 때문 (그냥 alt를 갖는게 더 좋아서 알려주는것)
  • props를 받아왔고 어떤 props를 가지고 있는지 알면 좋을것 같아서 PropTypes를 사용하자
    npm i prop-types 로 설치

react route 세팅

이제 React Router에 대해 배워보자! React Router는 페이지를 전환하는 거임.

  • 위의 사진에선 home 페이지에 있고

  • 위에 처럼 movies/movie.id가 붙은 페이지로 가길 원함

일단 npm i react-router-dom@5.3.0 로 설치하자
(현재는 react-router-dom 6버전을 쓰지만 이 강의(2021)에서는 react-router-dom을 5.3.0버전을 씀. 문법이 다르니 참고하기)

  • components라는 폴더를 생성해 주고 기존의 Movie.js 를 옮겨줌 (다양한 컴포넌트들을 모아놓는 폴더)
  • routes 라는 폴더를 생성해 주고 Detail.jsHome.js파일 만들어줌 (url마다 다른 페이지를 보여주는 용도)

App.js

function App() {
  return null;
}

export default App;

Home.js

import { useState, useEffect } from "react";
import Movie from "../components/Movie";

function Home() {
  const [loading, setLoading] = useState(true);
  const [movies, setMovies] = useState([]);

  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 ? (
        <h1>Loading...</h1>
      ) : (
        <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;
  • 기존의 App.jsHome.js로 옮겨옴

Detail.js

function Detail() {
  return <h1>Detail</h1>;
}

export default Detail;

react-route-dom으로 경로 설정

react-route-dom을 사용해보자!

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
//컴포넌트 import 꼭 해오기

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/movie">
          <Detail />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </Router>
  );
}

export default App;
  • 유저가 /movie path에 있다면 Detail 컴포넌트를 보여줄거임.
  • 유저가 / path에 있다면 Home 컴포넌트를 보여줄거임.

a태그에 href 속성을 안쓰는 이유는?

<h2> <a href="/movie">{title}</a></h2>
이런식으로 하면 페이지 전체가 다 로딩되기 때문이다.

대신 Link를 사용하자

//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((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  );
}

Movie.propTypes = { //PropTypes이라고 하니까 에러났음 propTypes로 바꾸면됨
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Movie;


-제목을 누르면 /movie 페이지로 가는걸 볼 수있음.

react-router-dom 최신6버전

⭐️추가: 강의의 댓글들을 보니 지금은 react-router-dom 5버전의 Switch 문법보다는 최신 6버전 문법을 많이 사용하는것 같았다. 그래서 6버전으로도 바꿔보자!

일단 npm install react-router-dom@6.3.0 6버전으로 설치하고 아래와 같이 작성하면됨

//App.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/movie" element={<Detail />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

공식문서를 참고하자!

//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((g) => (
          <li key={g}>{g}</li>
        ))}
      </ul>
    </div>
  );
}

Movie.propTypes = { //PropTypes이라고 하니까 에러났음 propTypes로 쓰기
  coverImg: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  summary: PropTypes.string.isRequired,
  genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default Movie;
  • Link는 똑같은 문법을 써서 안고쳐도 된다.

경로에 id 추가해서 id값 받아오기

리액트 Router는 다이나믹(동적) url을 지원해줌
다이나믹하다는 건 url에 변수를 넣을 수 있다는 의미임.


-위에처럼 id 변수를 받아오게 만들거임.

  • 데이터안의 id의 모습

  • 위에처럼 파라미터에:id를 작성해줌
    => 유저가 "/movie/:id" 경로로 오면 <Detail /> 페이지를 보여줄거임

  • 이렇게 되면<Movie/> 컴포넌트에 id값이 있어야 하는데 받아오는 props중에 id가 없으므로 부모 요소인Home에서 id를 props로 가져오자!

  • Home -> <Movie/> 컴포넌트로 props로 id를 넘겨주는 코드를 작성하고

  • props로 id를 받아 Link태그에 넣어줌
  • 이제 영화제목을 클릭하면 동적url로 이동함

파라미터에 오는 id값이 뭔지 알아내야 하는데 어떻게 알아낼까?
💡 useParams함수를 사용하면 됨!

useParams() 사용해서 React Router에서 변수값 넘겨받기

import { useParams } from 'react-router-dom';

function Detail() {
  const x = useParams()
  console.log(x);
  return <h1>Detail</h1>;
}

export default Detail;
  • useParams(): 리액트 라우터에서 제공하는 url에 변경되는 값을 반환해주는 함수
  • console.log()로 잘 받아오는지 확인하자
  • App.js에서 <Route path="/movie/:id" element={<Detail />} />의 :id 값을 반환해준거임

movie_id의 배열 json으로 받아오기

import { useEffect } from "react";
import { useParams } from "react-router-dom";

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;

  • 영화제목을 클릭하면 디테일 페이지로 감

  • id에 해당하는 ⭐️하나의 영화정보 데이터를 가져올 수 있음.
profile
Frontend Developer

0개의 댓글