자바스크립트 Promise 체이닝과 에러 처리 패턴

문제 상황

레거시 코드에서 여러 API를 순차적으로 호출하는 부분이 콜백 중첩으로 가독성이 떨어졌다. 에러 처리도 각 콜백마다 흩어져 있어 유지보수가 어려웠다.

getUser(userId, (err, user) => {
  if (err) return handleError(err);
  getProfile(user.id, (err, profile) => {
    if (err) return handleError(err);
    getSettings(profile.id, (err, settings) => {
      if (err) return handleError(err);
      // 최종 처리
    });
  });
});

Promise 체이닝으로 개선

Promise를 사용하면 체이닝으로 순차 처리를 평탄하게 표현할 수 있다.

getUser(userId)
  .then(user => getProfile(user.id))
  .then(profile => getSettings(profile.id))
  .then(settings => {
    // 최종 처리
  })
  .catch(error => {
    handleError(error);
  });

중간 에러 복구 패턴

특정 단계에서 에러가 나도 기본값으로 계속 진행하고 싶을 때가 있다.

getUser(userId)
  .then(user => getProfile(user.id))
  .catch(err => {
    // 프로필 조회 실패 시 기본값 반환
    return { id: null, name: 'Unknown' };
  })
  .then(profile => getSettings(profile.id))
  .catch(handleError);

첫 번째 catch는 getProfile 에러만 처리하고, 두 번째 catch는 전체 체인의 에러를 처리한다.

Promise.all로 병렬 처리

독립적인 API 호출은 병렬로 처리해 성능을 개선했다.

Promise.all([
  getUser(userId),
  getNotifications(userId),
  getRecentPosts(userId)
])
  .then(([user, notifications, posts]) => {
    // 모두 완료 후 처리
  })
  .catch(handleError);

하나라도 실패하면 catch로 넘어가므로, 일부 실패를 허용하려면 각 Promise에 catch를 붙여야 한다.

결론

Promise 체이닝으로 코드 가독성이 크게 개선됐다. async/await가 Stage 3이라 프로덕션에선 아직 사용하지 않지만, Babel로 트랜스파일하면 쓸 수 있을 것 같다. 다음엔 async/await 패턴을 테스트해볼 예정이다.