본문 바로가기

Frontend/JavaScript

프론트 비동기 작업 이해하기(feat. React)

프론트 단에서 데이터를 랜더링할 때, 데이터를 먼저 패치받아야 한다. 따라서 위의 작업은 비동기 방식으로 진행 되어야 한다. 자바스크립트에서 비동기 작업을 처리하는 방법에는 여러가지가 있다. 그 중 몇 가지 방법에 대해 설명하겠다.

콜백함수

function increase(number, callback) {
    setTimeout(()=> {
        const result = number +10;
        if(callback)
            callback(result);
    }, 1000);
}

increase(0, result => {
    console.log(result);
})

하지만 만약 비동기적으로 처리해야할 작업의 수가 많아진다면 어떻게 될까? 작업의 수만큼 콜백의 깊이가 깊어지고 가독성이 매우 나빠지게 될 것이다. 우리는 이러한 상황을 콜백 지옥이라고 부른다.

Promise

Promise는 콜백 지옥 상황을 해결하기 위해 ES6부터 도입된 기술이다. 아래 예시를 통해 Promise의 사용 법을 알아보자. 코드의 로직은 콜백 함수로 구현 한 것과 같다.

function increase(number) {
    const promise = new Promise((resolve, reject)=> {
        setTimeout(()=> {
            const result = number + 10;
            if(result > 50) {
                const e = new Error('Too Big Number');
                return reject(e); //실패하면서 Error 객체 반환
            }
            resolve(result); //성공하면서 값 result 반환
        }, 1000);
    });
    return promise;
}

increase(0)
    .then(number => { // resolve된 값은 .then을 통해서 받아 올 수 있다.
        console.log(number);
        return increase(number);
    })
    .catch(err => { // 비동기 실행 도중 reject된 값은 .catch에서 처리한다.
        console.log(err);
    });

async/await

aysnc/await는 Promise를 더욱 쉽게 사용할 수 있도록 하는 ES2017(ES8) 문법이다. 이 문법의 사용 방법은 비동기 처리하는 함수의 앞에 async 키워드를 추가하고, 해당 함수 내부에서 Promise앞 부분에 await 키워드를 사용한다. increase 함수는 위와 같게 사용한다.

const runTask = async () => {
  try {
    let result = await increase(0);
    console.log(result);
    result = await increase(result);
    console.log(result);
  } catch(e) {
    console.log(e);
  }
}

runTask();

React에서 데이터 연동하기

데이터를 API요청으로 불러올 때, useEffect 훅을 사용해서 컴포넌트가 처음 렌더링 된 직후에 API요청을 하면 된다. 주의할 점은 useEffect에 등록하는 함수에 async를 붙이면 안 된다는 것이다. useEffect에서 반환해야 하는 값은 뒷정리 함수이기 때문이다.

따라서 useEffect 내부에서 async/await를 사용하고 싶다면, 함수 내부에 async 키워드가 붙은 또 다른 함수를 만들어서 사용해야 한다.

추가로 loading이라는 상태를 두어 API 요청 전후 여부를 저장해 둘 것이다.

import React, { useState, useEffect } from "react";
import axios from "axios";

const NewsList = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [news, setNews] = useState();

  useEffect(() => { // useEffect를 사용해 첫 번째 렌더링을 한 직후 데이터 로드
    const fetchData = async () => { // async/await는 useEffect에 사용하지 말고 안에 따로 지정한 함수 안에서 처리
      try { // try catch 문법
        const res = await axios.get("https://someURL.com");
        setNews(res.data);
      } catch (e) {
        console.log(e);
      }
      setIsLoading(false);
    };
    fetchData();
  }, []);

  if (isLoading) return <div>Loading...</div>; // 로딩중이면 로딩화면 렌더링
  if (!news) return <div>No Data...</div>; // 데이터가 존재하지 않으면 그에 따른 처리

  return ( // 정상 데이터 렌더링
    <div>
      {news.map((newsItem, index) => (
        <div key={index}>{newsItem}</div>
      ))}
    </div>
  );
};

export default NewsList;

'Frontend > JavaScript' 카테고리의 다른 글

JEST  (0) 2021.08.31
history, location, match  (0) 2021.07.07
2020-05-21 :: Promise  (0) 2021.05.21
200824  (0) 2020.08.24
200816 DOM  (0) 2020.08.16