Promise.all과 에러 핸들링 - 일부 실패해도 계속 진행하기
문제 상황
대시보드에서 여러 통계 데이터를 한 번에 가져오는 로직을 작성했다. 각 API가 독립적이라 Promise.all로 병렬 처리했는데, 하나의 API만 500 에러가 나도 전체가 실패했다.
const fetchDashboard = async () => {
try {
const [users, orders, revenue] = await Promise.all([
fetchUsers(),
fetchOrders(),
fetchRevenue()
]);
// orders API가 실패하면 users, revenue도 사용 못함
} catch (error) {
console.error('데이터 로딩 실패', error);
}
};
주문 API가 일시적으로 불안정해도 사용자 수나 매출 통계는 보여줘야 하는데, 전체가 에러 처리되는 게 문제였다.
해결 방법
각 Promise를 catch로 감싸서 에러가 발생해도 reject되지 않도록 변경했다.
const fetchDashboard = async () => {
const results = await Promise.all([
fetchUsers().catch(err => ({ error: err })),
fetchOrders().catch(err => ({ error: err })),
fetchRevenue().catch(err => ({ error: err }))
]);
const [users, orders, revenue] = results;
// 각각 성공/실패 여부 확인
if (!users.error) {
renderUsers(users);
}
if (!orders.error) {
renderOrders(orders);
} else {
showOrdersError();
}
if (!revenue.error) {
renderRevenue(revenue);
}
};
개선
반복되는 패턴을 함수로 분리했다.
const withErrorHandling = (promise) => {
return promise
.then(data => ({ data }))
.catch(error => ({ error }));
};
const fetchDashboard = async () => {
const results = await Promise.all([
withErrorHandling(fetchUsers()),
withErrorHandling(fetchOrders()),
withErrorHandling(fetchRevenue())
]);
results.forEach((result, index) => {
if (result.data) {
// 성공 처리
} else {
// 실패 로깅
console.error(`API ${index} 실패:`, result.error);
}
});
};
결론
Promise.all은 하나라도 실패하면 전체가 reject되므로, 독립적인 비동기 작업들은 개별적으로 에러를 처리해야 했다. 이후 비슷한 상황에서는 이 패턴을 재사용하고 있다.