비동기 짚고 넘어가기

front-end developer·2022년 11월 15일
0

비동기적 코드에 대한 이해

이전 프로젝트에서는 서버와 통신해서 데이터를 받아오는 과정에 대해 비동기라는 인식이 부족했다. 이번에 비동기에 대한 이해를 제대로 짚고 넘어가자는 생각이 있었다.

  • 자바스크립트는 싱글스레드 기반의 언어
  • 즉, task들을 순차적으로 처리하는 시스템인데 소요시간이 긴 task의 경우 이 task를 처리하는 동안 나머지 작업이 이루어지지 않으면 비효율적이기 때문에 비동기 API를 사용하게 된다.
  • 서버와 통신하는 과정도 이러한 이유로 비동기적인 특성을 이용한다.
    • fetch나 axios의 통신은 비동기적인 특성을 갖고 있다.
    • 그렇기때문에 fetch,axios를 통해 얻은 결과값은 비동기처리가 필요하게 된다.
    • 이전에는 공식처럼 fetch를 사용 후 then()으로 받아오는 로직을 사용했었는데, 이 방식이 비동기처리라는 이해가 부족했다.
  • 비동기API를 활용해 얻은 결과들은 Promise를 반환하도록 되어 있다.
    • 예전에는 각 라이브러리별로 비동기에 대한 처리방식이 달랐다. (callback 함수를 이용하는 등)
    • 그러나 Promise라는 객체가 등장함으로써 비동기 처리 방식이 좀 더 명확해지고 효율적이 되었다.
    • 또 시간이 지나면서 promise 보다 더 직관적이고 동기적인 코드로 해석하기 좋은 async await 가 등장했고 사람들이 편해서 많이 사용했다고 한다.
    • 이 asycn await를 이번 프로젝트에서 제대로 이해하고 사용하기로 했다.
// src/api/users/get.js

import axios from 'axios';
const url = process.env.REACT_APP_PUBLIC_API_URL;

export async function getUsersApi(payload) {
  const token = localStorage.getItem('login-token');
  const data = await axios.get(url + '/user', {
    params: payload,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
  });
  return data;
}
  • user 페이지에서 data를 받아오는 로직을 나타내는 get.js 파일이다.

  • axios.get() 함수를 통해 데이터를 받아오면 원래 promise 형태로 반환된다.

    • 이때, promise의 상태는 pending 상태로 데이터를 아직 다 받아오지 않은 상태이다.
    • 자바스크립트는 axios라는 비동기함수를 만나면 따로 비동기API에 데이터를 받아오는 task를 넘기고, 다시 본래의 스레드(call stack)으로 넘어온다.
    • 이때, 아무 값도 받는것이 아니라 promise 객체 형태를 받는데, 처음 받았을 때는 pending 상태이다.
    • 이후 비동기 API( web API)에서 데이터 받아오는 과정이 정상적으로 완료되면 Promise 객체는 fulfilled 상태가 되어 promise 객체안에 데이터가 담기게 된다.
    • 이 promise 객체는 .then()을 통해 fullfiled되어 받은 데이터를 처리할 수 있으며, catch()는 에러를 처리할 수 있다.
  • async await

    • 받아온 promise에 대해 동기적인 형태로 처리하는 다른 방식이 있는데 그것이 async await다.

    • getUsersApi 함수에 async를 붙이고 비동기 axios 함수에 await를 붙이면 result라는 변수에는 Promise 객체가 아닌 서버에서 보내주는 실질적인 data가 담긴다.

      • 이 부분에서 코드는 data가 들어올때 까지 기다리고, 그 다음 줄을 실행한다.
    • 그러나, await를 사용안하고 바로 result에 담으면 Promise 객체가 담긴다.

    • getUsersApi()를 다른 js 파일에서 import해서 가져오면,

      // src/hooks/useFetch.js
      
      import { getUsersApi } from 'api/user/get';
      
      //중략
      
      const fetchData = async (order_type, sort_type) => {
          switch (page) {
            case 'userinfo':
              const payloadUser = {
                ...store.userFilter,
                page: currentPage,
                perPage: perPage,
                pagination: 'Y',
                order: order_type,
                sort: sort_type,
              };
      
              const result = await getUsersApi(payloadUser);
      				// console.log('result', result) 
      
              try {
                setBuckets(result.data.data);
                setTotalPage(result.data.total);
              } catch ({ response }) {
                if (response.status === 401) {
                  logout();
                }
              }
    • 똑같이 getUsersApi 함수 앞에 await를 사용해서 result에 담으면 실제 data가 담긴다.

    • 그러나, await를 사용하지 않고 담으면 Promise 객체가 담겼다. (getUserApi 내부는 async await 형태임에도 불구하고!)

      • 이 부분이 이해가 안되어서 console.log()를 통해 로직 순서를 확인해 봤다.

      • 먼저 getUsersApi 앞에 await를 지우고 다음줄에 console.log로 result를 찍었다.

        // src/api/users/get.js
        const result = getUsersApi(payloadUser);
        console.log('result', result);
      • 그리고 getUsersApi 로직 내부에서는 axios() 이후 코드 줄에 console.log()를 추가했다.

        // src/hooks/useFetch.js
        import axios from 'axios';
        const url = process.env.REACT_APP_PUBLIC_API_URL;
        
        export async function getUsersApi(payload) {
          console.log('비동기 시작');
          const token = localStorage.getItem('login-token');
          const data = await axios.get(url + '/user', {
            params: payload,
            headers: {
              'Content-Type': 'application/json',
              Authorization: `Bearer ${token}`,
            },
          });
          console.log('비동기 종료');
          console.log('result', data);
          return data;
        }
      • 원래 내가 이해한대로라면 getUsersApi 가 호출되었을 때, getUsersApi 내부에서 console이 차례대로 찍히고 나서, get.js에서 console.log('result', result)가 찍혀야했다고 생각했다.

      • 그러나 결과는 다음과 같았다.

      • getUsersApi 호출하고 내부에서 await를 기다리는 동안 바로 return이 일어났고 그 return 값에는 Promise 객체가 담긴다.

      • 이 Promise 객체안에는 data가 담겨져 있었다.

      • 그 후, 기다렸던 data가 담기고 getUsersApi 내부 로직이 종료되었다.

        ⇒ 결국, async 함수는 무조건 Promise 객체를 반환하며, axios 비동기 함수와 같은 원리로 동작한다. 그래서 반환 값이 promise 였던 것.

profile
학습한 지식을 개인적으로 정리하기 위해 만든 블로그입니다 :)

0개의 댓글