useEffect에서 async 키워드를 사용했을 때 생긴 일들

박세진·2022년 10월 30일
3

개인적으로 토이 프로젝트(Movie trailer)를 만들면서, 관심사의 분리에 따라서 분리해보기 위해서 movie API 호출하는 함수를 분리해보고자 했다.

async 함수를 useEffect에서 호출했을 때

// movieApi.js 파일
import axios from 'axios';

const MOVIE_API_KEY = process.env.REACT_APP_MOVIE_API_KEY;
const popularMovieApi = `https://api.themoviedb.org/3/movie/popular?api_key=${MOVIE_API_KEY}`;

export const getPopularMovieList = async () => {
  try {
    const response = await axios.get(popularMovieApi);
    const homeMovieList = response.data;
    return homeMovieList.results;
  } catch (error) {
    console.log(error);
  }
};

이렇게 API를 호출하는 함수를 분리하고, 컴포넌트 파일에서 useEffect를 이용해서 마운트 됐을 때, useState의 state를 이용해서 데이터를 관리하고자 했다.

// homePage.jsx 파일
import { useEffect, useState } from 'react';

import { getPopularMovieList } from '../apis/MovieApi';
import Card from '../components/common/Card';

const HomePage = () => {
  const [movieList, setMovieList] = useState([]);

  useEffect(() => {
    const data = getPopularMovieList();
    setMovieList(data);
  }, []);
  
  return (
    <div>
      {movieList.map((item) => (
        <Card key={item.id} imgSrc={item.poster_path} title={item.title} />
      ))}
    </div>
  );
};

export default HomePage;

하지만 계속 movieList는 map 메서드를 사용할 수 없다는 오류가 출력됐고, 그래서 변수 data에 값이 잘 담기는지 보기 위해서 console을 찍어보았다.

console을 확인해보니 data는 pending 상태였다.

이 문제를 해결하기 위해서는 일단 async ... await에 대해서 알아야 된다.

async ... await

비동기적인 동작을 동기적으로 제어하기 위해서 콜백함수를 사용할 수 있는데, 그러다 보면 콜백 지옥에 빠지게 된다. 콜백 지옥에서 탈출하기 위해서 Promise를 사용할 수 있다. async ... await 키워드를 사용하면 프라미스를 편하게 사용할 수 있다.

  • async ... await을 사용하기 위해서는 function 키워드 앞에 async 키워드를 사용해야 되고, async 키워드가 붙은 함수는 반드시 프라미스를 반환한다.

  • await 키워드는 async 함수 안에서만 작동하고, await 키워드를 만나면 프라미스가 처리될 때까지 기다렸다가 Promise.then처럼 프라미스의 값을 얻을 수 있도록 해주는 문법이다.

함수를 호출했는데 Promise{} 상태였던 것은 async 키워드가 붙은 함수는 반드시 프라미스를 반환하기 때문에 프라미스가 반환된 것이었다. 그래서 useEffect에서 async ... await 키워드를 사용하여 다시 문제를 해결해보려고 했다.

  useEffect(async () => {
    const response = await getPopularMovieList();
    setMovieList(response);
  }, []);
  

이렇게 작성했더니, 아래와 같은 오류메시지가 나타났다.

useEffect는 `clean-up 함수 이외의 것을 반환해서는 안된다고 경고가 출력됐고, useEffect(async() => ... )를 작성했거나 Promise를 반환한 것 같다고 에러 메시지가 출력됐다. 그러면서 대신 비동기 함수를 effect 함수 안에서 쓰고, 즉시 호출하라고 방법을 알려줬다.

useEffect(() => {
  async function fetchData() {
    const movieList = await getHomeMovieList();
    setMovieList(movieList);
  }

  fetchData();
}, []);
  • 이렇게 작성하니, 제대로 데이터를 얻을 수 있었다.

하지만 API 호출 함수를 분리한 이유는 좀 더 코드를 깔끔하게 작성해보기 위해서 분리한 것이었다. 이렇게 알려준대로 작성한 것은 내가 생각했던 의도와는 달랐다.

그렇게 고민을 하다가, 주말마다 방문하는 코드숨 공부방에 계신 윤석 멘토님에게 질문을 했다.
윤석님께서 콜백지옥과 프라미스에 대해서 설명을 해주고, async 함수는 프라미스를 반환하니까 간단하게 .then을 이용하면 된다고 알려줬다.

useEffect(() => {
  getPopularMovieList().then((result) => setMovieList(result));
}, []);

이 코드를 좀 더 깔끔하게 작성하고자 한다면?

 useEffect(() => {
  getPopularMovieList().then(setMovieList);
}, []);

// result가 중복되기 때문에 제거해주고 setMovieList만 작성해도 된다.

설명을 듣고, async ... await에 대해서 공부한 결과 모던 자바스크립트 튜토리얼 사이트에서도 문제에 대한 답을 찾을 수 있었다.

해답 : 바로 async가 아닌 함수에서 async 함수를 호출하기 위해서는 .then()을 붙이면 됐던 것이다.


모던자바스크립트 튜토리얼

이렇게 useEffect에 effect 함수에 async 키워드를 사용했을 때, 생긴 일들에 대해서 정리해보았습니다. 혹시 내용에서 더 설명해주고 싶은 내용이 있거나 잘못된 정보가 있을 경우 알려주시면 감사하겠습니다! 😊

profile
경험한 것을 기록

0개의 댓글