Express asyncHandler로 에러 처리하기

Express 프레임워크를 사용하여 서버를 만들때 나는 DB 쿼리실행과 같은 비동기 작업때 async/await 구문을 사용하고자 하였다. async/await 는 ECMAScript2017 추가되어 기존 promise를 사용했을 때보다 더 읽기 쉽도록 만들어 준다.
아래는 나의 프로젝트에 쓰인 코드로 같은 비동기 작업에서의 async/await 와 promise 표현방식이다.

//device.js

//등록기기조회 promise
router.get('/regist', verifyToken, (req, res, next) => {
  db.getDevices(req.query)
    .then((result) => {
      if (isEmpty(result)) {
        throw new createError.BadRequest('등록기기 없음')
      }
      res.status(200).json({ success: true, devices: result })
    })
    .catch((error) => {
      next(error)
    })
})

//등록기기조회 async/await
router.get(
  '/regist',
  verifyToken,
  asyncHandler(async (req, res, next) => {
    const result = await db.getDevices(req.query)
    if (isEmpty(result)) {
      throw new createError.BadRequest('등록기기 없음')
    }
    res.status(200).json({ success: true, devices: result })
  })
)

위의 코드에서 async/await를 사용할떄 추가로 asyncHandler 이라는 랩퍼 메서드를 사용하였는데 그 이유는 Express는 async 메서드의 에러를 감지하지 못하기 때문이다. 내가 사용한 모듈인 express-async-handler 의 내부 코드를 보면 내부적으로 Promise 객체를 리턴하는것을 볼 수 있다.

const asyncUtil = (fn) =>
  function asyncUtilWrap(...args) {
    const fnReturn = fn(...args)
    const next = args[args.length - 1]
    return Promise.resolve(fnReturn).catch(next)
  }

module.exports = asyncUtil

이렇게 asyncHandler 을 이용하여 에러 처리를 한 뒤에는 일반적인 에러를 구분해 맵핑을 해놓았다.

//app.js

//일반적인 에러 매핑
const newErrorMap = new Map([['ER_NO_DEFAULT_FOR_FIELD', 400]])

//매핑 에러핸들러
app.use((err, req, res, next) => {
  const newError = newErrorMap.get(err.code)
  if (newError) {
    err.status = newError
    next(err)
  } else {
    next(err)
  }
})

//최종 에러핸들러
app.use(function (err, req, res, next) {
  //에러응답
  res.status(err.status || 500)
  res.json({ error: err, success: false })
})

위의 코드는 라우터에서 ER_NO_DEFAULT_FOR_FIELD 에러가 catch 될 경우 res 상태코드를 400으로 하여 응답하는 예시이다.

내가 서버를 구축할땐 위와 같은 방법으로 에러 처리를 하였고 더 깔끔하고 우아한 방법이 있을 것도 같다..

Previous post
React 간단하게 input 값 초기화 하기
Next post
Express Knex 쿼리빌더를 이용하여 DB쿼리 실행