Express.js에서 async/await를 사용하며 마주친 에러 핸들링 문제

문제 상황

Node 8이 출시되면서 async/await를 네이티브로 사용할 수 있게 됐다. 기존 Promise 체인을 async/await로 바꾸는 작업을 진행하던 중, Express 라우터에서 에러가 발생하면 서버가 응답 없이 멈춰버리는 현상을 발견했다.

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

위 코드에서 User.findById가 reject되면 에러가 catch되지 않아 unhandled promise rejection이 발생한다.

해결 방법

1. 매번 try-catch 사용

app.get('/users/:id', async (req, res, next) => {
  try {
    const user = await User.findById(req.params.id);
    res.json(user);
  } catch (error) {
    next(error);
  }
});

가장 기본적인 방법이지만 모든 라우터마다 try-catch를 작성해야 해서 번거롭다.

2. 래퍼 함수 작성

const asyncHandler = fn => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

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

실제로 이 방식을 팀에 제안해서 적용했다. express-async-handler 같은 npm 패키지도 있지만, 코드가 간단해서 직접 유틸로 만들어 사용 중이다.

결론

async/await 자체는 코드를 깔끔하게 만들어주지만, Express는 아직 Promise 기반 미들웨어를 완전히 지원하지 않는다. 래퍼 함수를 만들어 사용하는 것이 현재로선 가장 실용적인 방법이었다.