3 minute read

미들웨어

미들웨어는 Express 모듈의 핵심이다.

Node.js에서 미들웨어는 클라이언트의 요청과 서버의 응답 사이에서 동작하는 함수이다.

app.use(미들웨어);

app.use 안에 있는 모든 함수들은 모두 미들웨어이며 요청이 올때마다 이 미들웨어를 거친다. 즉, 요청과 응답의 중간(middle, 미들)에 위치하여 미들웨어 라고 부른다. 미들웨어는 요청과 응답을 조작하여 기능을 추가하기도 하고, 나쁜 요청을 걸러내기도 한다.

미들웨어 처리 과정

  1. 미들웨어는 요청과 응답을 조작하고,
  2. 다음 미들웨어를 호출하고,
  3. 요청을 처리하는 핵심 애플리케이션 로직에 접근할 수 있다. (next)

미들웨어 구조

미들웨어는 req, res, next와 같은 매개변수를 받아서 작동한다.

구조

// function 부분이 미들웨어 이다. 
app.use(function(req, res, next) {
  // 미들웨어 처리 로직
  next();
});

즉, app.use에 매개변수가 req, res, next인 함수를 넣으면 된다.
여기서 미들웨어는 next 인자가 들어있다는 점이 특징이다.

  • req: HTTP 요청 객체.
  • res: HTTP 응답 객체
  • next: 다음 미들웨어를 호출하는 함수. 미들웨어는 next 함수를 호출하지 않으면 다음 미들웨어를 실행하지 않는다

미들웨어 사용

주소를 첫 번째 인수로 넣어주지 않는다면 미들웨어는 모든 요청에서 실행되고, 주소를 넣는다면, 해당하는 요청에서만 실행됩니다.

Express 함수 내용
app.use(미들웨어) 모든 요청에서 해당 미들웨어 실행
app.use(/경로, 미들웨어) path로 시작하는 요청에서 미들웨어 실행
app.get(/경로, 미들웨어) path로 시작하는 GET 요청에서 미들웨어 실행
app.post(/경로, 미들웨어) path로 시작하는 POST 요청에서 미들웨어 실행

사진1

출처) 바로가기

미들웨어 동작

  1. 클라이언트가 서버로 요청을 보낸다.
  2. 요청이 Express 애플리케이션에 도착하면, 등록한 미들웨어 함수들이 먼저 차례대로 실행된다.
  3. 모든 미들웨어 함수의 처리가 완료되면, 요청을 처리할 수 있는 라우트 핸들러가 실행된다.
  4. 라우트 핸들러가 응답(response)을 반환하면, 응답 객체가 클라이언트로 전송 된다.
const express = require('express');
const cors = require('cors'); // cors 미들웨어 임포트

const app = express();

// CASE1: 모듈 미들웨어 (이미 구현된 cors 라는 모듈의 미들웨어)
// app.use가 미들웨어가 아니라 app.use()의 인자안의 함수가 미들웨어임.
app.use(cors()); 

// CASE2: 사용자정의 미들웨어
app.use((req, res, next) => {   // 로그를 찍는 용도의 미들웨어
  console.log('first');
  next();
});

// CASE3: 모듈 미들웨어 (라우트 핸들러 미들웨어)
// app.get이 미들웨어가 아니라 app.get()의 인자안의 함수가 미들웨어임.
app.get('/', (req, res) => {
  res.send('Hello, World!');
});

위 코드에서는 총 3개의 미들웨어를 구현하고있다.

  • cros 미들웨어
  • 로그를 찍는 미들웨어
  • 라우터핸들러 미들웨어

이제 라우터 요청을 수행하기 전에 등록한 미들웨어가 동작하게 된다. 마치 스프링의 AOP 미들웨어를 구현한 느낌이다..!

미들웨어 구현 예제

# 미들웨어 관리 폴더를 생성하고 작업한다.
mkdir middlewares
cd middlewares

로그 찍는 미들웨어 모듈 만들기

/middlewares/logger.js

const logger = () => (req, res, next) => {
  const log = `${req.method} ${req.url}`
  console.log(log)
  next()
}

module.exports = logger

/app.js

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

// 미들웨어 Import
const logger = require('./middlewares/logger'); 

// 미들웨어 등록
app.use(logger())

// 서버 실행
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

# 접속 /about
GET /about
# 접속 /product
GET /product
# 접속 /admin
GET /admin

Express의 에러처리 미들웨어

에러 핸들링을 위한 미들웨어어 알아본다. Express에서 제공하는 next 함수의 인자로 에러 객체를 전달하면, 에러 핸들러 미들웨어에서 처리할 수 있다.

/app.js

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

// (라우트 핸들러 미들웨어)
app.get('/', (req, res) => {
  res.send('Hello, World!');
});

// (에러처리 미들웨어) 
// 1. 맨앞 err을 포함한 인자 4개를 가지는 특징이 있다.
// 2. 라우터 미들웨어보다 뒤에있는 특징이 있다.
app.use((err, req, res, next) => {
  console.error(err);                       // 에러 로그를 출력
  res.status(500).send('Something broke!'); // 에러 응답을 전송
});

app.get('/errRun', (req, res) => {
   console.log('의도한 익셉션 수행');
   throw new Error('에러는 에러 처리 미들웨어로 갑니다.');
});

// 서버 실행
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

Express에서 에러 처리 미들웨어는 4개의 인자로 오버로딩 되어 구현되어있다.
이 미들웨어의 특이한 점은 next() 함수를 호출하지 않아도 된다. 이는 다른 미들웨어와 달리, 요청 처리 결과에 영향을 미치지 않는 특수한 미들웨어이기 때문이다.

  1. app.use를 사용하여 등록 한다.
    • 첫 번째 인자를 err(에러) 객체로 하여 총 4개의 인자를 부여 해야한다.
  2. 특수한 미들웨어라서 일반적으로, 모든 미들웨어 다음줄 code line 에 등록하며, 요청 처리 중 발생하는 에러를 처리한다. 아래는 호출되는 예시다.
    • next(err) 함수를 호출한 경우
        // 실제 미들웨어 내용을 까보면 아래 내용이 구현되어 있을것이다.
        try {
       	   // .. 에러 발생 코드
        } catch(err) {
        	      error(err); // next()에 인수가 있다면, 에러 처리 미들웨어로 점프한다.
        }
      
      
    • 라우터 함수나 미들웨어 함수 내부에서 에러가 발생한 경우
  3. 에러가 발생한 경우 이후의 미들웨어는 실행되지 않고, 바로 에러 처리 미들웨어로 전달 된다.
    • /errRun 을 요청해보면 결과는 다음과 같다.
        의도한 익셉션 수행
        Error: 에러는 에러 처리 미들웨어로 갑니다.
            at /fswas/wasadm/nodeStudy/app_temp.js:19:10
            at Layer.handle [as handle_request] (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/layer.js:95:5)
            at next (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/route.js:144:13)
            at Route.dispatch (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/route.js:114:3)
            at Layer.handle [as handle_request] (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/layer.js:95:5)
            at /fswas/wasadm/nodeStudy/node_modules/express/lib/router/index.js:284:15
            at Function.process_params (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/index.js:346:12)
            at next (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/index.js:280:10)
            at Layer.handle [as handle_request] (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/layer.js:91:12)
            at trim_prefix (/fswas/wasadm/nodeStudy/node_modules/express/lib/router/index.js:328:13)
      
      

라우터 미들웨어

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

// 라우터 미들웨어 등록
//이 미들웨어는 router.get() 함수로 등록된 라우트 핸들러가 실행되기 전에 항상 실행
router.use((req, res, next) => {
  console.log('Router middleware executed.');
  next();
});

/* 라우트 핸들러 등록
* WAS 설정에서 docBase 설정과 동일해보인다.
*/
router.get('/co', (req, res) => {
  res.send('Hello, router!');
});

/* 라우터 등록 
* WAS 설정에서 ContextPath 설정과 동일해보인다. (만약 /router 경로 요청이 오면)
*/
app.use('/router', router);

// 기본 라우트 핸들러 등록
app.get('/', (req, res) => {
  res.send('Hello, world!');
});

app.listen(3000, () => {
  console.log('Server is listening on port 3000');
});

정리

라우터 미들웨어는 더 깊게 다루어야 하므로 따로 포스팅 예정이다.

Leave a comment