미들웨어

수민·2022년 12월 9일
0

code

목록 보기
26/47

🍔미들웨어

자동차 공장에서는 컨베이어 벨트 위에 올려진 자동차의 뼈대에, 각 공정마다 부품을 추가합니다. 모든 부품이 추가되면 완성된 자동차가, 어딘가 문제가 있다면 불량품이 결과물로 나오게 됩니다. 미들웨어(Middleware)는 자동차 공장의 공정과 비슷합니다. 컨베이어 벨트 위에 올라가 있는 요청(Request)에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 합니다. 미들웨어는 express의 가장 큰 장점이라고 할 수 있습니다.

미들웨어를 사용하는 상황은 다음과 같습니다.

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

미들웨어를 이용하면 Node.js 만으로 구현한 서버에서는 번거로울 수 있는 작업을 보다 쉽게 적용할 수 있습니다.
3번과 4번은 직접 만들어 볼 예정이므로, Express로 구현한 서버에서 흔하게 사용하는 미들웨어인 1번과 2번의 경우를 살펴보겠습니다.

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

Node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻을 수 있습니다.
네트워크 상의 chunk를 합치고, buffer를 문자열로 변환하는 작업이 필요합니다.

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


[코드] Node.js로 HTTP 요청 body를 받는 코드

body-parser를 사용하면 이 과정을 간단하게 처리할 수 있습니다.



npm install body-parser

[코드] npm으로 body-parser를 설치

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

// 생략
app.post('/users', jsonParser, function (req, res) {

})

[코드] body-parser를 미들웨어를 이용한 코드

Express v4.16.0 부터는 body-parser를 따로 설치 하지 않고, Express 내장 미들웨어인 을 사용합니다.

const jsonParser = express.json();

// 생략
app.post('/api/users', jsonParser, function (req, res) {

})

Mini-Node Server 리팩토링을 진행하다가 express.json() 미들웨어 사용에 에러가 난다면? 의 options에 해답이 있습니다.

const jsonParser = express.json({strict: false});

// 생략
app.post('/api/users', jsonParser, function (req, res) {

})


options에 {strict: false}를 추가 했습니다. 어떤 의미인지 설명할 수 있어야 합니다.

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

Node.js HTTP 모듈을 이용한 코드에 CORS 헤더를 붙이려면, 응답 객체의 writeHead 메서드를 이용할 수 있습니다. Node.js에서는 이 메서드 등을 이용하여 라우팅마다 헤더를 매번 넣어주어야 합니다. 그뿐만 아니라, 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()
}


cors를 사용하면 이 과정을 간단하게 처리할 수 있습니다.



npm install cors
onst cors = require('cors');

// 생략
app.use(cors());

모든 요청에 대해 CORS 허용
const cors = require('cors')

// 생략

app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

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

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

프로그램은 직접 만들면서 확인하면 작동 원리를 더 이해하기 쉽습니다. case3과 4에 해당하는 를 직접 구현하면서 작동원리를 이해합니다.

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

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

위 그림은 endpoint가 /이면서, 클라이언트로부터 GET 요청을 받았을 때 적용되는 미들웨어입니다. 파라미터의 순서에 유의해야 합니다. req, res는 우리가 잘 아는 요청(request), 응답(response)이고 next는 다음 미들웨어를 실행하는 역할을 합니다.

만약 특정 enpoint가 아니라, 모든 요청에 동일한 미들웨어를 적용하려면 어떻게 해야 할까요? 메서드 를 사용하면 됩니다. 아래 코드를 직접 실행해 보세요. 모든 요청에 대해 LOGGED가 출력되는 걸 확인할 수 있습니다.

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);

만약 아래 그림과 같이, 모든 요청에 대해 메서드와 url을 출력하려면 어떻게 해야할까요? 직접 구현해 봅니다.


그림] 모든 요청에 대해 메서드와 url을 출력하는 예시

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

const myLogger = function (req, res, next) {
  //req를 활용합니다.
  next();
};

app.use(myLogger);

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

app.listen(3000);

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

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

토큰(Token): 주로 사용자 인증에 사용하며, Section3 인증(Authentication) 유닛에서 다룹니다.

app.use((req, res, next) => {
  // 토큰이 있는지 확인, 없으면 받아줄 수 없음.
  if(req.headers.token){
    req.isLoggedIn = true;
    next();
  } else {
    res.status(400).send('invalid user')
  }
})
profile
헬창목표

0개의 댓글