본문 바로가기
JavaScript/React

React) 비동기 작업의 이해 - 콜백함수, Promise, async/await, axios

by 박채니 2022. 11. 24.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

비동기 작업의 이해

동기적 처리는 요청이 끝날 때까지 기다리는 동안 중지 상태가 되므로 다른 작업을 할 수 없습니다.

즉, 요청이 끝난 후 다음 작업이 실행되는 것

 

하지만 비동기 처리는 웹 애플리케이션이 멈추지 않으므로 동시에 여러 요인을 처리할 수 있고, 기다리는 동안 다른 함수를 호출할 수도 있습니다.

https://chanychu.tistory.com/281

 

Javascript) 동기식 / 비동기식 - Timer API, DOM 관련 이벤트 처리, DOM 연속

안녕하세요, 코린이의 코딩 학습기 채니 입니다. 개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다. 동기 (synchronous) / 비동기 (asynchronous) 비동기 - Javascript는 싱글 쓰레드로 진

chanychu.tistory.com

 

setTimeout 함수를 사용해 특정 작업을 예약하여 비동기적 처리를 할 수도 있습니다.

function printMe() {
    console.log('Hello world');
}
setTimeout(printMe, 3000);
console.log('대기 중..');

// 콘솔 출력 값
대기 중..
Hello world

setTimeout 함수를 호출 후 3초 동안 멈추는 것이 아닌 코드가 위부터 아래까지 모두 호출된 후 3초 뒤에 printMe 함수가 호출되는 것을 확인할 수 있습니다.

 

자바 스크립트에서 비동기 작업 시 흔히 사용하는 방법 중 하나인 콜백함수를 사용한 것입니다.

 

콜백 함수

 

파라미터 값이 주어지면 1초 뒤 10을 더해 반환하는 함수가 있다고 가정,

해당 함수가 처리된 직후 어떠한 작업을 하고 싶다면 아래와 같이 작업해야할 것입니다.

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

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

// 콘솔 출력값
10

콜백 함수를 이용해 10이 더해진 result 값이 출력!

 

만일 1초에 거쳐서 10, 20, 30, 40을 출력한다면 아래와 같이 작업해야할 것입니다.

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

console.log('작업 시작');

increase(0, result => {
    console.log(result);
    increase(result, result => {
        console.log(result);
        increase(result, result => {
            console.log(result);
            increase(result, result => {
                console.log(result);
                console.log('작업 완료');
            });
        });
    });
});

// 콘솔 출력값
작업 시작
10
20
30
40
작업 완료

콜백 안에 콜백 함수를 넣어 구현할 수 있지만, 너무 중첩되다보니 가독성이 나빠집니다.

이러한 형태의 코드를 '콜백 지옥'이라고 표현합니다.

 

Promise

 

  • 콜백 지옥 같은 코드가 형성되지 않게 하는 방안 (ES6 도입)
  • then을 사용
function increase(number) {
    const promise = new Promise((resolve, reject) => {
        // resolve는 성공, reject은 실패
        setTimeout(() => {
            const result = number + 10;
            if(result > 50) {
                // 50보다 높으면 에러 발생
                const e = new Error('NumberTooBig');
                return reject(e);
            }
            resolve(result); // number + 10 값 성공 처리
        }, 1000);
    });
    return promise;
}

increase(0)
    .then(number => {
        // Promise에서 resolve된 값은 .then을 통해 받아올 수 있음
        console.log(number);
        return increase(number); // Promise를 리턴하면
    })
    .then(number => {
        // 또 .then으로 처리 가능
        console.log(number);
        return increase(number);
    })
    .then(number => {
        console.log(number);
        return increase(number);
    })
    .then(number => {
        console.log(number);
        return increase(number);
    })
    .then(number => {
        console.log(number);
        return increase(number);
    })
    .catch(e => {
        // 도중에 에러 발생 시 catch절을 통해 알 수 있음
        console.log(e);
    });

이렇게 then을 사용하기 때문에 콜백 지옥이 형성되지 않습니다.

※ Promise 관련 포스팅

https://chanychu.tistory.com/282

 

Javascript) Promise (기본, setTimeout, Promise chain)

안녕하세요, 코린이의 코딩 학습기 채니 입니다. 개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다. Promise - 기존 callback 함수를 통한 비동기처리를 개선하기 위한 문법 - producer

chanychu.tistory.com

https://23life.tistory.com/110

 

리액트를 다루는 기술 _ 14장

눈 깜짝하니 목요일이다. 14장. 외부 API를 연동하여 뉴스 뷰어 만들기 지금까지 배운 것을 바탕으로 뉴스 뷰어 프로젝트를 진행해 볼 예정! 전체 흐름은... 1. 비동기 작업의 이해 2. axios로 API 호출

23life.tistory.com

 

async/await

 

  • Promise를 더 쉽게 사용할 수 있도록 해주는 ES2017(ES8) 문법
  • 함수 앞부분async 키워드, 해당 함수 내부에서 Promise 앞부분await 키워드 사용
  • Promise가 끝날 때까지 기다리고, 결과 값을 특정 변수에 담음
function increase(number) {
    const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            const result = number + 10;
            if (result > 50) {
                const e = new Error('NumberTooBig');
                return reject(e);
            }
            resolve(result);
        }, 1000)
    });
    return promise;
}

async function runTask() {
    try { // try~catch 구문을 사용해 에러 처리
        let result = await increase(0);
        console.log(result);
        result = await increase(result);
        console.log(result);
        result = await increase(result);
        console.log(result);
        result = await increase(result);
        console.log(result);
        result = await increase(result);
        console.log(result);
        result = await increase(result);
        console.log(result);
    } catch(e) {
        console.log(e);
    }
};

runTask();

※ async/await 관련 포스팅

https://chanychu.tistory.com/285

 

Javascript) async & await (await fetch, 랜덤 강아지/고양이 사진 추출)

안녕하세요, 코린이의 코딩 학습기 채니 입니다. 개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다. async - ES2017에 추가된 일반 함수의 프라미스화를 지원하는 문법 HTML 코드 asy

chanychu.tistory.com

 


axios로 API 호출해서 데이터 받아 오기

axios

- 가장 많이 사용되고 있는 자바스크립트 HTTP 클라이언트

- HTTP 요청을 Promise 기반으로 처리

 

프로젝트를 생성하여 라이브러리를 설치해 사용하겠습니다.

$ yarn create react-app news-viewer
$ cd news-viewer
$ yarn add axios

 

prettier 설정

.prettierrc (최상위 디렉터리)

{
    "singleQuote": true,
    "semi": true,
    "useTabs": false,
    "tabWidth": 2,
    "trailingComma": "all",
    "printWidth": 80
}

 

파일 자동 불러오기 활성화를 위해 jsconfig.json 파일 생성

jsconfig.json (최상위 디렉터리)

{
    "compilerOptions": {
        "target": "es6"
    }
}

 

App.js

import { useState } from 'react';
import axios from '../node_modules/axios/index';

function App() {
  const [data, setData] = useState(null);
  const onClick = () => {
    axios.get("https://jsonplaceholder.typicode.com/todos/1").then(response => {
      console.log(response);
      setData(response.data);
    })
  }

  return (
    <div>
      <div>
        <button onClick={onClick}>불러오기</button>
      </div>
      {data && <textarea rows={7} value={JSON.stringify(data, null, 2)} readOnly={true} />}
    </div>  
  );
}

export default App;

console.log(response) 결과

axios.get 함수파라미터로 전달된 주소에 GET 요청을 해줍니다.

그리고 해당 결과는 .then을 통하여 비동기적으로 확인할 수 있습니다.

 

※ JSON.stringify 관련 문서 (JSON.stringify(data, null, 2)에서의 매개인자들은 어떤 걸 뜻하는 가?)

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

 

JSON.stringify() - JavaScript | MDN

JSON.stringify() 메서드는 JavaScript 값이나 객체를 JSON 문자열로 변환합니다. 선택적으로, replacer를 함수로 전달할 경우 변환 전 값을 변형할 수 있고, 배열로 전달할 경우 지정한 속성만 결과에 포함

developer.mozilla.org

 

async/await 적용 시

function App() {
  const [data, setData] = useState(null);
  const onClick = async () => {
    try {
      const response = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
      setData(response.data);
    } catch(e) {
      console.log(e);
    }
    
    ...
    ...

동일하게 결과가 잘 출력 됩니다.