[Server] Express, Middleware
Computer Science/Server, 네트워크

[Server] Express, Middleware

반응형

Express.js 소개

 

MERN stack은 JavaScript 생태계에서 인기 있는 프레임워크인 MongoDB, Express, React, Node를 지칭하는 말입니다. 

이 중에서 Express.js는 Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크입니다.

Express로 구현한 서버가 http 모듈로 작성한 서버와 다른 점은 다음과 같습니다.

  1. 미들웨어 추가가 편리하다.
  2. 자체 라우터를 제공한다.

공식 문서를 따라 Express로 간단한 웹 서버를 만들 수 있습니다. 

 

 

순수한 node.js 코드로 라우팅을 구현한 아래의 코드

const requestHandler = (req, res) => {
  if(req.url === '/lower') {
    if (req.method === 'GET') {
      res.end(data)
    } else if (req.method === 'POST') {
      req.on('data', (req, res) => {
        // do something ...
      })
    }
  }
}

 

Express는 프레임워크 자체에서 라우터 기능을 제공합니다.

Express의 라우터를 활용하면 아래와 같이 직관적인 코드를 작성할 수 있습니다.

const router = express.Router()

router.get('/lower', (req, res) =>{
  res.send(data)
})

router.post('/lower', (req, res) =>{
  // do something
})

 

 

 

Middleware

 

 

미들웨어는 자동차 공장의 공정과 비슷합니다. 컨베이어 벨트 위에 올라가 있는 request에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 합니다. 미들웨어는 express의 가장 큰 장점입니다.

공식문서

 

자주 사용하는 미들웨어

미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행합니다.  먼저, 미들웨어를 사용하는 상황은 다음과 같습니다.

 

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

공식 문서에서 확인할 수 있는 미들웨어의 구성

endpoint가 /이면서, 클라이언트로부터 GET 요청을 받았을 때 적용되는 미들웨어입니다. 파라미터의 순서에 유의해야 합니다. req, res는 우리가 잘 아는 요청(request)/응답(response)이고, next는 다음 미들웨어를 실행합니다. 이 콘텐츠의 최상단에 나오는 이미지에서 next 의 역할을 확인할 수 있습니다. 위 그림을 살펴보면, 미들웨어 내부에서는 아무런 작업을 하고 있지 않습니다. 그저 next() 함수를 호출하여 다음 미들웨어로 데이터를 전달하고 있습니다.

만약 특정 enpoint가 아니라 모든 요청에 동일한 미들웨어를 적용하려면 메소드 app.use 를 사용합니다.

모든 요청에 대해 LOGGED가 출력되는 걸 확인할 수 있습니다.

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

const myLogger = function (req, res, next) {
  console.log('LOGGED'); // 이 부분을 req, res 객체를 이용해 고치면, 여러분들은 모든 요청에 대한 로그를 찍을 수 있습니다.
  next();
};

app.use(myLogger);

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

app.listen(3000);

 

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 미들웨어를 사용하면 위 과정을 아래처럼 간단하게 처리할 수 있습니다.

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

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

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()
}

 

cors 미들웨어를 사용하면 위 과정을 아래 코드처럼 간단하게 처리할 수 있습니다.

const cors = require('cors')

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

 

특정 요청에만 cors 모듈을 적용할 수도 있습니다.

const cors = require('cors')

// 생략
// 특정 요청에 대해 CORS 허용
app.get('/products/:id', cors(), function (req, res, next) {
  res.json({msg: 'This is CORS-enabled for a Single Route'})
})

 

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

 

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

app.use((req, res, next) => {
  // 토큰 있니? 없으면 받아줄 수 없어!
  if(req.headers.token){
    req.isLoggedIn = true;
    next()
  } else {
    res.status(400).send('invalid user')
  }
})

 

반응형