본문 바로가기

Node.js/Express

Express - 라우터 (Router)

익스프레스를 사용하는 이유 중 하나가 라우팅을 깔끔하게 관리할 수 있다는 점이다. 예를 들어, app.js에서 app.get 같은 메서드가 라우터 부분이다. 그러나 라우터를 많이 연결하면 app.js 코드가 매우 길어진다. 익스프레스에서는 라우터를 분리할 수 있는 방법을 제공한다. routes 폴더를 만들고 그 안에 index.js와 user.js를 다음과 같이 작성해주자.

 

// routes/index.js
const express = require('express');

const router = express.Router();

router.get('/', (req, res) => {
    res.send('Hello, Express');
});

module.exports = router;

 

// routes/user.js
const express = require('express');

const router = express.Router();

router.get('/', (req, res) => {
    res.send('Hello, User');
});

module.exports = router;

 

그리고 app.js에 다음과 같은 코드를 추가해주자.

 

...

const indexRouter = require('./routes');
const userRouter = require('./routes/user');

...

app.use('/', indexRouter);
app.use('/user', userRouter);

app.use((req, res, next) => {
    res.status(404).send('Not Found');
});

...

 

indexRouter는 경로 (path) 를 폴더로 지정해주어도 자동으로 index.js가 불러와지기 때문에 생략 가능하다. 또한 index.js 와 user.js 둘 다 생긴건 비슷하지만 실제 app.js 내에서 다른 주소의 라우터 역할을 하고 있다. app.use로 연결할 때의 차이 때문이다. indexRouter는 app.use('/')에 연결했고 userRouter는 app.use('/user')에 연결했다. 그리고 각각 get의 '/'에 연결되어 있으므로, indexRouter는 GET / 라우터가 되었고, userRouter는 GET /user 라우터가 된 것이다.

 

서버로 실행한 뒤 localhost:3000과 localhost:3000/user로 접속하면 각각에 해당하는 응답을 받을 수 있다.

 

이제 next 함수에 다음 라우터로 넘어가는 기능을 사용해보자. next('route') 라는 코드로 사용하며, 라우터에 연결된 나머지 미들웨어들을 건너뛰고 싶을 때 사용한다. 아래 코드를 보자.

 

router.get('/', (req, res, next) => {
    next('route');
}, (req, res, next) => {
    console.log('실행되지 않습니다.');
    next();
});
router.get('/', (req, res) => {
    console.log('실행됩니다.');
    res.send('Hello, Express.');
});

 

첫 번째 라우터에서 next로 다음 라우터로 넘어가므로, 해당 주소의 다음 라우터로 넘어가게 되어 두 번째 미들웨어인 '실행되지 않습니다.'를 콘솔에 찍는 미들웨어는 실행되지 않는다. 그러나 그 다음 라우터로 넘어가 해당 라우터의 첫 번째 미들웨어인 '실행됩니다.'를 콘솔에 찍는 미들웨어는 실행된다.

 

라우터 주소에는 정규표현식을 비롯한 특수 패턴을 사용할 수 있다. 여러 패턴이 있지만, 자주 쓰이는 패턴 하나를 알아보자. 라우트 매개변수라고 불리는 패턴이다. 

 

router.get('/user/:id', (req, res) => {
    console.log(req.params, req.query);
});

 

리액트 라우터에서 사용하던 패턴과 비슷하다. 주소에 :id가 있는데, 문자 그대로 :id를 의미하는 것이 아닌 다른 값이 들어가는 장소이다. 예를들어 /users/1, /users/123, /users/beomseok 등의 요청을 이 라우터가 처리하게 되며, 이 방식의 장점은 :id에 해당하는 1이나 123, beomseok 등을 조회할 수 있다는 점이며, req.params 객체 안에 들어가게 된다. :id이므로, req.params 객체는 { id: 'foo' } 와 같이 형성된다.

 

그러나 이 경우는 한가지 문제점이 있는데, 예를 들어 admin 유저는 다른 처리를 시키고 싶을 때, 이 라우터 (일반 라우터) 는 앞서 사용한 매개변수 라우터보다 위에 위치해야 정상적으로 처리가 된다.

 

router.get('/user/admin', (req,res) => {
    console.log('Hello, admin!');
}); // /user/admin으로 들어가면 실행됨
router.get('/user/:id', (req, res) => {
    console.log(req.params, req.query);
});
router.get('/user/foo', (req,res) => {
    console.log('Hello, foo!');
}); // /user/foo로 들어가도 실행되지 않음

 

주소에 쿼리 스트링을 쓸 수도 있는데. 쿼리 스트링의 키-값 정보는 req.query객체 안에 들어있다.

 

예를 들어 쿼리와 매개변수는 다음과 같이 파싱하여 사용할 수 있다.

 

router.get('/user/:id', (req, res) => {
    console.log(req.params, req.query);
    res.send(`Hello, ${req.params.id}!`);
});
app.use('/', router);

 

위에서 라우터 관련된 코드들을 잠시 제거하고 위의 코드를 404 코드 위에 작성한 후, /users/123?limit=5&skip=10 라는 주소로 요청을 하면,

 

{ id: '123' } { limit: '5', skip: '10' }

 

이와 같은 결과를 받을 수 있다.

 

만약 주소는 같지만 메서드가 다른 코드가 있을 때, 이를 하나의 덩어리로 줄여주기 위해 라우터에서 router.route 또는 app.route를 사용할 수 있다.

 

router.get('/abc', (req, res) => {
    res.send('GET /abc');
});
router.post('/abc', (req, res) => {
    res.send('POST /abc');
});

 

위와 같은 코드를 아래와 같이 한 묶음으로 처리할 수 있다.

 

router.route('/abc')
    .get((req,res) => {
        res.send('GET /abc');
    })
    .post((req, res) => {
        res.send('POST /abc');
    });

 

 

출처

Node.js 교과서 개정 2판 - 길벗, 조현영