첫번째 목적
- Banner 컴포넌트에서 배경 이미지를 사용하려고 한다.
- 사용될 이미지 소스는 The Movie DB API 에서 받은 데이터를 사용할 것이다.
- API 요청을 위한 Axios 인스턴스를 생성하여 재사용성을 높이려 한다.
- Banner 컴포넌트가 랜더링 될 때, Axios 인스턴스가 실행되어야 한다.
방법
- useEffect를 사용하여, 컴포넌트가 랜더링 됨과 동시에 aixos 인스턴스를 실행하도록 구현한다.
useEffec(() => {
fetchData() }, [])
- api 폴더 내, axios.js에서 axios 인스턴스를 만들고, 이를 banner 컴포넌트에서 import 하여 사용한다.
import axios from "axios";
import requests from "./requests";
export const instance = axios.create({
baseURL: "https://api.themoviedb.org/3/" ,
params: {
api_key: "a38fa376de46b95eb6b7c9eff2b1116c",
language: "ko-KR",
},
});
export const fetchData = () => {
const request = instance.get(requests.fetchNowPlaying);
console.log("request :", request);
};
- Banner 컴포넌트를 App 컴포넌트에 import 하여 원하는 데이터가 들어왔는지 확인한다.
문제: "다시 확인! 비동기 처리의 필요성"
- App 컴포넌트를 랜더링 하였으나, 콘솔창에는 다음 과 같은 결과가 나왔다.
request : Promise {<pending>}
원인 : 동기적 데이터 흐름
- 의도는 Axios 인스턴스 요청으로 서버에서 Response가 도착한 뒤 결과값을 콘솔로 확인하려 했다.
- 그러나, 실제로는 Response가 오지 않은 상태에서 결과 값을 콘솔로 확인 했기 때문에, pending 이 된 것이다.
이는 아래 이미지를 통해 이해할 수 있다.
해결 : 비동기 적용
- 위 문제는 Axios 요청과 응답을 동기적으로 진행했기 때문에 발생한 사소한 문제이다.
- 이를 해결하기 위해, 의도대로 Response 가 도착한 뒤 호출할 수 있게 비동기로 전환하면 간단히 해결할 수 있다.
- 이때, fetch를 사용해도 되지만, then 체이닝이나, 결과값을 JSON으로 전환해야하는 번거로움이 있으므로, Async & Await를 사용하여 간결하게 작성했다.
export const fetchData = async () => {
const request = await instance.get(requests.fetchNowPlaying);
console.log("request :", request);
};
두번째 목적
- 호출된 영화 정보들 중 랜덤으로 영화 하나의 ID를 가져오려고 한다.
- 랜덤으로 가져온 영화 ID의 상세 정보(비디오 정보 포함)를 가져온다.
방법
- 랜덤한 값의 범위는 전달 받은 영화들의 개수 만큼 설정한다.
- 이를 구현하기 위해, Math.floor와 Math.random() 을 사용한다.
Math.floor(Math.random() * request.data.results.length)
- 위와 같이 얻게 될 랜덤 값을 전달받은 영화 데이터의 인덱스로 사용하여, 원하는 영화 ID를 가져온다.
export const fetchData = async (setState) => {
const requestMovieData =
await axiosInstance.get(requests.fetchNowPlaying);
console.log("requestMovieData :", requestMovieData);
const movieId =
requestMovieData.data.results[
Math.floor(Math.random() * requestMovieData.data.results.length)
].id;
console.log("movieId :", movieId);
- 랜덤 한 영화 ID를 얻었으므로, 이를 사용하여, 해당 영화 ID의 상세 정보를 받아 온다.
- 이때, 비디오 정보도 포함해서 받아 와야 한다.
이는 The Movie API Append To Response를 참고해서 진행했다.
export const fetchData = async (setState) => {
const requestMovieData = await axiosInstance.get(requests.fetchNowPlaying);
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" },
});
};
문제 발생 : 스코프에 막힌 상태 변경
- 영화 상세 정보를 가져와 movieDetail 에 담아놓았다.
- 상세 정보를 가져오는 fetchData 인스턴스를 Banner 컴포넌트에서 사용하여, 영화 정보 상태를 변경하려고 했다.
- 그러나, 인스턴스 내에서 가져온 영화 상세 정보 데이터는 인스턴스를 import한 Banner 컴포넌트에서 사용하려 할 때, 스코프에 막혀 데이터를 인용할 수 없는 문제가 발생했다.
- 그런데, 이는 조금만 생각하면 너무 간단하게 풀리는 문제였다.
- fetchData 인스턴스의 매개변수로 컴포넌트가 사용할 State를 전달받으면 되는 문제였다.
- 이렇게하면, 필요한 movieDetail가 스코프 내에서 Banner 컴포넌트의 setMovie에 인용될 수 있게 된다.
export const fetchData = async (setState) => {
const requestMovieData =
await axiosInstance.get(requests.fetchNowPlaying);
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);
};
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;