# `Promise.all` vs `Promise.allSettled`, 실수하기 쉬운 차이

> Promise.all은 하나라도 실패하면 전체가 날아갑니다. 대시보드가 통째로 흰 화면이 된 경험 있으신가요? Promise.all과 Promise.allSettled의 핵심 차이를 실무 예제로 살펴보고, 언제 어떤 걸 써야 하는지 정리합니다.

**Published:** 2026-04-08 | **Updated:** 2026-04-08

---


대시보드를 만들었습니다. 뿌듯했습니다. `Promise.all`로 API 세 개를 동시에 불러오는 코드가 딱 세 줄이었고, 코드 리뷰에서도 "깔끔하다"는 피드백을 받았어요.

{{< img src="images/contents/multitask-image.jpg" alt="여러 작업이 동시에 마술 - Promise.all은 하나라도 실패하면 전체가 영향을 받습니다" caption="사진: <a href='https://unsplash.com/ko/%EC%82%AC%EC%A7%84/%ED%9D%91%EC%9D%B8%EA%B3%BC-%EB%B0%B1%EC%9D%B8-%ED%81%AC%EB%A3%A8%EB%84%A5-%ED%8B%B0%EC%85%94%EC%B8%A0%EB%A5%BC-%EC%9E%85%EC%9D%80-%EC%97%AC%EC%9E%90-%EC%B9%B4%EB%93%9C-%EB%86%80%EC%9D%B4-YlgnX_ISPLo?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText'>Unsplash</a>의 <a href='https://unsplash.com/ko/@roinuj16?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText'>Edson Junior</a>" >}}

```javascript
const [user, notifications, activity] = await Promise.all([
  fetchUser(),
  fetchNotifications(),
  fetchActivity(),
]);
```

그런데 어느 날부터 대시보드가 통째로 흰 화면이 됩니다. 알고 보니 `fetchNotifications`가 서버 문제로 가끔 실패하고 있었어요. 나머지 두 개는 멀쩡히 성공했는데도요.

`Promise.all`은 하나라도 실패하면 전체를 실패로 처리합니다. 알림 하나 때문에 대시보드 전체가 날아간 셈이죠.

왜 이런 일이 생기는지 이해하려면, 먼저 Promise와 Promise.all이 어떻게 동작하는지 짚고 넘어가야 합니다.

## Promise와 Promise.all, 간단히 짚고 가기

**Promise**는 "나중에 완료될 작업의 결과"를 담는 객체입니다. 서버에 데이터를 요청하면 즉시 결과가 오는 게 아니라, 잠시 후에 오죠. Promise는 그 결과가 오기 전까지 "대기 중(pending)" 상태를 유지하다가, 성공하면 "이행(fulfilled)", 실패하면 "거부(rejected)" 상태가 됩니다.

```javascript
// fetch()는 Promise를 반환합니다
const promise = fetch("/api/user");

// await으로 결과를 기다립니다
const response = await fetch("/api/user");
```

그런데 API 요청이 여러 개라면 어떨까요?

```javascript
// 하나씩 기다리면 순서대로 실행됩니다 — 느립니다
const user = await fetchUser();          // 300ms 대기
const posts = await fetchPosts();        // 300ms 대기
// 총 600ms+
```

**Promise.all**은 여러 Promise를 동시에 실행하고, 모두 완료될 때까지 기다립니다. 순차 실행 대신 병렬 실행이 가능해져 훨씬 빠르죠.

```javascript
// Promise.all로 동시에 실행 — 빠릅니다
const [user, posts] = await Promise.all([fetchUser(), fetchPosts()]);
// 300ms 대기 (가장 오래 걸리는 것 기준)
```

이런 상황에서 자주 쓰입니다:

- **페이지 초기 로딩**: 사용자 정보, 알림, 피드 등 여러 데이터를 한 번에 가져올 때
- **연관 없는 API 병렬 호출**: 서로 의존하지 않는 요청들을 동시에 처리할 때
- **배치 작업**: 파일 여러 개를 동시에 업로드하거나 변환할 때

서로 **의존 관계가 없는** 비동기 작업이라면 순서대로 기다릴 필요가 없습니다. `Promise.all`은 그럴 때 쓰는 도구예요.

여기까지는 완벽해 보입니다. 문제는 **실패했을 때** 생깁니다.

## Promise.all: 전부 성공하거나, 하나라도 실패하면 끝

`Promise.all`은 전달된 Promise들이 **모두 성공할 때** 결과를 반환합니다.

```javascript
const results = await Promise.all([
  Promise.resolve("A"),
  Promise.resolve("B"),
  Promise.resolve("C"),
]);

console.log(results); // ["A", "B", "C"]
```

순서는 보장됩니다. 세 번째 Promise가 가장 먼저 끝나도 결과 배열의 순서는 입력 순서 그대로예요.

**하지만 하나라도 실패하면 즉시 reject됩니다.**

```javascript
try {
  const results = await Promise.all([
    Promise.resolve("A"),
    Promise.reject(new Error("B가 실패했습니다")),
    Promise.resolve("C"),
  ]);
} catch (error) {
  console.log(error.message); // "B가 실패했습니다"
  // A와 C의 결과는 알 수 없습니다
}
```

중요한 건 **나머지 Promise는 취소되지 않는다**는 점입니다. 내부적으로는 계속 실행되지만, 결과를 받을 방법이 없어요. 마치 결과 보고서를 찢어버린 것처럼요.

## Promise.allSettled: 모두 끝날 때까지 기다립니다

`Promise.allSettled`는 성공이든 실패든 **모든 Promise가 완료될 때까지** 기다립니다.

```javascript
const results = await Promise.allSettled([
  Promise.resolve("A"),
  Promise.reject(new Error("B가 실패했습니다")),
  Promise.resolve("C"),
]);

console.log(results);
// [
//   { status: "fulfilled", value: "A" },
//   { status: "rejected", reason: Error("B가 실패했습니다") },
//   { status: "fulfilled", value: "C" },
// ]
```

각 결과는 `status` 필드로 성공/실패를 구분합니다.

- 성공: `{ status: "fulfilled", value: 결과값 }`
- 실패: `{ status: "rejected", reason: 에러 }`

이 구조 덕분에 각각의 결과를 개별적으로 처리할 수 있어요.

```javascript
const results = await Promise.allSettled([
  fetchUser(),
  fetchNotifications(),
  fetchActivity(),
]);

const [userResult, notifResult, activityResult] = results;

const user = userResult.status === "fulfilled" ? userResult.value : null;
const notifications =
  notifResult.status === "fulfilled" ? notifResult.value : [];
const activity =
  activityResult.status === "fulfilled" ? activityResult.value : [];
```

알림을 못 불러와도 대시보드는 정상적으로 표시됩니다. 알림 부분만 "불러오기 실패" 메시지를 보여주면 되죠.

{{< img src="images/contents/promise-flow.png" alt="Promise.all과 Promise.allSettled의 흐름 비교 - 전자는 하나 실패 시 전체 실패, 후자는 개별 결과 반환" caption="제작: 나노바나나" >}}

## 언제 뭘 써야 할까?

둘의 차이를 알았으니, 기준을 정해봅시다.

**`Promise.all`을 쓸 때**

모든 결과가 다 있어야 의미가 있을 때입니다.

```javascript
// 주문 처리: 재고 확인 + 결제 + 배송 예약이 모두 성공해야 진행
const [stock, payment, shipping] = await Promise.all([
  checkStock(items),
  processPayment(card),
  reserveShipping(address),
]);
```

재고는 있는데 결제가 실패했다면? 배송을 예약할 이유가 없죠. 하나라도 실패하면 전체를 취소해야 할 때 `Promise.all`이 적합합니다.

**`Promise.allSettled`를 쓸 때**

각 작업이 서로 독립적이고, 일부 실패해도 나머지를 처리해야 할 때입니다.

```javascript
// 이미지 여러 장 업로드: 일부 실패해도 성공한 건 처리
const uploadResults = await Promise.allSettled(
  images.map((img) => uploadImage(img))
);

const succeeded = uploadResults
  .filter((r) => r.status === "fulfilled")
  .map((r) => r.value);

const failed = uploadResults
  .filter((r) => r.status === "rejected")
  .map((r) => r.reason);

console.log(`${succeeded.length}개 업로드 성공, ${failed.length}개 실패`);
```

10장 중 2장이 실패했다고 8장을 버릴 수는 없잖아요.

둘의 차이가 명확해졌으니, 이제 자신 있게 쓸 수 있겠죠? 저도 그렇게 생각했습니다.

## 흔한 실수 패턴

### Promise.all에서 에러 처리를 빠뜨리기

```javascript
// ❌ 하나라도 실패하면 unhandled rejection 에러
const results = await Promise.all([api1(), api2(), api3()]);

// ✅ try-catch로 감싸야 합니다
try {
  const results = await Promise.all([api1(), api2(), api3()]);
} catch (error) {
  // 어떤 Promise가 실패했는지는 알 수 없습니다
  console.error("하나 이상의 요청이 실패했습니다", error);
}
```

### Promise.allSettled 쓰고 status 체크를 안 하기

```javascript
const results = await Promise.allSettled([api1(), api2()]);

// ❌ status 체크 없이 바로 value 접근
const data = results.map((r) => r.value); // 실패한 건 value가 undefined

// ✅ status 먼저 확인
const data = results
  .filter((r) => r.status === "fulfilled")
  .map((r) => r.value);
```

`allSettled`를 쓴다고 끝이 아닙니다. 결과를 쓰기 전에 `status` 확인이 필수예요.

## 정리

| 상황 | 선택 |
|------|------|
| 모든 결과가 다 있어야 의미 있을 때 | `Promise.all` |
| 일부 실패해도 성공한 것만 처리하면 될 때 | `Promise.allSettled` |
| 에러를 개별적으로 처리해야 할 때 | `Promise.allSettled` |

- **`Promise.all`**: 하나라도 실패 → 즉시 reject. 전부 성공해야 할 때.
- **`Promise.allSettled`**: 모두 완료 후 결과 배열 반환. 독립적인 작업 처리에.

처음 대시보드를 만들 때로 돌아간다면, 알림 API에는 `allSettled`를 썼을 겁니다. 사용자 정보처럼 없으면 페이지 자체가 의미 없는 것만 `all`로 묶어서요.

코드를 직접 실행하면서 동작을 확인하고 싶다면 [데모 페이지](https://isaaceryn.github.io/demo_codes/javascript-promise-methods/)에서 해보세요.

---

{{< faq >}}

## 이 시리즈의 다른 글

- [함수 선언식과 표현식, 제대로 알고 쓰기: 호이스팅까지 한 번에]({{< relref "/posts/javascript-function-hoisting" >}})

