JavaScript 비동기 에러 핸들링 - async/await의 try-catch 패턴

문제 상황

API 호출 코드를 Promise 체이닝에서 async/await으로 리팩토링하던 중, 에러 핸들링 코드가 지나치게 반복되는 문제가 생겼다. 모든 함수마다 try-catch를 감싸니 보일러플레이트가 늘어났다.

기존 코드

function fetchUserData(userId) {
  return fetch(`/api/users/${userId}`)
    .then(res => res.json())
    .catch(err => {
      logger.error(err);
      throw err;
    });
}

async/await 전환 후

async function fetchUserData(userId) {
  try {
    const res = await fetch(`/api/users/${userId}`);
    return await res.json();
  } catch (err) {
    logger.error(err);
    throw err;
  }
}

해결 방법

고차 함수를 만들어 에러 핸들링을 분리했다.

const asyncHandler = (fn) => async (...args) => {
  try {
    return await fn(...args);
  } catch (err) {
    logger.error(err);
    throw err;
  }
};

const fetchUserData = asyncHandler(async (userId) => {
  const res = await fetch(`/api/users/${userId}`);
  return await res.json();
});

Express 라우터에서도 활용했다.

router.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  res.json(user);
}));

결과

  • 비즈니스 로직과 에러 핸들링 분리
  • 반복 코드 제거
  • 에러 로깅을 한 곳에서 관리

다만 특정 에러만 처리하고 싶을 때는 여전히 try-catch를 직접 써야 한다. 상황에 맞게 선택해서 사용하면 될 것 같다.