ES6 Promise 체이닝과 에러 핸들링 정리

배경

레거시 코드베이스의 콜백 헬을 Promise로 리팩토링하는 작업을 진행했다. 단순히 new Promise로 감싸는 것은 쉬웠지만, 여러 API를 순차적으로 호출하면서 에러 처리를 제대로 하는 부분에서 혼란이 있었다.

문제 상황

사용자 인증 → 프로필 조회 → 권한 확인의 3단계 API 호출이 필요했다. 중간에 실패하면 특정 단계의 에러인지 명확히 알아야 했다.

fetchUser(userId)
  .then(user => {
    return fetchProfile(user.profileId);
  })
  .then(profile => {
    return checkPermission(profile.role);
  })
  .catch(error => {
    console.error('어느 단계에서 실패한 걸까?', error);
  });

해결 방법

각 단계별로 에러를 구분해서 처리하려면 catch를 중간에 배치하고, 필요시 에러를 다시 throw 해야 했다.

fetchUser(userId)
  .then(user => {
    return fetchProfile(user.profileId);
  })
  .catch(error => {
    console.error('사용자 조회 실패:', error);
    throw new Error('USER_FETCH_FAILED');
  })
  .then(profile => {
    return checkPermission(profile.role);
  })
  .catch(error => {
    if (error.message === 'USER_FETCH_FAILED') {
      throw error;
    }
    console.error('권한 확인 실패:', error);
    return { hasPermission: false };
  });

학습 내용

  • catch는 그 이전 체인의 모든 에러를 잡는다
  • catch 이후에 then을 붙이면 에러 복구 후 체이닝 계속 가능
  • Promise.all은 하나라도 실패하면 전체 실패 처리됨
  • finally는 ES2018 스펙이라 아직 사용 불가

당분간은 Babel로 트랜스파일하면서 Promise 패턴에 익숙해지는 게 우선이다.