Express.js & Middleware

Jiyoung·2021년 1월 8일
1

Express.js

JavaScript 생태계에서 인기있는 프레임워크의 앞글자를 따서 MERN(MongoDB, Express, React, Node) stack이라고 하는데, 이 중에서 Express.js는 Node.js 환경에서 웹 어플리케이션 혹은 API를 제작하기 위해 사용되는 인기있는 프레임워크이다.

express 프레임워크는 npm을 통해 다운로드 받을 수 있다. express가 기존 http 모듈로 작성했던 서버와 갖는 큰 차이점은 1)미들웨어를 붙이기 쉽고, 2)자체 라우터를 제공한다는 것이다.

라우팅(Routing): 라우팅은 URI(또는 경로) 및 특정한 HTTP 요청 메소드(GET, POST 등)인 특정 엔드포인트(endpoint)에 대한 클라이언트 요청에 애플리케이션이 응답하는 방법을 결정하는 것을 말한다.

어떠한 라이브러리를 사용하지 않고 순수 node.js 코드를 작성하면 아래와 같이 작성해야 한다.

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

반면에 express는 자체 라우터 기능을 제공하므로, 라우터를 활용하면 아래와 같이 코드를 매우 직관적으로 처리할 수 있다.

const router = express.Router()

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

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

Middleware

미들웨어는 컨베이어 벨트 위에 올라가있는 request에 무언가 악세사리를 덕지 덕지 붙이거나, 혹은 불량품이라면 밖으로 걷어내는 역할을 한다고 보면 된다. 미들웨어는 express의 가장 큰 장점 중 하나이다.

다음은 미들웨어가 주로 쓰이는 상황이다.

  1. 모든 요청에 대해 url이나 메소드를 알고자 할 때
  2. POST 요청 등에서 쓰이는 body(payload)를 쉽게 얻어내고자 할 때
  3. 모든 요청/응답에 CORS 헤더를 붙일 때
  4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인하고 싶을 때

위와 같이 node.js만을 이용해서 구현할 때에 다소 번거로운 작업을 미들웨어를 통해 적용하면 보다 손쉽게 해결할 수 있다.

1) 모든 요청에 대해 url이나 메소드를 알고자 할 때

가장 단순한 미들웨어는 logger 이다. 말 그대로 로거는 디버깅이나 서버 관리에 도움이 되기 위해 console.log를 적절하게 찍어주는 역할을 한다.
이미지 출처: Express.js공식문서

미들웨어는 위와 같은 구성을 갖는다. 위 그림은 endpoint가 /이면서, GET 요청을 받았을 때 사용하는 미들웨어로, 주목할 것은 파라미터 순서이다. req, res는 우리가 잘 아는 요청/응답이며, next는 다음 컨베이어 벨트로 넘기는 작업을 한다.

특정 endpoint가 아닌 모든 요청에 대해서 미들웨어 붙일때는 아래와 같이 app.use라는 메소드를 사용한다.

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 허용
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')
  }
})
profile
경계를 넘는 삶

0개의 댓글