테스트 코드 작성이 쉬운 Express 서버 설계

이글의 목적

express를 이용하여 서버 프로그램을 개발 할 때 테스트 코드를 작성하기 쉬운 구조로 설계 하는 방법에 대해 소개합니다.

기본적으로, express를 이용하여 서버를 개발 할 때 아래와 같이 코드를 작성합니다.

// app.js
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(port)

위 코드는 아주 기초적인 예시이고, 실제로 서버를 개발할 때는 express.Router 클래스를 활용하여 많은 라우트 핸들러를 작성하게 됩니다.

라우트 핸들러가 많아지면 별도의 모듈로 라우트 핸들러를 작성하고, app.js 에서 작성한 모듈을 마운트하는 방식을 많이 사용합니다.

아래 코드는, 사용자 관련 요청을 처리하는 모듈을 별도로 작성하여 app.js 에서 마운트하는 예시입니다.

// users.js - 사용자 관련 요청 처리 라우트 모듈
const express = require('express');
const router = express.Router();

router.get('/', function(req, res) {
  //사용자 조회 로직
});

router.post('/new', function(req, res) {
  //신규 사용자 추가 로직
});

module.exports = router;
//app.js
const users = require('./users');
...
app.use('/users', users);
...

위와 같은 방식으로 코드를 작성하면 app.js가 깔끔해지고, 연관성이 있는 라우트 핸들러들을 하나의 모듈로 묶어서 관리 할 수 있다는 장점이 있습니다.

하지만, 비즈니스 로직을 처리하는 함수가 익명 함수로 선언되어 있기 때문에, 테스트 코드를 작성하기가 어려운 단점이 있습니다.

아래 코드를 보면 신규 사용자 추가를 수행하는 함수가 익명 함수로 라우터에 등록이 되므로, 저 익명함수를 따로 호출하여 단위 테스트를 작성할 수가 없습니다. express 서버를 구동시켜 테스트를 하거나, 별도의 라이브러리 도움을 받아 테스트를 해야합니다.

router.post('/new', function(req, res) {
  //신규 사용자 추가 로직
});

단위 테스트를 하기 쉬운 구조로 만들기 위해서는, 비즈니스 로직을 수행하는 함수와 라우터에 등록하는 코드를 분리해야 합니다.

아래 코드는 사용자 관련 요청 비즈니스 로직을 수행하는 모듈, 비즈니스 수행 모듈을 라우터에 등록하는 모듈, 그리고 app.js 3단계로 나누어진 모습입니다.

//users_biz.js
exports.new = function(req,res){
    //신규 사용자 추가 로직
}
exports.get = function(req,res){
    //사용자 조회 로직
}
exports.delete = function(req,res){
    //사용자 삭제 로직
}
//users_router.js
const express = require('express');
const router = express.Router();
const biz = require('./users_biz');

//신규 사용자 추가 요청 처리 핸들러
router.post('/new', biz.new);

//사용자 조회 요청 처리 핸들러
router.get('/get', biz.get);

//사용자 삭제 요청 처리 핸들러
router.delete('/delete', biz.delete);
//app.js
const users = require('./users_router');
...
app.use('/users', users);
...

위와 같이 코드를 작성하면, 단위 테스트 코드에서 users_biz.js를 마운트 하여 사용자 관련 비즈니스 수행 함수들을 테스트 할 수 있게 됩니다.