개발 R.I.P.

7.01 Dev.Feedback (미들웨어)

편행 2021. 7. 1. 14:44
반응형

미들웨어 작동 원리 이해하기

먼저 미들웨어 작동원리를 이해하기 위해 자주 사용하는 미들웨어를 적용하면서 정리해보려 한다.

 

미들웨어를 자주 사용하는 상황

  1. 모든 요청에 대해 url이나 메소드를 확인할 때
  2. POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
  3. 모든 요청/응답에 CORS 헤더를 붙여야할 때
  4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

미들웨어를 이용하면 node.js만으로 구현한 서버에서는 번거로울 수 있는 작업을 쉽게 적용할 수 있다.

case 1: 모든 요청에 대해 url이나 메소드를 확인할 때

미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행한다. 수많은 미들웨어가 있지만, 가장 단순한 미들웨어 로거(logger)를 예로 들어보면, 로거는 디버깅이나, 서버 관리에 도움이 되기 위해 console.log 로 적절한 데이터나 정보를 출력해준다. 데이터가 여러 미들웨어를 거치는 동안 응답할 결과를 만들어야 한다면, 미들웨어 사이사이에 로거를 삽입하여 현재 데이터를 확인하거나, 디버깅에 사용할 수 있다.

위 이미지는 endpoint가 '/'이면서, 클라이언트로부터 GET 요청을 받았을 때 적용되는 미들웨어이다. 파라미터의 순서를 잘 기억해둬야 한다. req, res는 요청(request)/응답(response)이고, next는 다음 미들웨어를 실행해주는 콜백 인수이다.

다시 위의 이미지를 보면, 미들웨어 내부에서는 아무런 작업을 하고 있지 않고, 그저 next() 함수를 호출하여 다음 미들웨어로 데이터를 전달하고 있다.

 

만약 특정 endpoint가 아닌 모든 요청에 동일한 미들웨어를 적용하고 싶을 때는 메소드 app.use를 사용하면 된다.

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

const myLogger = function (req, res, next) {
  console.log('LOGGED');
  next();
};

app.use(myLogger);

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

app.listen(3000);

위의 코드를 사용하면 모든 요청에 대해 LOGGED가 출력됨으로 동일한 미들웨어가 적용이 된 것을 확인할 수 있다.

 

case 2: POST 요청 등에 포함된 body(payload)를 구조화할 때

순수 node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻을 수 있다.

let body = [];
request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // body 변수에는 문자열 형태로 payload가 담겨져 있다.
});

body-parser 미들웨어를 사용하면 순수 node.js로 했던 것보다 더 간단하게 처리할 수 있다.

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

// 생략
app.post('/api/users', jsonParser, function (req, res) {
  // req.body에는 JSON의 형태로 payload가 담겨져 있다.
})

case 3: 모든 요청/응답에 CORS 헤더를 붙일 때

순수 node.js 코드에 CORS 헤더를 붙이려면, 응답 객체의 writeHead 메소드 등을 이용해야 한다. 이런 메소드를 이용하더라도 Access-Control-Allow-* 헤더를 매번 재정의해야 한다. 그뿐만 아니라, OPTIONS 메소드에 대한 라우팅도 따로 구현해야 했다.

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};

// 생략
if (req.method === 'OPTIONS') {
  res.writeHead(201, defaultCorsHeader);
  res.end()
}

// 이후 매번 요청을 할 때마다 writeHead메소드를 이용해 계속 불러줘야 함.

cors 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.

const cors = require('cors')

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

물론 특정 요청에만 cors 미들웨어를 적용할 수도 있다.

const cors = require('cors')

// 생략
// endpoint가 /products/:id 인 것을 요청하는 상황에만 CORS를 허용
app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

case 4: 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

app.use((req, res, next) => {
  // 토큰의 여부를 판단하고, 있으면 다음 미들웨어를 적용하고, 아니면 에러메세지를 보낸다.
  if(req.headers.token){
    req.isLoggedIn = true;
    next()
  } else {
    res.status(400).send('invalid user')
  }
})

HTTP 요청에서 토큰이 있는지 여부를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어 예제

 

반응형

'개발 R.I.P.' 카테고리의 다른 글

7.04 Dev.Feedback (Node.js #3)  (0) 2021.07.04
7.03 Dev.Feedback (Side Effect)  (0) 2021.07.03
6.30 Dev.Feedback (HTTP/ Network)  (0) 2021.06.30
6.29 Dev.Feedback (Promise)  (0) 2021.06.29
6.28 Dev.Feedback (React #6 useEffect)  (0) 2021.06.28