본문 바로가기

개발/Node.js

[nodejs] 오류 처리 미들웨어 사용법 (비동기)

이전글 : 미들웨어 개념, 종류, 사용법

https://datobi.tistory.com/34

 

[nodejs] Middleware 미들웨어 개념, 종류, 사용법(에플리케이션 레벨 미들웨어, 라우터 레벨 미들웨어

미들웨어란? 미들웨어는 요청(Request)과 응답(Response) 도중에 다른 함수로 접근할 수 있는 함수이다. 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시된다. 또한 app.use() 나 app.Method() - app.g

datobi.tistory.com

 

오류처리 미들웨어

다른 미들웨어 함수와 동일반 방법으로 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 구체적으로 말하면 (err, req, res, next) 시그니처를 갖는다는 점이 다르다.

오류처리 미들웨어는 맨 마지막에 둬야 한다.

형태

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

 

간단한 예제

app.js

app.use('/', indexRouter);
app.use('/users', usersRouter);

app.get("*", function (req, res, next) { // 에러 던지기 다음 에러처리 미들웨어로 이동
  throw new Error("woops")
})

app.get("*", function (req, res, next) { // 이 미들웨어는 에러처리 미들웨어가 아님(인자가 3개)
  console.log("this will not print")
})

app.use(function (error, req, res, next) { // 에러 메세지 'woops'와 함께 HTTP 응답을 보냄
  res.json({ message: error.message })
})

woops 라는 이름의 에러가 던져지고, 에러를 처리하는 함수( function(error, req, res, next))를 찾아갈 것이다. 

 

결과값


next()를 사용해야 하는 이유

next()를 사용하지 않는 경우

app.use('/', indexRouter);
app.use('/users', usersRouter);

app.get("*", function (req, res, next) { 
  setImmediate(() => {
    throw new Error("woops") // 에러 발생
  })
})

app.use(function (error, req, res, next) {  // 익스프레스는 위 에러를 잡지 못하기 때문에 여기에 도달하지 못함
  res.json({ message: error.message })
})

위의 경우 에러를 잡지 못해서 에러처리 미들웨어에 가지 못한다. 하지만 next()를 사용한다면 express가 에러가 있음을 인지할 수 있다.

 

next()를 사용하는 경우

const app = require("express")()

app.get("*", function (req, res, next) { 
  setImmediate(() => {
    next(new Error("woops")) // 비동기 오류를 보고하려면 반드시 next()를 통과
  })
})

app.use(function (error, req, res, next) { // 잘 도달한다
  res.json({ message: error.message })
})

app.listen(3000)

위에 같은 경우 next() 함수를 통해 에러가 잘 전달함을 확인 할 수 있다.

 

 

에러 처리 미들웨어를 맨 마지막에 선언해주어야 하는 이유

app.use(function (error, req, res, next) {
  console.log("will not print")
  res.json({ message: error.message })
})

app.get("*", function (req, res, next) { // 실행되지 않음
  setImmediate(() => {
    next(new Error("woops"))
  })
})

Nodejs는 위에서 아래로 간다. 위와 같은 경우에 미들웨어가 실행되지 않음을 알 수있다.

 

결과값

 

이러한 현상을 해결하고 비동기로 에러 처리 미들웨어를 작성하는 방법을 알아보자.


비동기 에러 처리

 

나쁜 예

 

const app = require("express")()

app.get("*", function (req, res) {
  return new Promise((resolve, reject) => { // 비동기 오류는 항상 next() 를 통해 던져줘야 한다.
    setImmediate(() => reject(new Error("woops")))
  })
})

app.use(function (error, req, res, next) {  // 호출되지 않는다.
  console.log("will not print")
  res.json({ message: error.message }) 
})

app.listen(3000)
async function err(){
  throw new Error('에러 발생');
}

app.get('/', async function(req, res) {
  const result = await err();
  res.send(result);
})

두 케이스 다 에러를 catch 하지 못하고 있다.

 

좋은 예 

 

app.get("/", wrapAsync(async function (req, res) {
    await new Promise(resolve => setTimeout(() => resolve(), 50))
    throw new Error("woops") // 비동기 에러를 던짐
  })
)

app.use(function (error, req, res, next) {
  res.json({ message: error.message }) // wrapAsync() 때문에 호출됨
})

function wrapAsync(fn) {
  return function (req, res, next) {     // 오류를 .catch() 처리하고 next() 미들웨어에 전달
    fn(req, res, next).catch(next)
  }
}
wrapAsync()는 async 함수를 리턴한다. 라우팅 핸들링 함수를 실행하고 실행된 async 라우팅 핸들러 함수에서 promise를 리턴하면, catch 메서드로 잡아서 next 콜백으로 전달하는 형태이다.
 

 

async function err(){
  throw new Error('에러 발생');
}

app.get('/',  wrapAsync(async function (req, res) {
    const result = await err();
    res.send(result);
  })
)

function wrapAsync(fn) {
  return async function (req, res, next) {
    await fn(req, res, next).catch(next)
  }
}

 

이런형태로 사용하는 이유는 express 안에 layer.js 라는 js에서 힌트를 얻을 수 있다.

 

 

app.get('/',  errorFunction(async function (req, res) { // 라우터 실행되다가
    const result = await err();	// 에러가 발생
    res.send(result);
  })
)

async function err(){
  throw new Error('에러 발생');
}

app.use(function (error, req, res, next) { // 여러가 여기로 넘어옴
  res.json({ message: error.message })
})

function errorFunction(fn) {
  return async function (req, res, next) {
    await fn(req, res, next).catch(next)
  }
}

나중에 더 응용해보자

 

 

 

Reference

https://programmingsummaries.tistory.com/399

 

Express.js 라우팅 핸들러에 async/await 을 적용할 수 있을까?

들어가며 지난 2017년 2월 22일, node.js 의 자바스크립트 엔진인 V8 이 5.5 버전으로 업그레이드되면서 특별한 옵션 없이도 바로 async/await 을 네이티브로 사용할 수 있게 되었다. 물론 이전 버전의 nod

programmingsummaries.tistory.com

https://jeonghwan-kim.github.io/node/2017/08/17/express-error-handling.html

 

에러 처리를 위한 익스프레스 가이드

원문: http://thecodebarbarian.com/80-20-guide-to-express-error-handling.html 익스프레스의 에러 처리 미들웨어는 HTTP 응답 로직을 견고하게 만드는 파워풀한 도구입니다. 아래는 익스프레스 코드로 작성한 코드

jeonghwan-kim.github.io

http://expressjs.com/ko/guide/error-handling.html

 

Express 오류 처리

오류 처리 다른 미들웨어 함수와 동일한 방법으로 오류 처리 미들웨어 함수를 정의할 수 있지만, 오류 처리 함수는 3개가 아닌 4개의 인수, 즉 (err, req, res, next)를 갖는다는 점이 다릅니다. 예를

expressjs.com