Express.js의 라우터 기능

@bbearcookie · March 12, 2023 · 3 min read

Express.js의 Router 기능을 통해서 라우팅 경로를 지정하는데 의도치 않은 상황을 겪은 적이 있다.
예를 들어 /photo 경로에 대한 라우터와 /voucher 경로에 대한 라우터를 지정하고 각각 router.get('/') 으로 컨트롤러 로직을 등록했는데 /photo 에 요청해도 /voucher에 요청해도 똑같은 컨트롤러의 로직만 실행되는 것이었다.
원인은 express.Router() 객체를 참조한 것을 각 라우터가 공유해서 사용하도록 만들었기 때문이었다.

API 서버 프로그램을 만들다 보면 express에 관한 것 말고도 다양한 설정들이 존재하기에 나는 프로그램의 시작 포인트인 app.js에 express 관련 설정을 몽땅 놓지 않고 관련 파일 express.js를 따로 만들어서 설정하는 방법을 선호한다.

그래서 express.js 파일에서 app 객체를 생성하고 관련 설정을 한 뒤, app.js 같은 곳에서 사용할 수 있도록 내보냈는데 이 과정에서 express.Router() 객체도 같은 방법으로 내보내서 사용했기 때문에 문제가 생겼던 것이었다.

예시

src
├── config
│   ├── express.js
│   ├── router.js
├── router
│   ├── photo.js
│   ├── voucher.js
├── app.js

app.js

const { app, init: initExpress } = require('./config/express');
const { init: initRoute } = require('./route/route');

initExpress(); // express 설정
initRoute(app); // 라우터 경로 설정

app.listen(4000, () => {
  console.log("서버 4000 실행");
})

config/express.js

const express = require('express');
const app = express();
const router = express.Router(); // 라우터는 이렇게 공유해서 사용하면 안된다!
function init() {
  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));
}

module.exports.init = init;
module.exports.app = app;
module.exports.router = router;

config/router.js

const photo = require('../route/photo');
const voucher = require('../route/voucher');

function init(app) {
  app.use('/photo', photo);
  app.use('/voucher', voucher);
}

module.exports.init = init;

router/photo.js

// const { router } = require('../config/express');const express = require('express');
const router = express.Router();

router.route('/')
  .get((req, res) => res.json({ message: '포토 조회 API' }))
  .post((req, res) => res.json({ message: '포토 추가 API' }))
  .delete((req, res) => res.json({ message: '포토 삭제 API' }))

module.exports = router;

router/voucher.js

// const { router } = require('../config/express');const express = require('express');
const router = express.Router();

router.route('/')
  .get((req, res) => res.json({ message: '소유권 조회 API' }))
  .post((req, res) => res.json({ message: '소유권 추가 API' }))
  .delete((req, res) => res.json({ message: '소유권 삭제 API' }))

module.exports = router;

위와 같이 프로젝트가 구성되어 있을때 express.Router()각각의 라우팅 파일에서 새로 참조를 해야한다.
만약 라우팅 파일의 1번 라인 처럼 한번 참조했던 것을 가져와서 공유해서 사용한다면
/photo 로 요청해도 /voucher로 요청해도 / 경로에 대해 먼저 등록된 포토카드 관련 컨트롤러만 실행이 되어 버린다.

@bbearcookie
Frontend Developer