JavaScript에서 여러 비동기 작업을 동시에 처리할 때 Promise.all()과 Promise.allSettled()을 자주 사용한다.
두 메서드 모두 여러 Promise를 한 번에 실행할 수 있지만 실패를 처리하는 방식에는 차이가 있다.
API를 여러 개 호출하는 화면에서는 이 차이를 모르고 사용하면 일부 API의 실패 때문에 정상적으로 받아온 데이터까지 사용하지 못하는 상황이 발생할 수 있다.
이번 글에서는 Promise.all()과 Promise.allSettled()의 차이와 실무에서 어떤 기준으로 선택하면 되는지 정리한다.

Promise.all이란?
Promise.all()은 여러 Promise를 전달받아 모든 작업이 성공했을 때 결과를 반환한다.
//Javascript
const [user, products, notices] = await Promise.all([
fetchUser(),
fetchProducts(),
fetchNotices()
])
세 API는 순차적으로 실행되는 것이 아니라 거의 동시에 시작된다.
다음과 같이 작성한 코드와 차이가 있다.
const user = await fetchUser()
const products = await fetchProducts()
const notices = await fetchNotices()
각 요청에 1초가 걸린다고 가정한다.
순차적으로 실행하면 전체 처리 시간이 약 3초가 될 수 있다.
반면 Promise.all()을 사용하면 세 요청을 동시에 시작할 수 있으므로 가장 오래 걸리는 요청의 완료 시점을 기준으로 다음 로직을 실행할 수 있다.
서로 의존하지 않는 API를 여러 개 호출해야 한다면 Promise.all()을 먼저 고려할 수 있다.
Promise.all은 하나만 실패해도 reject된다
Promise.all()에서 가장 중요한 특징은 전달한 Promise 중 하나라도 reject되면 전체 결과가 reject된다는 점이다.
try {
const [user, products, notices] = await Promise.all([
fetchUser(),
fetchProducts(),
fetchNotices()
])
console.log(user)
console.log(products)
console.log(notices)
} catch (error) {
console.error(error)
}
예를 들어 다음과 같은 결과가 발생했다고 가정한다.
fetchUser() 성공
fetchProducts() 성공
fetchNotices() 실패
이 경우 Promise.all() 자체가 reject된다.
fetchUser()와 fetchProducts()가 성공했더라도 구조 분해 할당을 통해 정상 결과를 받을 수 없다.
따라서 모든 작업이 성공해야 다음 작업을 진행할 수 있는 상황에 적합하다.
Promise.allSettled란?
Promise.allSettled()은 모든 Promise가 처리될 때까지 기다린다.
여기서 처리된다는 것은 성공과 실패를 모두 포함한다.
const results = await Promise.allSettled([
fetchUser(),
fetchProducts(),
fetchNotices()
])
결과는 다음과 같은 형태로 반환된다.
[
{
status: 'fulfilled',
value: user
},
{
status: 'fulfilled',
value: products
},
{
status: 'rejected',
reason: error
}
]
성공한 Promise는 fulfilled 상태와 value를 가진다.
실패한 Promise는 rejected 상태와 reason을 가진다.
따라서 각각의 결과를 개별적으로 처리할 수 있다.
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(result.value)
return
}
console.error(result.reason)
})
Promise.all()과 달리 일부 Promise가 실패했다고 해서 전체 작업이 바로 reject되지는 않는다.
실무에서는 화면의 요구사항을 기준으로 선택한다
두 메서드 중 어떤 것이 더 좋은지는 정해져 있지 않다.
화면이나 기능이 일부 데이터 실패를 허용하는지가 가장 중요한 기준이다.
예를 들어 쇼핑몰 주문 상세 화면을 생각해본다.
const [order, payment] = await Promise.all([
fetchOrder(),
fetchPayment()
])
주문 정보와 결제 정보가 모두 있어야 정상적인 화면을 구성할 수 있다면 하나라도 실패했을 때 오류 화면을 표시하는 것이 자연스럽다.
이 경우에는 Promise.all()이 적합하다.
반대로 대시보드 화면을 생각해본다.
사용자 정보
공지사항
최근 활동
추천 콘텐츠
광고
각 영역이 독립적이라면 공지사항 API 하나가 실패했다고 전체 대시보드를 오류 화면으로 바꾸는 것은 사용자 경험 측면에서 좋지 않을 수 있다.
이 경우 Promise.allSettled()을 사용할 수 있다.
const results = await Promise.allSettled([
fetchUser(),
fetchNotices(),
fetchActivities(),
fetchRecommendations()
])
성공한 데이터는 정상적으로 보여주고 실패한 영역에만 오류 상태를 표시할 수 있다.
Promise.allSettled 결과를 실무에서 처리하는 방법
Promise.allSettled()은 결과 구조가 조금 복잡하기 때문에 필요한 값을 직접 분리하는 경우가 많다.
const [userResult, noticeResult] = await Promise.allSettled([
fetchUser(),
fetchNotices()
])
const user =
userResult.status === 'fulfilled'
? userResult.value
: null
const notices =
noticeResult.status === 'fulfilled'
? noticeResult.value
: []
이후 화면에서는 각각의 상태를 기준으로 처리한다.
if (!user) {
return <UserError />
}
return (
<>
<UserProfile user={user} />
<NoticeList notices={notices} />
</>
)
사용자 정보는 반드시 필요하지만 공지사항은 없어도 화면을 표시할 수 있다는 정책을 코드에 반영할 수 있다.
Promise.all을 사용할 때 자주 하는 실수
다음과 같은 코드를 작성하는 경우가 있다.
const users = await fetchUsers()
const products = await fetchProducts()
const notices = await fetchNotices()
세 요청 사이에 아무런 의존성이 없다면 불필요하게 순차 실행되고 있는 것이다.
다음처럼 변경할 수 있다.
const [users, products, notices] = await Promise.all([
fetchUsers(),
fetchProducts(),
fetchNotices()
])
반대로 모든 await를 Promise.all()로 묶는 것도 적절하지 않다.
const user = await fetchUser()
const orders = await fetchOrders(user.id)
fetchOrders()를 실행하려면 user.id가 필요하다.
두 요청은 의존 관계가 있으므로 순차 실행해야 한다.
결국 병렬 처리 여부는 Promise의 개수가 아니라 작업 사이의 의존 관계를 기준으로 판단해야 한다.
API 요청 자체가 취소되는 것은 아니다
Promise.all()에서 한 작업이 실패하면 전체 Promise는 reject된다.
하지만 이것이 나머지 네트워크 요청을 자동으로 취소한다는 의미는 아니다.
await Promise.all([
fetch('/api/a'),
fetch('/api/b'),
fetch('/api/c')
])
/api/a 요청이 먼저 실패했다고 하더라도 이미 시작된 /api/b, /api/c 요청이 자동으로 취소되는 것은 아니다.
실제 요청까지 중단해야 한다면 AbortController와 같은 별도의 취소 처리가 필요하다.
이 부분은 Promise.all()의 실패 처리와 네트워크 요청 취소를 혼동하기 쉬운 부분이다.
Promise.all과 Promise.allSettled 선택 기준
실무에서는 다음과 같이 구분할 수 있다.
| 모든 데이터가 반드시 필요함 | Promise.all() |
| 하나라도 실패하면 전체 기능을 중단해야 함 | Promise.all() |
| 일부 데이터 실패를 허용함 | Promise.allSettled() |
| 여러 독립적인 UI 영역을 조회함 | Promise.allSettled() 고려 |
| 요청 사이에 의존 관계가 있음 | 순차 await |
| 요청 자체를 중단해야 함 | AbortController 등 별도 처리 |
Promise.allSettled()이 더 안전한 방법이고 Promise.all()이 위험한 방법인 것은 아니다.
오히려 반드시 모든 데이터가 필요한 기능에서 실패를 숨기고 일부 결과만 사용하는 것이 더 큰 문제를 만들 수 있다.
정리
Promise.all()과 Promise.allSettled()은 여러 비동기 작업을 병렬로 처리한다는 점에서는 비슷하지만 실패 처리 방식이 다르다.
Promise.all()은 하나의 Promise라도 실패하면 전체 Promise가 reject된다.
Promise.allSettled()은 모든 Promise의 성공과 실패 결과를 각각 반환한다.
따라서 메서드를 선택할 때는 단순히 API 요청 개수를 기준으로 판단하지 않는다.
각 요청 사이에 의존성이 있는지, 일부 요청이 실패해도 기능을 계속 사용할 수 있는지를 기준으로 결정하는 것이 적절하다.
프론트엔드에서는 특히 여러 API를 호출하는 대시보드나 메인 화면에서 이러한 차이가 화면의 장애 범위와 사용자 경험에 직접적인 영향을 줄 수 있다.
'FrontEnd > JavaScript' 카테고리의 다른 글
| JavaScript에서 async/await를 사용해도 Promise를 이해해야 하는 이유 (0) | 2026.09.01 |
|---|---|
| [JS] 자바스크립트 기본 제공 함수로 개발/테스트/운영 모드 구분하는 방법 (feat. window.location) (1) | 2023.12.20 |
| [ChatGPT] OpenAi You exceeded your current quota, please check your plan and billing details (0) | 2023.03.22 |
| [JavaScript] 정규표현식 숫자 제외 삭제 하는 방법 (0) | 2022.09.24 |
| [JavaScript] 문자열을 배열로 변경하는 방법, How to change string to Array/Object (0) | 2022.08.24 |



























