본문 바로가기
React study/React JS로 영화 웹 서비스 만들기

[React] 7-3. React로 영화 앱 만들기 (1)

by 카누가 좋아요 2023. 6. 7.

📌 영화 앱 만들기 시작!

✔️ 영화를 보여주고, 그 영화들에 대한 정보도 좀 보여주고, 거기에 링크까지 넣어 그 영화에 대해 더 많은 것을 찾아볼 수 있도록 앱에서 다른 곳으로 연결을 시켜주는 기능을 넣은 영화 앱을 제작해 볼 것이다.


우리는 지금까지 하나의 상호작용이 있는 스크린을 만들어 왔다.

하지만 다른 페이지로 전환하는 것을 지금부터 해 볼 것이다.



1️⃣ 가장 먼저, 영화들을 화면에 모두 보여주기 위한 API가 필요하다.


많은 영화 정보들을 받을 수 있는 API를 통하여 영화를 가져올 것이다.

주소창에 https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year 라고 입력하면 json 형식의 많은 영화 데이터들을 볼 수 있다.

주소에 minimum_rating을 8.5라고 입력하였는데, 이는 별점이 8.5점 이상인 영화들만 추출해내기 위해 작성한 것이다.

또한 sort_by=year라고도 입력하였는데, 이는 최신 영화순으로 데이터를 보여준다.



2️⃣ 이전 시간에 했던 것과 같이 API를 모두 받아오기 전까지는 화면에 로딩 중을 보여주고, 로딩이 끝났을 때 영화들을 보여주어야 한다.


2-1.

우선 loading state를 만들어 초깃값을 true로 설정하고 (처음에는 무조건 API를 받아오는 중이므로) loading이 true일 때 화면에 Loading을 보여주고, false일 때 일단은 아무것도 보여주지 않는 코드를 작성하였다.

(null 자리에 나중에 영화들를 보여주는 코드가 들어갈 것이다.)


import { useState } from "react";

function App() {
  const [loading, setLoading] = useState(true);
  return <div>{loading ? <h1>Loading...</h1> : null}</div>;
}

export default App;

2-2.

movie API는 처음 딱 한번만 받아오면 되므로 useEffect를 이용하여 API를 받아오는 코드는 한번만 실행되도록 한다.


import { useState, useEffect } from "react";

function App() {
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    // 주목!
    fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    )
      .then((response) => response.json())
      .then((json) => console.log(json));
  }, []);
  return <div>{loading ? <h1>Loading...</h1> : null}</div>;
}

export default App;

첫번째 .then에 들어가는 함수의 인자(response)에 우리가 위 주소에 요청을 보내 받은 정보가 들어가는데 response는 HTTP 응답 전체를 나타내는 객체이다. 따라서 JSON 본문 컨텐츠만을 추출하기 위해 json 메서드를 호출하였다.


두번째 .then에 들어가는 함수의 인자(json)에 아까 추출해낸 json 파일이 들어가는데, 우리는 이를 console창에서 확인해본 결과 객체를 하나 받는데, data에 속해 있는 movies 배열에 많은 영화 정보 객체들이 담겨 있는 것을 볼 수 있다.


이제 받아온 movie data를 movies state를 새로 만들어 그곳에 할당해준다.


.....
function App() {
  .....
  const [movies, setMoives] = useState([]);     // 주목!
  useEffect(() => {
    fetch(
      "https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year"
    )
      .then((response) => response.json())
      .then((json) => setMoives(json.data.movies));     // 주목!
  }, []);
  .....

2-3.

이제 loading을 끝냈으므로 setLoading 함수의 인자로 false를 전달해 주어야 한다.


unction App() {
  .....
      .then((json) => {
        setMoives(json.data.movies);
        setLoading(false);     // 주목!
  .....

사실 요즘 보편적으로 사용하는 것은 then이 아닌 async-await이다.


↪️ async-await를 이용하여 코드 다시 작성해보기


function App() {
  .....
  const getMovies = async () => {
    const response = await fetch(
      `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year`
    );
    const json = await reponse.json();
    setMoives(json.data.movies);
    setLoading(false);
  };
  useEffect(() => {
    getMovies();
  }, []);
  .....

좀 더 간단하게 적고 싶으면 다음과 같이 await을 한번에 2번 적용하여 response 변수를 따로 만들지 않는 형식으로 적을수도 있다.


function App() {
  .....
  const getMovies = async () => {
    const json = await (
      await fetch(
        `https://yts.mx/api/v2/list_movies.json?minimum_rating=8.5&sort_by=year`
      )
    ).json();
    setMoives(json.data.movies);
    setLoading(false);
  };
 .....

다음과 같이 console.log 해서 console창에서 결과를 살펴보자.


function App() {
  .....
  useEffect(() => {
    getMovies();
  }, []);
  console.log(movies);
  .....

처음에 뜨는 빈 배열([])은 Loading일 때의 상태이다. (처음 렌더링될 때 상태)

두 번째에 뜨는 movie 정보들이 채워진 배열은 setMovies(json.data.movies)로 movie state가 변경되었기 때문에 나타난다.

세 번째에도 똑같은 배열이 뜨는데, 이는 setMovies(json.data.movies) 다음에 실행된 setLoading(false)로 인해 loading state가 변경되어 컴포넌트가 재렌더링 되었기 때문이다.



3️⃣ map 메서드를 사용하여 movies에 들어 있는 정보를 화면에 보여준다.


3-1.

앞에서 loading이 true일 때는 화면에 Loading 글자를 보여주고, false일때는 null을 반환하는 코드를 작성하였었다.

loading이 false라는 것은 API를 다 받아와서 더 이상 로딩이 필요하지 않다는 의미이므로 null 대신 영화들을 화면에 보여 줄 것이다.


function App() {
  .....
  return (
    <div>
      {loading ? (
        <h1>Loading...</h1>
      ) : (
        <div>     // 주목!
          {movies.map((movie) => (     // 주목!
            <div key={movie.id}>     // 주목!
              <h2>{movie.title}</h2>     // 주목!
              <p>{movie.summary}</p>
            </div>     // 주목!
          ));
        </div>      // 주목!
      )}
    </div>
  );
}

movies에 들어 있는 배열을 살펴보면 각 배열의 요소로 하나의 영화의 정보가 들어 있는 객체들이 있었고 그 객체 안에 영화를 구분해주는 고유한 id영화 제목인 title, 영화 내용 요약인 summary가 존재하는 것을 볼 수 있었다.

id를 이용하여 div tag의 key를 설정해 주었고, title은 화면에 보여주도록 코드를 작성하였다.


<div key={movie.id}>
  <h2>{movie.title}</h2>
  <p>{movie.summary}</p>
</div>

바로 위 코드에 적혀 있는 component들은 movies array에 있는 각 movie에서 변형되어 나온 것이라는 것을 기억해야 한다.


3-2.

이번에는 영화 장르도 같이 보여주려고 한다.

그런데 movie 객체를 살펴보면 'genres'의 value는 배열로 존재하는 것을 볼 수 있다. 따라서 그 요소들을 모두 꺼내어 주기 위해 여기서도 map을 사용한다.


return (
    .....
              <ul>
                {movie.genres.map((g) => (
                  <li key={g}>{g}</li>
                ))}
              </ul>
    .....

g는 genres의 value의 요소 하나하나를 의미한다.

따라서 undordered list 형식으로 장르 하나하나를 화면에 보여준다.

g는 하나의 영화에서 고유한 값이기 때문에 li의 key로도 사용이 가능하다.


3-3.

이제 cover image까지 가져와 보겠다.

movie 객체를 살펴보면 사이즈별로 cover image key와 value(이미지 링크)가 있는 것을 볼 수 있다. 우리는 medium_cover_image를 가져올 것이다.


{movies.map((movie) => (
  <div key={movie.id}>
    <img src={movie.medium_cover_image} />     // 주목!
    <h2>{movie.title}</h2>
    .....

movie.medium_cover_image는 이미지 주소 그 자체이므로 img 태그의 src 속성의 값으로 적어준다.



지금까지 우리가 한 것은 API를 한번만 받아오고, 그것을 state(movies)에 저장해 state로부터 받은 data를 화면에 보여주는 것을 반복한 것 뿐이다!


다음 시간에는 사람들이 영화의 제목을 클릭하면 그 영화에 관련된 또 다른 페이지로 가게 만들 것이다. (페이지 전환)

movie 객체에는 그 영화에 관련된 url 정보가 담겨 있다.

우리는 movie API 주소의 movie_id= 에 원하는 영화의 id를 입력하여 그 영화에 대한 정보를 볼 수 있다. 그 안에서 url 정보들을 볼 수 있는 것이다.

댓글