Promise , Axios, Async/Await (지속적으로 업데이트 24.10)

Yeong·2023년 4월 26일

둘의 차이와 어떤 때에 사용하는지 헷갈려서 정리를 해보고자 한다.

먼저 비동기와 동기에 대해서 알고 가야하니 위 이미지를 참고하길!
동기는 요청->응답->요청->응답 이렇게 순서대로 하나씩 해나가야한다.
비동기는 요청을 보내고 응답을 기다리지않고 다음 요청을 수행해나갈 수 있는 멀티플레이어의 느낌이다.

비동기 작업의 결과를 그 다음 작업에 쓰고 싶다면, 비동기 작업과 그 이후 작업을 마치 동기 작업'처럼' 만들어주면 될 것이다.

💡 콜백방식(비동기방식)

비동기 통신 방식인 콜백함수이다.

콜백함수(=완료후 실행 할 함수)
서버에 요청

두번째 줄에서 서버에 요청이 가면, 요청 완료 후 실행할 함수가 동작하는 형태이다.
살짝 거꾸로 된(?) 느낌의 방식! 이런 코드 진행 방식은 가독성이 떨어지고 자칫하면 콜백지옥에 빠질 수 있다.

이 문제를 해결하고자 Promise 객체가 나왔다.

📍 Promise(비동기 방식)

Promise는 비동기 작업이 끝난 후 결과를 알려주는 용도로 사용한다.
"비동기 작업 끝나면 연락줄게~!"

Promise를 사용하면 비동기 작업이 끝난 시점을 알 수 있어서 동기작업’처럼’ 구성할 수 있게 된다.

동기작업 처럼 이란 아까의 콜백방식처럼 코드의 동작 방식이 거꾸로 된 것이 아니라 순차적으로, 서버에 요청을 하고 -> 완료 후 실행할 함수 순서가 된다는 것이다.

이 Promise 객체는 대기, 이행, 거부 이 세 가지 상태를 가진다. 각각 작업 완료 전, 작업 완료 후, 에러 발생하여 실패. 사용 방법은 비동기 작업이 작업 종료 후 Promise 객체를 반환하도록 하고, Promise 객체가 제공하는 then, catch 를 이용하면 된다.

const promise = //Promise를 반환하는 비동기 작업

promise.then((result) => console.log('성공!').catch((error) => console.log('실패')

promise 객체에 .then() 을 붙이면 이행 상태의 처리를 할 수 있고,
.catch 를 붙이면 실패 상태를 처리할 수 있다.

📍 Axios(비동기 방식)

Axios는 브라우저, Node.js를 위해서 만들어진 Promise를 활용하는(기반으로 만들어진) HTTP 클라이언트 라이브러리다. (백엔드와 프론드엔드간에 통신을 위해서 만들어진 AJAX도 더불어 사용하기도 함.)
"서버로부터 데이터를 가져와야할 때, 특히 Http 요청을 보내는 경우 사용"

특징

  1. Axios는 운영환경에 따라서 브라우저간 XMLHttpRequest 객체 또는 Node.js의 HTTP API를 사용한다.
  2. Promise(ES6) 를 사용. (promise가 상태를 관리하여 다른 코드가 비동기적으로 실행될 수 있도록 만드는 객체.)
  3. 요청(Request) 응답 (reply)을 JSON 형태로 자동 변경

📍 Async/Await(비동기 방식)

await 서버에요청
완료후 실행 할 함수

그런데 뒤에 뭔가를 안 붙이고, 더 간단하게! 비동기 작업을 동기적인것 처럼 만들어주는 것이 await 이다.

사용법이 더 간단하다. 비동기 작업 앞에 await 키워드만 붙이면 된다. 그러면 비동기 작업이 결과를 낼 때까지 기다린다. (모든 작업이 종료X 내가 사용할 결과값이 나올 때까지 기다린다는 의미) 메인 작업들은 멈추지 않고 await을 포함하고 있는 함수만 일시정지된다.

await는 async 함수 안에서 Promise가 완료될 때까지 기다리게 하는 키워드로, 비동기 함수를 동기적인 흐름으로 사용할 수 있게 해준다.
✨ 데이터를 받고 나서야 그 데이터를 처리할 수 있는 상황에 유용하다.

async는 선언된 함수 내에 비동기적으로 실행될 내용이 있다는걸 알리는거다. 그리고 그 비동기적으로 실행될 내용 앞에 await을 붙여서 표시한다. async 를 붙인 함수는 Promise 객체를 반환한다.

function networkRequest() {
	return new Promise ((resolve) => {
		setTimeout (() => {
			console. log('데이터를 받아왔습니다');
			resolve('서버 1');
		}, 2000);
	}) ;
}

async function getUser () {
  const result = await networkRequest);
  console. log (result);
  return '코딩';
}

// 콘솔결과는 데이터를 받아왔습니다, 서버1, 예시테스트 순으로 출력됨.

위에서 resolvePromise가 성공적으로 완료되었을 때 값을 반환하는 역할을 한다.

  1. networkRequest 함수:

    • networkRequest 함수는 Promise를 반환하는데 이 Promise는 비동기적으로 실행된다.

    • setTimeout으로 인해 2초가 지난 후 콜백 함수가 실행된다. 이 콜백 함수가 실행되면 console.log('데이터를 받아왔습니다')로 데이터를 받았다는 메시지를 출력하고, resolve('서버 1')을 호출한다.

    • resolve('서버 1')Promise를 성공적으로 완료하고 '서버 1'이라는 값을 반환하도록 지시한다.
      즉, resolvePromise가 성공 상태로 완료될 때 반환하는 값을 지정하는 함수인 것 !!

      📎 만약, resolve 함수의 인자가 없다면 Promise는 여전히 성공 상태로 완료되지만, 반환값은 undefined가 된다.

  2. getUser 함수:

    • async 함수인 getUser에서 await를 사용해 networkRequest가 완료될 때까지 기다린다.
    • await networkRequest()networkRequestPromise가 완료될 때까지 기다린 후, 그 결과('서버 1')를 result 변수에 저장한다.
    • console.log(result)'서버 1'을 출력하고, 마지막에 return '코딩'을 실행하여 '코딩'을 반환한다.

정리!

  • resolvePromise가 정상적으로 완료되었음을 알리며 값을 반환한다.
  • awaitPromise가 완료될 때까지 기다리게 해주는 키워드로, 완료 후 resolve에서 반환된 값을 반환한다.

이렇게 Promiseasync/await을 통해 비동기 작업을 처리할 수 있다.

예시코드

1. AWAIT

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

const Test = () => {
  const [data, setData] = useState(null);
  const onClick = () => {
    axios.get("http://localhost:8080/product/1").then((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 Test;

버튼을 클릭했을 때(onClick) api에 연결해 가져온 값을 setData로 data에 새로 집어넣고 있다.

onClick함수에서 사용한 axios.get 함수가 파라미터로 전달된 주소에 GET요청을 하고, 그 결과를 .then을 통해 비동기적으로 확인한다. 결과는 다음과 같다.

2. Async/Await

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

const Test = () => {
  const [data, setData] = useState(null);
  const onClick = async () => {
    try {
      const response = await axios.get("http://localhost:8080/product/1");
      setData(response.data);
    } catch (e) {
      console.log(e);
    }
  };
  return (
    <div>
      <div>
        <button onClick={onClick}>불러오기</button>
      </div>
      {data && (
        <textarea
          rows={7}
          value={JSON.stringify(data, null, 2)}
          readOnly={true}
        />
      )}
    </div>
  );
};

export default Test;

onClick 시작부분에 async를 추가하고, promise 부분에 await를 추가하면 끝이다.그리고 에러를 처리하기 위해 try/catch구문을 사용.

리액트 axios cors

웹에서는 다른 출처로의 리소스 요청을 제한하는 것과 관련된 두 가지 정책 CORS와 SOP(Same-Origin Policy)가 있다. SOP(Same-Origin Policy)는 같은 출처에서만 리소스를 공유할 수 있다”라는 규칙을 가진 정책이다.

그러나 웹에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이라 무작정 막을 수도 없는 노릇이라 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하기로 했는데, 그 중 하나가 “CORS 정책을 지킨 리소스 요청”이다.

CORS란 Cross Origin Resource Sharing의 약자로, 도메인이 다르더라도 데이터를 공유할 수 있게 해주는 것이다.

쉽게 말해서 백엔드를 호출하는 애가 아무나 호출하면 안되니까 호출하는(오리진)이 누군지 확인하는 것,

특정한 것만 허용시켜서 받아갈 수 있게 해주는 것이다.

우선의 방법으로는 백엔드 스프링부트의 controller에 @CrossOrigin("http://localhost:3000/")를 한줄 작성했다.

결론

서버로부터 데이터를 가져와야 하는 경우 axios를 사용하고, 이후 받아온 데이터를 가공하여 처리해야 할 경우 async/await를 사용할 수 있다!!

둘다 비동기통신방식이지만 axios는 http 클라이언트 라이브러리이고 데이터를 요청받을 때 주로 사용을 하고, then() 메소드를 이용해서 받아온 값을 처리할 수 있어서 코드 가독성을 높일 수 있다.

async/await도 데이터 가공 및 처리가 되지만 await로 비동기작업을 처리함에 있어서 시간이 걸릴 수 있기때문에 웹성능이 저하될 수 있다는 우려가 있다.


느낀점

promise와 await 그리고 async/await에 대해서 세개가 어떤 유사점이 있고 어떤 차이가 있으며 때에 따라 어떻게 쓰면 될지가 헷갈렸는데 이번에 제대로 알게 되었다.
프로젝트를 하면서 참 많이 썼는데 이제 왜 이렇게 했는지 알 것같은!
제로웨이브의 경우 node.js를 함께 사용했기때문에 await를 많이 썼고, 그 안에서 method와 uri를 통해서 어떻게 데이터를 주고받았는지 이해가 잘 간다.

0개의 댓글