Back to the Basics

[Web Server][Express]- Middleware 기초 - Writing Middleware for use in Express App 본문

Web

[Web Server][Express]- Middleware 기초 - Writing Middleware for use in Express App

9Jaeng 2021. 11. 5. 19:38
728x90
반응형

Middleware

Express Middleware 기초적인 사용을 위해 작성한 code 분석 , method 공부 , 개념 공부 포스팅

Express 공식문서의 Writinf middleware for use in Express apps을 참고하였습니다.

  • Middleware function Middleware 함수는 request object, response object , request-response 주기에서 next 함수에 대한 권한을 갖는 함수이다.
  • next function : Express router에서 현재 middleware 다음에 실행 될 middleware 함수를 실행한다.
  • Middleware 함수는 아래의 작업들을 수행한다.
    • 모든 code를 실행한다.
    • 요청,응답 객체에 대한 변경을 실행한다.
    • 요청 - 응답 cycle을 종료한다.
    • stack 내의 다음 middleware를 호출한다. 현재의 middleware req-rsp cycle이 종료되지 않는 경우 next() 메서드를 호출하여 다음의 middleware 함수로 control을 전달해야 한다.
/* Express middleware를 이하 위한  기초 연습

1. 모든 요청에 대해 url이나 메소드를 확인할 때 use()
2. 요청 헤더에 사용자 인증 정보다 담겨있는지 확인할 때 
 */

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

const mylogger = function (req, res, next) {
  console.log(`http request is ${req.method}, url is ${req.url}`);
  next();
}; 

// use()를 사용하면 routing 전에 미들웨어 함수를 로드한다.
app.use(mylogger); // 모든 요청에 mylogger middleware를 적용한다.
app.use((req, res, next) => {
  // token이 있는 경우에만 허용
  if (req.headers.token) {
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send("invalid user");
  }
});

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

app.listen(3000);
  • next() 메서드를 middlewae 함수 내부에서 호출하면, 앱 내의 다름 미들웨어 함수가 호출된다.
const mylogger = function (req, res, next) {
  console.log(`http request is ${req.method}, url is ${req.url}`);
  next();
};
  • use() 메서드를 사용하면 모든 request에 적용된다.아래와 같은 코드를 작성하면, app이 요청을 수신할 때마다 use로 호출한 middleware 함수가 호출된다.
// use()를 사용하면 routing 전에 미들웨어 함수를 로드한다.
app.use(mylogger); // 모든 요청에 mylogger middleware를 적용한다.
app.use((req, res, next) => {
  // token이 있는 경우에만 허용
  if (req.headers.token) {
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send("invalid user");
  }
});
  • middleware는 먼저 로드된 middleware 함수가 먼저 실행된다. 만약 use()를 사용한 middleware가 루트 경로에 대한 라우팅 이후에 로드되면, 루트 경로의 route handler가 request- response cycle을 종료하기 때문에 use() 메서드를 사용한 middleware는 실행되지 않는다. middleware function은 stack에 있는 middleware 함수에 request를 전달한다.
app.use(mylogger); // 모든 요청에 mylogger middleware를 적용한다.
app.use((req, res, next) => {
  // token이 있는 경우에만 허용
  if (req.headers.token) {
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send("invalid user");
  }
});

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

실습

  • requestTime 이라는 middleware function을 만들고 사용해보는 예제 (공식문서 예제)
/*
Express - Middleware - Create requestTime 

- requestTime middleware 함수를 만들고 request object에 requestTime이라는 property를 추가한다. 

- requestTime middleware는 현재의 시각을 request.requestTime property에 할당한다.

- root 경로의 route의 callback 함수는 이 middleware 함수가 request에 추가하는 특성을 사용한다.
*/

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

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

app.use(requestTime);
app.get("/", function (req, res) {
  let responseText = "Hello Sora!<br>";
  responseText += `<small>Request at: ` + req.requestTime + `</small>`;
  res.send(responseText);
});

app.listen(3000);

위의 앱은 요청을 실행할 때, 요청의 timestemp를 브라우저에 표시한다.

자주 사용하는 미들웨어

위의 개념을 사용하여 middleware function의 사용을 확인.

  • 미들웨어를 사용하는 상황
    1. 모든 요청에 대해 url 또는 method확인
    2. body(payload)를 구조화할 때
    3. 모든 req/res에 CORS header를 추가할 때
    4. request header에 사용자 인증 정보가 담겨있는지 확인할 때
  1. 모든 요청에 대해 URL , Method 확인

middleware function의 인자인 req, res를 사용하여 method, url 등의 정보를 쉽게 출력할 수 있다.

const myLogger=function(req,res,next){
        console.log(req.method, req.url)
};
app.use(myLogger);
app.get('/',function(req,res){
res.send('Hello sora!')
});
app.listen(3000);
  1. body(payload) 구조화

HTTP body를 node.js로 받을 때에는 아래와 같이 chunk를 합치고, buffer를 string으로 변환하는 작업으로, 다소 복잡한 방식으로 body를 얻었다.

let body=[]
request.on('data',(chunk)=>{
    body.push(chunk);
}).on('end',()=>{
    body=Buffer.concat(body).toString();
});

하지만, body-parser middlewaer를 사용하면 간단하게 구현이 가능하다.

const bodyParser=require('body-parser')
const jsonParser=bodyParser.json()

app.post('/api/users',jsonParser,function(req,res){
    // JSON type으로 req.body에 payload가 담겨져있다.
})

만약 body-parser를 사용하지 않고 아래와 같이 express로 post 요청을 처리한다면 req.body는 undefined 이기 때문에 undefined error를 마주친다.

app.post('/',function(req,res){
    console.log(req.body)
})

// Undefined Error !

body-parser 문서를 참고.

body-parser는 node.js 모듈이다. client의 post 요청의 body로부터 prameter를 편리하게 추출한다.

  • bodyParser.json([options])
    • 오직 json만 parse 하고 Content-Type header가 type option과 일치하는 요청만 확인하는 middleware를 반환한다. (Content-type이 application/json 방식인 경우만 받아준다)
    • middleware 이후에 parse 된 data를 포함한 body 객체가 request 객체에 포함된다.
  1. 모든 req-res에 CORS header를 붙일 때

node.js 에서는 아래와 같이 writeHead method를 사용해야 하고, OPTION 메서드에 대한 routing을 따로 구현해야 하며 매번 Access-Control-Allow-* header를 정의해야 한다.

// 매번 이런 header를 작성해야한다
const corsHeader={
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
'Access-Constol-Allow-Headers': 'Content-Type, Accept',
'Access-Control-Max-Age': 10
};

// OPTIONS에 대한 routing을 작성해야한다.
if(req.method==='OPTIONS'){
    res.writeHead(201,corsHeader);
    res.end()
}

하지만 cors middleware를 사용하면 이 또한 간단하게 처리할 수 있다.

const cors=require('cors')

// 생략
app.use(cors()) // 모든 요청에 대해 CORS를 허용

또한, 특정 요청에만 cors 모듈을 적용할 수도 있다.

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

app.option('/goods/:id',cors()) 
// Delete 요청에 대해 pre-flight 요청을 활성화한다.
app.del('//goods/:id',cors(),function(req,res,end){
    res.json({msg: 'This is CORS-enabled for all origin'})
})
app.listen(80,function(){
    console.log('CORS-enabled web server listening on port 80')
})

// 아래와 같이 전면적으로 pre-flight을 적용할 수도 있다.
app.options('*',cors())
  • corscors는 node.js 다양한 options CORS를 활성화제 사용할 수 있는 Connect / Express middleware를 제공하는 package이다.
    • Enabling CORS Pre - Flightpre-flight이 가능하려면 , OPTIONS handler를 route 해야 한다.
const express=require('express')
const cors=require('cors')
const app=express()

app.option('/goods/:id',cors()) 
// Delete 요청에 대해 pre-flight 요청을 활성화한다.
app.del('//goods/:id',cors(),function(req,res,end){
    res.json({msg: 'This is CORS-enabled for all origin'})
})
app.listen(80,function(){
    console.log('CORS-enabled web server listening on port 80')
})

// 아래와 같이 전면적으로 pre-flight을 적용할 수도 있다.
app.options('*',cors())
  • Configuring CORS Asynchronously
var express = require('express')
var cors = require('cors')
var app = express()

var allowlist = ['http://example1.com', 'http://example2.com']
var corsOptionsDelegate = function (req, callback) {
  var corsOptions;
  if (allowlist.indexOf(req.header('Origin')) !== -1) {
    corsOptions = { origin: true } 
// reflect (enable) the requested origin in the CORS response
  } else {
    corsOptions = { origin: false } 
// disable CORS for this request
  }
  callback(null, corsOptions) 
// callback expects two parameters: error and options
}

app.get('/products/:id', cors(corsOptionsDelegate), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for an allowed domain.'})
})

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})
  1. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

HTTP request에서 token의 여부를 판단하고, 이미 로그인되었다면 성공을 아니라면 error를 보내는 middleware 예시이다.


app.use((req,res,next)=>{
if(req.headers.token){
    req.isLoggedIn=true;
    next()
}else{
    res.status(400).send('invalid user')
}
})
728x90
반응형
Comments