Express에서 Exception 처리하기
이글의 목적
Express은 공통적인 exception handling 방법을 제공하지 않기 때문에, 모든 함수 내부에 try-catch문을 이용하여 exception을 처리해야 합니다. 모든 함수 내부에 exception 처리 로직을 넣는 것은 반드시 필요하나, 반복적인 코드가 늘어나게 됩니다. 이 글에서는 모든 함수에서 발생하는 exception을 한 곳에서 처리 할 수 있는 방법을 안내합니다.
일반적인 방법
일반적으로 아래와 같이 라우터에 등록하는 모든 라우팅 핸들러 함수는 내부적으로 try-catch문으로 exception 처리를 해야 합니다. 라우팅 핸들러 개수가 적으면 상관 없겠지만, 개수가 많아지면 모든 함수에 try-catch문을 작성하는 것은 힘든 일입니다.
const express = require('express');
const app = express();
const port = 3000;
const router = express.Router();
router.get('/', (req, res) => {
try{
//비즈니스 로직
}catch(e){
//exception 처리
}
res.send('응답');
});
app.use(router);
app.listen(port, () => {
console.log('서버가 구동되었습니다.');
});
Exception 처리 코드 반복 피하는 방법
Express.Router 클래스는 사용자가 작성한 라우팅 핸들러를 등록받고, HTTP 요청이 오면 해당 함수를 호출하여 처리합니다.
이 글에서 사용하는 방법은 다음과 같습니다. 먼저, Express.Router 클래스를 감싸는 Wrapper 모듈을 만들고, 그 모듈에 라우팅 핸들러를 등록합니다. Wrapper 모듈은 등록받은 라우팅 핸들러를 try-catch문으로 감싸서 Express.Router에 등록을 하는 방식입니다.
// register.js
const add = function (router, method, path, fn) {
let addFn;
if(method === 'get'){
addFn = router.get;
}else if(method === 'post'){
addFn = router.post;
}else if(method === 'delete'){
addFn = router.delete;
}else if(method === 'put'){
addFn = router.put;
}else{
throw `지원하지 않은 HTTP Method 입니다.(${method})`;
}
addFn.call(router, path, async function (req, res) {
try {
await fn(req, res);
} catch (e) {
/**
Exception 처리 공통 로직
ex: 로깅, 404 응답 등
**/
}
});
};
exports.getRegister = function(){
const express = require('express');
const router = express.Router();
return {
get: (path, fn) => {
add(router, 'get', path, fn);
},
post: (path, fn) => {
add(router, 'post', path, fn);
},
put: (path, fn) => {
add(router, 'put', path, fn);
},
delete: (path, fn) => {
add(router, 'delete', path, fn);
},
getRouter: () => router
}
};
위 모듈을 통해 라우팅 핸들러를 등록하면, exception이 발생했을 때 공통적으로 처리해야 할 로직을 각각 핸들러에 작성하지 않고 Wrapper 모듈 한 곳에 작성하여 처리 할 수 있습니다.
const express = require('express');
const app = express();
const port = 3000;
const register = require('./register').getRegister();
register.get('/', (req, res) => {
//비즈니스 로직
});
register.post('/', (req, res) => {
//비즈니스 로직
});
app.use(register.getRouter());
app.listen(port, () => {
console.log('서버가 구동되었습니다.')
});
위 코드와 같이 각 라우팅 핸들러에는 비즈니스 로직만 작성하고, exception이 발생했을 때 공통적으로 처리해야 하는 로직(예: 에러 메시지를 포함한 response 리턴)을 반복적으로 작성하는 것을 피할 수 있습니다.