[개인프로젝트] API 호출(1)

배상건·2023년 3월 15일
0

프로젝트

목록 보기
5/10

첫번째 목적

  1. Banner 컴포넌트에서 배경 이미지를 사용하려고 한다.
  2. 사용될 이미지 소스는 The Movie DB API 에서 받은 데이터를 사용할 것이다.
  3. API 요청을 위한 Axios 인스턴스를 생성하여 재사용성을 높이려 한다.
  4. Banner 컴포넌트가 랜더링 될 때, Axios 인스턴스가 실행되어야 한다.

방법

  1. useEffect를 사용하여, 컴포넌트가 랜더링 됨과 동시에 aixos 인스턴스를 실행하도록 구현한다.
useEffec(() => {
  /*Axios 인스턴스 : */fetchData() }, [])
  1. api 폴더 내, axios.js에서 axios 인스턴스를 만들고, 이를 banner 컴포넌트에서 import 하여 사용한다.
// src > api > axios.js 

import axios from "axios";
import requests from "./requests";

export const instance = axios.create({
  baseURL: "https://api.themoviedb.org/3/" /* 중복 URL */,
  params: {
    api_key: "a38fa376de46b95eb6b7c9eff2b1116c",
    language: "ko-KR",
  },
});

export const fetchData = () => {
  // 현재 상영 중인 영화 정보 가져오기
  const request = instance.get(requests.fetchNowPlaying);
  // request 확인하기
  console.log("request :", request);
};
  1. Banner 컴포넌트를 App 컴포넌트에 import 하여 원하는 데이터가 들어왔는지 확인한다.

문제: "다시 확인! 비동기 처리의 필요성"

  1. App 컴포넌트를 랜더링 하였으나, 콘솔창에는 다음 과 같은 결과가 나왔다.
request : Promise {<pending>}

원인 : 동기적 데이터 흐름

  1. 의도는 Axios 인스턴스 요청으로 서버에서 Response가 도착한 뒤 결과값을 콘솔로 확인하려 했다.
  2. 그러나, 실제로는 Response가 오지 않은 상태에서 결과 값을 콘솔로 확인 했기 때문에, pending 이 된 것이다.
    이는 아래 이미지를 통해 이해할 수 있다.

해결 : 비동기 적용

  1. 위 문제는 Axios 요청과 응답을 동기적으로 진행했기 때문에 발생한 사소한 문제이다.
  2. 이를 해결하기 위해, 의도대로 Response 가 도착한 뒤 호출할 수 있게 비동기로 전환하면 간단히 해결할 수 있다.
  3. 이때, fetch를 사용해도 되지만, then 체이닝이나, 결과값을 JSON으로 전환해야하는 번거로움이 있으므로, Async & Await를 사용하여 간결하게 작성했다.
export const fetchData = async () => {
  // 현재 상영 중인 영화 정보 가져오기
  const request = await instance.get(requests.fetchNowPlaying);
  // request 확인하기
  console.log("request :", request);
};

두번째 목적

  1. 호출된 영화 정보들 중 랜덤으로 영화 하나의 ID를 가져오려고 한다.
  2. 랜덤으로 가져온 영화 ID의 상세 정보(비디오 정보 포함)를 가져온다.

방법

  1. 랜덤한 값의 범위는 전달 받은 영화들의 개수 만큼 설정한다.
  2. 이를 구현하기 위해, Math.floor와 Math.random() 을 사용한다.
Math.floor(Math.random() * request.data.results.length)
  1. 위와 같이 얻게 될 랜덤 값을 전달받은 영화 데이터의 인덱스로 사용하여, 원하는 영화 ID를 가져온다.
export const fetchData = async (setState) => {
  // 현재 상영 중인 영화 정보 가져오기
  const requestMovieData = 
        await axiosInstance.get(requests.fetchNowPlaying);
  // request 확인하기
  console.log("requestMovieData :", requestMovieData);
  const movieId =
    requestMovieData.data.results[
      Math.floor(Math.random() * requestMovieData.data.results.length)
    ].id;
  console.log("movieId :", movieId);

  1. 랜덤 한 영화 ID를 얻었으므로, 이를 사용하여, 해당 영화 ID의 상세 정보를 받아 온다.
  2. 이때, 비디오 정보도 포함해서 받아 와야 한다.
    이는 The Movie API Append To Response를 참고해서 진행했다.
export const fetchData = async (setState) => {
  // 현재 상영 중인 영화 정보 가져오기
  const requestMovieData = await axiosInstance.get(requests.fetchNowPlaying);
  // request 확인하기
  console.log("requestMovieData :", requestMovieData);
  const movieId =
    requestMovieData.data.results[
      Math.floor(Math.random() * requestMovieData.data.results.length)
    ].id;
  console.log("movieId :", movieId);

  const { data: movieDetail } = 
        await axiosInstance.get(`movie/${movieId}`, {
    params: { append_to_response: "videos" },
  });
};

문제 발생 : 스코프에 막힌 상태 변경

  1. 영화 상세 정보를 가져와 movieDetail 에 담아놓았다.
  2. 상세 정보를 가져오는 fetchData 인스턴스를 Banner 컴포넌트에서 사용하여, 영화 정보 상태를 변경하려고 했다.
  3. 그러나, 인스턴스 내에서 가져온 영화 상세 정보 데이터는 인스턴스를 import한 Banner 컴포넌트에서 사용하려 할 때, 스코프에 막혀 데이터를 인용할 수 없는 문제가 발생했다.
  4. 그런데, 이는 조금만 생각하면 너무 간단하게 풀리는 문제였다.
  5. fetchData 인스턴스의 매개변수로 컴포넌트가 사용할 State를 전달받으면 되는 문제였다.
  6. 이렇게하면, 필요한 movieDetail가 스코프 내에서 Banner 컴포넌트의 setMovie에 인용될 수 있게 된다.
// src > api > axios.js
export const fetchData = async (setState) => {
  // 현재 상영 중인 영화 정보 가져오기
  const requestMovieData = 
        await axiosInstance.get(requests.fetchNowPlaying);
  // request 확인하기
  console.log("requestMovieData :", requestMovieData);
  const movieId =
    requestMovieData.data.results[
      Math.floor(Math.random() * requestMovieData.data.results.length)
    ].id;
  console.log("movieId :", movieId);

  const { data: movieDetail } = 
        await axiosInstance.get(`movie/${movieId}`, {
    params: { append_to_response: "videos" },
  });
  setState(movieDetail);
};
// src > components > Banner.js
import React, { useEffect, useState } from "react";
import { fetchData } from "../api/axios";

const Banner = () => {
  const [movie, setMovie] = useState([]);
  useEffect(() => {
    fetchData(setMovie);
  }, []);
  return <div>Banner</div>;
};

export default Banner;
profile
목표 지향을 위해 협업하는 개발자

0개의 댓글