Express의 미들웨어

송준섭 Junseop Song·2023년 10월 18일

JavaScript

목록 보기
3/5
post-thumbnail

참고: https://expressjs.com/ko/guide/writing-middleware.html

📌  개요

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트(res), 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수에 대한 접근 권한을 갖는 함수

그 다음의 미들웨어 함수는 일반적으로 next 라는 이름의 변수로 표시

미들웨어 함수는 다음과 같은 태스크를 수행

  • 모든 코드를 실행
  • 요청 및 응답 오브젝트에 대한 변경을 실행
  • 요청-응답 주기를 종료
  • 스택 내의 그 다음 미들웨어를 호출

현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 함(그렇지 않으면 해당 요청은 정지된 채로 방치됨)

아래는 미들웨어 함수 호출의 요소들

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

var myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();  // next()로 그 다음 미들웨어 함수를 호출
};

app.use(myLogger);

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

app.listen(3000);

위에서는 “myLogger”라는 미들웨어 함수를 정의

app.use(myLogger)를 통해 미들웨어 함수 myLogger를 로드

→ 루트 경로(/)로 라우팅하기 전에 항상 myLogger 미들웨어 함수를 로드함

앱이 요청을 수신할 때마다 앱은 “LOGGED”라는 메시지를 출력

미들웨어의 로드 순서는 중요하며, 먼저 ㄹ드되는 미들웨어 함수가 먼저 실행됨

루트 경로에 대한 라우팅 이후에 myLogger가 로드되면 루트 경로의 라우트 핸들러가 요청-응답 주기를 종료하므로 요청은 myLogger에 도달하지 못해 “LOGGED”를 출력하지 않음

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

var requestTime = function (req, res, next) {
  req.requestTime = Date.now();  // req에 requestTime이라는 특성 추가
  next();
};

app.use(requestTime);

app.get('/', function (req, res) {
  var responseText = 'Hello World!';
  responseText += 'Requested at: ' + req.requestTime + '';  // 요청의 타임스탬프
  res.send(responseText);  // 브라우저에 표시
});

app.listen(3000);

위에서는 “requestTime”이라는 특성을 요청 오브젝트(req)에 추가해주는 “requestTime”이라는 미들웨어 함수를 정의

앱의 루트에 대한 요청을 실행할 때 앱은 요청의 타임스탬프를 브라우저에 표시

사용자는 요청 오브젝트, 응답 오브젝트, 스택 내의 그 다음 미들웨어 함수, 그리고 모든 Node.js API에 대한 액세스 권한을 가지게 됨 → 미들웨어 함수의 가능성은 끝이 없음

📌  미들웨어 사용

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크

Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출

Express 애플리케이션은 다음과 같은 유형의 미들웨어 사용 가능

  • 애플리케이션 레벨 미들웨어
  • 라우터 레벨 미들웨어
  • 오류 처리 미들웨어
  • 기본 제공 미들웨어
  • 써드파티 미들웨어

애플리케이션 레벨 미들웨어 및 라우터 레벨 미들웨어는 선택적인 마운트 경로를 통해 로드할 수 있음

일련의 미들웨어 함수를 함께 로드할 수도 있으며 이를 통해 하나의 마운트 위치에 미들웨어 시스템의 하위 스택을 작성할 수 있음

❗️ 애플리케이션 레벨 미들웨어

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드 할 수 있음

이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT, POST, ,,)의 소문자로 된 HTTP 메소드

const app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

위는 마운트 경로가 없는 미들웨어 함수가 존재 → 이 함수는 앱이 요청을 수신할 때마다 실행

app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

위의 예에서는 “/user/:id” 경로에 마운트되는 미들웨어 함수가 표시

“/user/:id” 경로에 대한 모든 유형의 HTTP 요청에대해 실행

app.get('/user/:id', function (req, res, next) {
  res.send('USER');
});

위의 예에서는 라우트 및 해당 라우트의 핸들러 함수(미들웨어 시스템)이 표시

위 함수는 “/user/:id” 경로에 대한 GET 요청을 처리

app.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

위에서는 하나의 마운트 경로를 통해 일련의 미들웨어 함수를 하나의 마운트 위치에 로드

“/user/:id” 경로에 대한 모든 유형의 HTTP 요청에 대한 요청 정보(req.originalUrl, req.method)를 인쇄하는 미들웨어 하위 스택을 나타냄

app.get('/user/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('User Info');
});

// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
  res.end(req.params.id);
});

위처럼 라우트 핸들러를 이용하여 하나의 경로에 대해 여러 라우트를 정의할 수 있음

“/user/:id” 경로에 대한 2개의 라우트를 정의

두 번째 라우트는 어떠한 문제도 발생시키지 않지만 첫 번째 라우트가 요청-응답 주기를 종료시키므로(res.send를 통해) 두 번째 라우트는 절대 호출되지 않음

app.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next route
  if (req.params.id == 0) next('route');
  // otherwise pass the control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
  res.render('special');
});

위의 예에서는 id값이 0인 경우 next(’route’)를 호출하여 제어를 그 다음 라우트로 전달함

즉 res.render(’regular’)의 부분은 실행되지 않고 건너뛰고, 요청-응답 주기가 종료되지 않았으므로 그 다음 라우트인 res.render(’special’) 부분이 실행되는 것

참고로 next(’route’)는 app.METHOD() 또는 router.METHOD() 함수를 이용해 로드된 미들웨어 함수에서만 작동

❗️ 라우터 레벨 미들웨어

라우터 레벨 미들웨어는 express.Router() 인스턴스에 바인드된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일한 방식으로 작동

const router = express.Router();

router.use() 및 router.METHOD() 함수를 사용하여 라우터 레벨 미들웨어를 로드할 수 있음

const app = express();
const router = express.Router();

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id == 0) next('route');
  // otherwise pass control to the next middleware function in this stack
  else next(); //
}, function (req, res, next) {
  // render a regular page
  res.render('regular');
});

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id);
  res.render('special');
});

// mount the router on the app
app.use('/', router);

위의 코드는 위의 애플리케이션 레벨 미들웨어에 대해 표시된 미들웨어 시스템을 라우터 레벨 미들웨어를 사용하여 복제한 것

❗️ 오류 처리 미들웨어

오류 처리 미들웨어에는 항상 4개의 인수가 필요

어떠한 함수를 오류 처리 미들웨어 함수로 식별하려면 4개의 인수를 제공해야 함

next 오브젝트를 사용할 필요는 없지만 시그니처를 유지하기 위해 해당 오브젝트를 지정

그렇지 않으면 next 오브젝트는 일반적인 미들웨어로 해석되어 오류 처리에 실패

즉, 미들웨어 함수의 네 번째 인자로 next 오브젝트를 넣어주지 않으면 오류 처리 미들웨어가 아닌 일반 미들웨어로 해석이 되므로 next를 사용하지 않더라도 인자에 넣어주어야 함

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

다른 미들웨어 함수와 동일한 방법으로 위와 같이 오류 처리 미들웨어 함수를 정의할 수 있음

인자의 개수가 3개가 아닌 4개(err, req, res, next)의 인자를 갖는다는 점이 다름

❗️ 기본 제공 미들웨어

Express의 유일한 기본 제공 미들웨어 함수로 express.static(root, [options]) 함수가 있음

serve-static 을 기반으로 하는 함수로, Express 애플리케이션의 정적 자산을 제공하는 역할

root 인자에는 정적 자산의 제공을 시작하는 위치가 되는 루트 디렉토리를 지정

options 오브젝트는 선택 사항으로 다음과 같은 특성을 가질 수 있음

const options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));

위는 상세한 옵션 오브젝트와 함께 express.static 미들웨어 함수를 사용하는 예

app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

이처럼 2개 이상의 정적 디렉토리를 가질 수 있음

❗️써드파티 미들웨어

Express 앱에 기능을 추가하기 위해 써드파티 미들웨어를 사용할 수 있음

필요한 기능을 위한 Node.js 모듈을 설치한 후 애플리케이션 레벨 또는 라우터 렙레에서 해당 모듈을 앱에 로드할 수 있음

$ npm install cookie-parser
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());

위는 쿠키 구문 분석 미들웨어 함수인 cookie-parser의 설치 및 로드 예시

Express와 함께 일반적으로 사용되고 있는 써드파티 미들웨어 함수의 일부 목록: https://expressjs.com/ko/resources/middleware.html

0개의 댓글