[Express] 어플리케이션 레벨 미들웨어와 라우터 레벨 미들웨어

2023. 10. 29. 20:29프로그래밍/웹 개발

https://expressjs.com/en/guide/using-middleware.html

 

Using Express middleware

Using middleware Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is essentially a series of middleware function calls. Middleware functions are functions that have access to the request ob

expressjs.com

 

- 라우터의 개념

express에서는 라우터라는 것이 있다. 라우터는 쉽게 말해 컨트롤러에서 url을 매핑시켜주는 역할을 제공한다.

조금 특이한 점이라고하면 이 라우터는 같은 METHOD타입의 같은 경로의 url을 여러개 가지고 처리할 수 있다는 점이다.

이를 '미들웨어'함수 라고 한다. 

미들웨어 함수는 스택으로 쌓일 수 있으며 next()를 호출함으로써 아래쪽의 미들웨어 스택으로 이동할 수 있다. 만약 현재 라우터에서 필요한 로직을 모두 처리했고 다음 라우터로 이동하기 위해 현재의 미들웨어 함수를 건너뛰고 싶다면 next('route')와 같이 호출한다고 한다.

그리고 아래쪽의 미들웨어 스텍을 모두 점검하고 난 후에 next('route')를 호출한 아래부터 코드가 실행된다.

var express = require('express');
var app = express();

/* GET home page. */
app.get('/', function (req, res, next) {
    console.log("첫번째 미들웨어 함수 실행")
    next();
});

app.get('/', function (req, res, next) {
    console.log("두번째 미들웨어 함수 실행")
    next('route');
    console.log("두번째 미들웨어 두번째 함수 실행")

})

app.get('/', function (req, res, next) {
    console.log("세번째 미들웨어 함수 실행")
    next();
});


app.get('/', function (req, res, next) {
    console.log("네번째 미들웨어 함수 실행")
    // next();
    return res.render('index.pug');
});


module.exports = app;

여기서 착각하면 안되는 것은 app.use든 app.get이든 app.post든 이와 같은 모든 것들이 라우터이면서 동시에 미들웨어라는 것이다. 

라우터의 개념을 알아봤으니 이제 어플리케이션 레벨의 미들웨어와 라우터  레벨 미들웨어에 대해 알아보자.

 

 

- 어플리케이션 레벨 미들웨어

아래는 일반적인 express 프로젝트의 app.js의 상위에 위치하는 코드이다.

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

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

이와 같이 require('express')를 이용해 얻어온 app객체를 이용해 어플리케이션에 관련된 여러가지 설정을 하는 미들웨어들이 어플리케이션 레벨 미들웨어이다. 일반적으로 설정되는 app.js파일애는 어플리케이션 레벨 미들웨어가 설정돼있고 이 부분은 렌더링할때마다 실행되게 된다. 그래서 라우터 등록은 보통 다음과 같이 많이 사용한다.

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
app.use('/', indexRouter);
app.use('/users', usersRouter);

* 추가 - javascript에서는 특이하게 변수를 다른 파일에서 사용하기 위해서는 exports하는 과정이 필요하다.

Java를 기준으로 설명하자면 Java는 접근제어자를 설정함으로써 다른 경로에 있는 java 파일에서 접근(import)이 가능하다.

그러나 javascript는 import의 개념이 require로 쓰이며 이 또한 내보내는 쪽에서도 exports로 명시를 해주어야만 참조할 수 있게 된다. 

exports와 module.exports에 대해서는 후술하겠다.

 

아무튼 위와같은 코드로 라우터를 따로 설정해서 사용하는데 그 이유는 다음과 같다.

라우터를 모듈화함으로써 가독성 및 유지 보수가 편리해진다.

특정 경로로 들어오는 요청에 대해서 라우터를 분리해놓으면 훨씬 더 명확하게 파일 계층 구조를 파악하고 라우터의 구조를 확인할 수 있다.

어플리케이션 레벨 라우터는 일반적으로 app이라는 이름으로 사용하고  어플리케이션 전체에 설정하는 로깅, 에러처리, 경로 설정등의 역할을 담당한다. 모든 요청에 대해서 하나의 파일에서 굉장히 많은 미들웨어 스택으로 관리하는 것은 비효율적일 수 있다. 따라서 특정 url로 들어오는 요청에 대한 라우터는 따로 설정해주고 exports를 통해 Router()로 생성한 라우터를 app.js로 내보낸 후 app.js에서는 해당 라우터를 특정 url요청에 대해서 이 라우터로 이동시키겠다 라고 위와 같이 등록하는 것이다.

 

 

- 라우터 레벨 미들웨어

라우터 레벨 미들웨어는 어플리케이션 레벨 미들웨어와 다르게 라우터와 관련된 설정들을 따로 해주는 미들웨어를 뜻한다. 

app.use를 통해 특정 경로로 들어오는 접속에 대한 처리를 하는 라우터를 생성후 (require('express').Router()) 매핑해주고 미들웨어를 추가해준다. 이렇게 url 요청에 따른 로직을 수행하기 위해 별도의 라우터로 설정된 미들웨어들을 라우터 레벨 미들웨어라고 하는 듯 하다.

이 라우터들을 등록할 떄는 app.js에서 default로 생성된 express의 app을 이용해 app.use로 등록해준다.

 

? exports와 module.exports의 차이는?

exports는 module.exports와 같은 객체를 가르키지만 내부적으로는 약간 다른 동작을 하는 듯 하다.

결론부터 말하자면 exports는 module.exports를 가르키는 Call By Reference에 의한 객체라는 것이다.

따라서 module.exports를 사용하면 바로 객체에 접근할 수 있게 되지만 exports를 사용할 것이라면 프로퍼티를 설정해줘서 접근하는 방식을 취해야한다. 

# justExports.js
exports.a = {a:3};

# module.js
module.exports = {b:3};


# test.js
const moduleResult1 = require('./service/justExports.js');
const moduleResult2 = require('./service/module.js');
console.log(moduleResult1.a)
console.log(moduleResult2)