ES6 Promise.all에서 일부 실패 처리하기
문제 상황
대시보드에서 여러 외부 API를 동시에 호출해 데이터를 조합하는 작업을 하고 있었다. Promise.all을 사용했는데, API 중 하나만 실패해도 전체가 reject되어 다른 성공한 데이터도 받을 수 없었다.
Promise.all([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(results => {
// 하나라도 실패하면 여기 도달 안 함
})
.catch(error => {
// 모든 데이터를 잃음
});
해결 방법
각 Promise를 catch로 감싸서 실패를 값으로 변환했다.
const promises = [
fetch('/api/users').catch(err => ({ error: err })),
fetch('/api/posts').catch(err => ({ error: err })),
fetch('/api/comments').catch(err => ({ error: err }))
];
Promise.all(promises)
.then(results => {
const users = results[0].error ? [] : results[0];
const posts = results[1].error ? [] : results[1];
const comments = results[2].error ? [] : results[2];
// 성공한 데이터로 렌더링
});
더 범용적으로 사용할 수 있게 유틸 함수로 만들었다.
function promiseAllSettled(promises) {
return Promise.all(
promises.map(p =>
Promise.resolve(p)
.then(value => ({ status: 'fulfilled', value }))
.catch(reason => ({ status: 'rejected', reason }))
)
);
}
promiseAllSettled([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('성공:', result.value);
} else {
console.log('실패:', result.reason);
}
});
});
정리
Promise.all은 all-or-nothing 동작을 하기 때문에 부분 실패를 허용해야 하는 경우 적합하지 않다. catch로 에러를 잡아 정상 값으로 변환하거나, 위와 같은 래퍼 함수를 만들어 사용하면 된다. ES2020에 Promise.allSettled가 표준으로 추가될 예정이라고 하는데, 그때까지는 폴리필을 사용해야 할 것 같다.