[Node.js] Express Middleware

유동균·2023년 2월 1일
0

Node.js

목록 보기
2/11
post-thumbnail

1. Express Middleware

  • 클라이언트에게 요청이 오고 그 요청에 따른 응답을 하는 중간 과정에서 목적에 맞게 처리를 하는 거쳐가는 함수
  • req(요청) 객체, res(응답) 객체, 어플리케이션 요청-응답 사이클 도중 그 다음의 미들웨어 함수에 대한 엑세스 권한을 갖는 함수
  • 다음 미들웨어 함수에 대한 엑세스는 next()를 이용해 다음 미들웨어로 현재 요청을 넘길수 있다. 즉, next()를 통해 미들웨어는 순차적으로 처리됨(순서가 중요함)

2. Middleware 작성

  • req, res, next를 가진 함수를 작성하면 해당 함수는 미들웨어로 동작할 수 있다.
    • req : HTTP 요청을 처리하는 객체
    • res : HTTP 응답을 처리하는 객체
    • next : 다음 미들웨어를 실행하는 함수
const foo = (req, res, next) => {
  console.log("Hello World!!!");
  next();
};
 
const bar = (req, res, next) => {
  if (!isAdmin(req)) {
    next(new Error("Not Authorized"));
    return;
  }
  next();
};
  • req, res, next를 인자로 갖는 함수를 작성하면 미들웨어가 된다.
  • req, res 객체를 통해 HTTP 요청과 응답을 처리하거나 next()를 통해 다음 미들웨어를 호출해야 한다.
  • next() 함수가 호출되지 않으면 미들웨어 사이클이 멈추기 때문에 주의해야 된다.
  • 미들웨어는 적용되는 위치에 따라서 애플리케이션 미들웨어, 라우터 미들웨어, 오류처리 미들웨어로 분류가 가능하다.
  • 필요한 동작 방식에 따라 미들웨어를 적용할 위치를 결정해야 된다.

3. Middleware 특징

3.1. 모든 요청에 코드 실행

app.use(express.static());
  • Express가 모든 요청에 정적 파일을 사용
app.use(express.json());
  • Express가 모든 요청의 body를 JSON으로 파싱
  • 등등...

3.2. 다음 미들웨어 호출(미들웨어가 순차적으로 실행)

const express = require("express");
const app = expresss();

const sayHello = function (req, res, next) {
  console.log("Hello!!!");
  next();
};

app.use(sayHello);

app.get("/", function(req, res) {
  res.send("World!!!")
});
  • 클라이언트가 루트 경로로 요청을 보냈을 때 sayHello를 먼저 거치고 sayHello는 다음 미들웨어 호출next()함수를 지정하여 res.send("World!!!"); 코드가 담긴 미들웨어로 넘어가게 된다는 의미이다.

3.3. res, req 객체 변경 가능

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

const requestTime = function (req, res, next) {
  req.requestTime = Date.now();
  next();
};

app.use(requestTime);

app.get('/', function (req, res) {
  const responseText = 'Hello World!';
  responseText += 'Requested at: ' + req.requestTime + '';
  res.send(responseText);
});

app.listen(3000);
  • requestTime 미들웨어는 req객체 안에 requestTime이라는 프로퍼티를 만들었고 다음 미들웨어에서 프로퍼티 값을 가져올 수 있다.

3.4. 요청-응답 주기를 종료(res methods)

  • response의 method를 이용하여 클라이언트에게 응답을 전송한다는 의미이다.
  • 2. res, req 객체 변경 가능의 코드는 res.send(responseText);가 주기를 종료한다는 의미.

4. Middleware 유형

4.1. 어플리케이션 레벨 Middleware

  • express()로 생성할 수 있는 app 객체의 app.use()app.METHOD()(ex. app.get, app.post) 함수를 이용해 미들웨어를 app 인스턴스에 바인딩하는 미들웨어이다.
  • 마운트 경로가 없는 미들웨어 함수는 앱이 요청을 수신할 때마다 실행하게 된다.
//마운트 경로가 있으며 다음 미들웨어 함수를 호출함
app.get('/pages/', (req, res, next) => {
  console.log('Time : ', Date.now());
  next();
});

//마운트 경로가 없으며 응답을 여기서 끝냄
app.get((req, res, next) => {
  console.log('Not Found');
});
  • 하나의 경로에 app 여러개의 Method
app.get('/pages/:id', (req, res, next) => {
  //pages id가 0이면 'regular'가 아닌 'special'로 넘어감
  if (req.params.id == 0) next('route');
  //pages id가 0이 아니라면 'regular'로 넘어감
  else next();
}, (req, res, next) => {
  res.send('regular');
}
});

//pages id가 0일 때 넘어올 미들웨어
app.get('/pages/:id', (req, res, next) => {
  res.send('special');
}
  • 이 경우 위에 미들웨어가 2개 묶인 app METHOD가 위에 있으므로 일단 special보다 더 먼저 실행된다.

  • next('route')는 지금 라우터 미들웨어 스택을 벗어난 다음 라우터(special)로 제어가 넘어가게 하는 것이다. 라우터 미들웨어 스택이란 하나의 app.use()나 app.METHOD()에 묶인 라우팅 미들웨어들을 말한다. 여기서는 if-else 절이 든 미들웨어와 regular 미들웨어가 한 스택에 같이 있다고 할 수 있다.

  • 여기서 그냥 next()는 regular로 넘어가게 된다. id가 0이 아니라서 regular로 넘어간다면, 거기서 next()를 호출하지 않았으므로 special은 호출되지 않는다.

  • next() 안에 인자가 들어가는 경우는 아마 next('route')와 next(err)뿐.

  • next 안에 뭔가 인자가 들어가면 express 앱은 그것을 오류 발생이라고 보고 오류 처리와 관련없는 다른 모든 미들웨어를 다 건너뛰고 오류 처리(error handling) 라우터로 넘어간다. 단 하나 'route'만 빼고 말이다. 'route'는 현재 메소드를 벗어나 path에 해당하는 다음 라우터로 제어가 넘어가는 것이다.

4.2. 라우터 레벨 Middleware

  • 라우터 레벨은 express.Router()로 생성할 수 있는 router 인스턴스에 미들웨어가 바인딩되는 것이다. 그것 외에는 애플리케이션 레벨 미들웨어가 차이가 없다.
  • router.use()나 router.METHOD() 함수를 이용해 로드할 수 있다.
  • express.Router() 로 router 객체를 생성할 수 있는데, 미들웨어와 HTTP 메소드 라우트를 router 객체에 붙일 수 있다.
//app.js
const express = require('express');
const app = express();
const pageRouter = ('./routes/pages');
app.use('/pages', pageRouter);

//pages.js
const express = require('express');
const router = express.Router();

router.get('/pages/:id', (req, res, next) => {
  //pages id가 0이면 'regular'가 아닌 'special'로 넘어감
  if (req.params.id == 0) next('route');
  //pages id가 0이 아니라면 'regular'로 넘어감
  else next();
}, (req, res, next) => {
  res.send('regular');
}
});

//pages id가 0일 때 넘어올 미들웨어
router.get('/pages/:id', (req, res, next) => {
  res.send('special');
}

module.exports = router;
  • 이 때는 app.use('/', router); 하면서 main에서 app.use에 router 인스턴스를 받아야 한다. 이 router 인스턴스는 사실상 미들웨어처럼 행동하기에 app.use()의 매개변수가 될 수 있다.

4.3. 오류 처리 Middleware

  • 오류 처리 미들웨어는 (err, req, res, next)를 인자로 받는 것이다. 항상 4개의 매개변수가 필요하다. err.stack으로 에러 메시지를 볼 수 있다.
//오류의 종류에 상관없이 모든 오류를 잡는 미들웨어
app.get((err, req, res, next) => {
  console.log(err.stack);
  res.status(500).send('Something broke!');
});
  • 주의할 점은 오류 처리 미들웨어는 app.use() 및 라우트 호출을 정의한 뒤 거의 코드의 맨 끝에 정의해야 한다는 점이다.
  • 위의 경우 모든 오류를 잡는 미들웨어 하나만 만들었는데, 에러마다 다른 오류 처리 미들웨어 함수를 정의할 수도 있다. 이 경우 catch-all 에러 핸들러는 그 오류 처리 미들웨어들 중에서도 가장 아래 있어야 한다.
  • 오류 처리 미들웨어는 다음과 같이 부를 수 있다.
app.get('/pages/:id', (req, res, next) => {
  if (!req.params.id) next(err);
});
  • 이런 식으로 명시적으로 next(err)를 해줘야 오류 처리 미들웨어로 넘어갈 수 있다.

4.4. 기본 제공 Middleware

  • 기본 제공은 정적 리소스를 제공할 루트 디렉토리를 정하는 express.static 같은 것이 있다.
  • 정적 파일을 전달해주는데, 여기서는 /public 디렉토리가 정적 파일들이 모여 있는 루트 디렉토리가 된다.
app.use(express.static(__dirname + '/public'));

4.5. 써드파티 Middleware

  • npm 에서 설치한 helmet이나 cookie-parser 같은 모듈들이 해당이 된다.
  • 쉽게 말해 express 자체적으로 제공하지 않고 따로 설치해야 하는 것들은 다 써드파티라고 보면 된다.
> npm i cookie-parser
const express = require('express');
const app = express();
const cookieParser = require('cookie-parser');

app.use(cookieParser());

0개의 댓글