API 병렬 호출 : Promise.all, Promise.allSettled, Promise.race 비교

yarnmiyarnmi
3 min read

들어가며

이번 주 리액트 스터디에서 API 병렬 호출과 관련한 추가 요구사항을 받았다. 주어진 요구사항은 다음과 같다.

  • 3개의 API를 병렬 호출할 것

  • 3개의 API 요청이 모두 성공한 뒤 데이터가 화면에 반영될 것

  • 실패 시 각 API마다 다른 에러 UI를 보여줄 것

이 요구사항을 해결하기 위해서 Promise.all을 사용해서 병렬 호출을 처리했다. 하지만 이 과정에서 Promise.allSettled을 알게 되어서 더 깊이 이해하기 위해서 이번 글을 작성했다.

이번 글에서는 Promise.all, Promise.allSettled, Promise.race를 비교하고, 각각의 사용 사례를 살펴보려고 한다.

Promise.all : 모든 요청이 성공해야 할 때

개념

  • 순회 가능한 프로미스를 Promise.all의 인자로 넘기고 모든 프로미스를 이행한 뒤 값을 배열 형태로 반환한다.

  • 배열에 담긴 프로미스 중 단 하나라도 실패하면, 첫 번째 거부 이유로 전체가 거부된다. 즉, 다른 프로미스의 이행 여부는 알 수가 없다.

요청 성공 시 예제

  function fetchData(id: string, delay: number) {
    return new Promise((resolve) => {
      setTimeout(() => resolve(`✅ Data from ${id}`), delay);
    });
  }

  async function getAllData() {
    const results = await Promise.all([
      fetchData("API 1", 3000), // 3초 후 응답
      fetchData("API 2", 1000), // 1초 후 응답
      fetchData("API 3", 2000), // 2초 후 응답
    ]);

    console.log("📌 반환된 결과:", results); // 모든 API가 성공한 경우에만 결과 출력 
  }

  getAllData();

요청 실패 시 예제

실패 테스트를 위해서 fetchData 함수에 shouldFail 플래그를 추가했다.

function fetchData(id, delay, shouldFail = false) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldFail) {
        reject(`❌ Failed to fetch data from ${id}`);
      } else {
        resolve(`✅ Data from ${id}`);
      }
    }, delay);
  });
}

async function getAllData() {
  try {
    const results = await Promise.all([
      fetchData("API 1", 3000), // 3초 후 응답 성공
      fetchData("API 2", 1000, true), // 1초 후 실패
      fetchData("API 3", 2000), // 2초 후 응답 성공
    ]);

    console.log("📌 반환된 결과:", results); 
  } catch (error) {
    console.error("❌ 하나라도 실패하면:", error); // 하나라도 실패하면 전체가 실패
  }
}

getAllData();

사용 케이스

  • 여러 API에서 데이터를 가져와서 한 번에 처리할 때

  • 모든 요청이 성공해야 하는 경우

단점

  • 하나라도 실패하면 전체 요청이 실패한다.

  • 다시 모든 API를 호출해야 하는 부담이 있다.

Promise.allSettled : 일부 요청이 실패해도 결과가 필요할 때

개념

  • 모든 프로미스의 상태를 배열로 반환한다.

  • 일부 요청이 실패해도 모든 요청의 결과를 확인할 수 있다.

요청 실패 시 예제

위의 코드에서 Promise.allPromise.allSettled로 변경하자, 각 프로미스의 상태와 결과를 개별로 확인할 수 있었다.

  const results = await Promise.allSettled([
    fetchData("API 1", 3000), // 3초 후 응답 성공
    fetchData("API 2", 1000, true), // 1초 후 실패
    fetchData("API 3", 2000), // 2초 후 응답 성공
  ]);

  console.log("📌 반환된 결과:", results);

사용 케이스

  • 일부 데이터만이라도 활용하고 싶을 때

  • 여러 API 요청을 병렬로 실행하면서 성공한 요청만 필터링할 때

단점

  • 개별 요청의 상태와 값을 직접 확인하고 추가로 처리해야 한다.

Promise.race : 가장 빠른 응답이 필요할 때

개념

성공이나 실패 여부와 상관없이 가장 먼저 완료되는 프로미스의 결과를 반환한다.

요청 성공 시 예제

  const results = await Promise.race([
    fetchData("API 1", 3000), // 3초 후 응답 성공
    fetchData("API 2", 1000), // 1초 후 실패
    fetchData("API 3", 2000), // 2초 후 응답 성공
  ]);

  console.log("📌 반환된 결과:", results);

사용 케이스

가장 빠르게 응답하는 데이터를 우선적으로 사용하고 싶을 때

단점

나머지 요청이 완료되지 않더라도 가장 먼저 완료된 응답만 반환한다.

마무리하며

API 병렬 요청을 처리하는 방법으로 Promise.all, Promise.allSettled, Promise.race를 비교해 보았다. 각 메서드의 API 요청의 성공과 실패를 다루는 방식을 다르므로, 네트워크 요청을 보다 효율적으로 관리하기 위해서 적절한 프로미스 메서드를 선택해야겠다.


참고 링크

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/race

0
Subscribe to my newsletter

Read articles from yarnmi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

yarnmi
yarnmi