Promise

Promise 란?

Promise 객체는 비동기 작업을 다루기 위해 만들어졌으며, 비동기 작업의 성공 혹은 실패 후 결과를 처리하기 쉽게 해준다. 또한 비동기 작업이 끝나고 실행할 콜백을 등록할 수 있는 메서드를 제공한다.

 

Promise 객체가 탄생하기 전에는 비동기 처리를 위해 콜백함수를 사용했는데, 일명 콜백 헬로 불리는 문제로 인해 가독성이 나쁘고 에러 처리가 분산되고 흐름의 추적하기 어려워지는 등 불편함이 있었는데 이러한 불편함을 보완할 수 있는 것이 Promise 이다.

 

Promise 상태

Pending : 아직 대기 중인 상태

Fulfilled : 비동기 작업이 성공적으로 완료된 상태이며, resolve 로 인해 pending 상태에서 fulfilled 상태로 변함

Rejected : 비동기 작업이 실패한 상태이며, reject 로 인해 pending 에서 rejected 상태로 변함

 

연습

API 호출은 비동기 작업이어서 promise 를 사용한다. fetch 함수를 사용하면 promise 객체를 반환하므로 비동기 작업 처리를 쉽게 할 수 있다. 테스트를 위해 jsonplaceholder 를 사용해 보자.

 

  • 예시 코드 1
import React, { useState, useEffect } from "react";

function App() {
  const [post, setPost] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((response) => response.json())
      .then((json) => setPost(json))
      .catch((error) => console.error("오류 발생 => ", error));
  }, []);

  return <div>{post ? <div>{post.title}</div> : <div>Loading...</div>}</div>;
}

export default App;

위 코드의 동작 방식은 다음과 같다. 

 

컴포넌트가 마운트될 때 useEffect 가 실행되어 fetch 함수를 사용해 비동기적으로 외부 API 로부터 데이터를 가져온다. fetch 함수는 Promise 객체를 반환하며, 호출 성공 시 then 블록에서 데이터를 받아 실행되어 setPost 를 사용해 상태를 업데이트하며, 실패 시 catch 블록에서 오류를 처리한다. 이렇게 promise 객체로 then 과 catch 를 사용하여 비동기 작업의 성공 실패 처리를 쉽게 할 수 있는 것을 알 수 있다.

 

  • 예시 코드 2
import React, { useState, useEffect } from "react";

function App() {
  const [post, setPost] = useState(null);

  useEffect(() => {
    const fetchPost = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/posts/1"
        );

        const data = await response.json();

        setPost(data);
      } catch (error) {
        console.log("error :>> ", error);
      }
    };

    fetchPost();
  }, []);

  return <div>{post ? <div>{post.title}</div> : <div>Loading...</div>}</div>;
}

export default App;

async/await 를 사용하여 promise 객체를 더 쉽게 다룰 수 있다.

 

async 함수는 promise 객체를 반환하며 await 키워드는 promise 가 수행될 때까지 기다린다. 그 후에 다음 코드를 실행한다.

 

고찰(?)

그러면 첫 번째 예시와 두 번째 예시의 차이점은 무엇일까? 두 예시 모두 fetch 를 사용하여 데이터를 가져오는 것은 똑같다. 차이점은 첫 번째는 Promise 체이닝 방식이고, 두 번째 코드는 async/await 방식을 사용한다. 물론 상황에 따라 맞는 것을 선택해서 사용하면 되겠지만 일반적으로는 async/await 를 권장한다고 한다. 

 

그 이유는 다음과 같다.

 

  • 가독성 및 유지보수

promise 체이닝 방식은 충접되거나 길어지면 읽기 어렵고 복잡해지는데, async/await 는 비동기를 마치 동기처럼 코드를 작성하기 때문에 디버깅이나 유지보수가 더 수월하다.

 

const response = await fetch(...);
const data = await response.json();

이 부분이 마치 동기처럼 순차적으로 동작하는 것을 명확하게 표현해 주고 있다.

 

  • 에러 처리

async/await 방식은 try-catch 문을 사용하여 에러를 처리할 수 있다. 이는 에러가 발생한 곳을 명확하게 파악할 수 있다. 하지만 promise 체이닝 방식은 catch 블록을 통해 에러를 처리하지만, 비동기 함수 내부에서 발생한 에어를 모두 잡기에는 불편할 수 있다고 한다.

 

정리하자면, async/await 방식이 더 현대적일뿐더러, 가동성, 유지보수, 에러처리 방면에서 장점이 많아서 더 선호한다고 한다.