Express로 구현한 서버가 Node.js HTTP 모듈로 작성한 서버와 다른 점은 다음과 같다.
- 라우터를 제공한다. (이전 포스팅)
- 미들웨어를 추가할 수 있다. (해당 포스팅)
이번 포스팅에서는 미들웨어에 대해 알아보겠다.
미들웨어 (Middleware)란?
사전적 의미
- 미들웨어(서로 다른 여러 프로그램을 함께 운용할 수 있는 소프트웨어)
- software that acts as a bridge between an operating system or database and applications, especially on a network
미들웨어(Middleware)는 자동차 공장의 공정과 비슷하다.
- 자동차 공장에서는 컨베이어 벨트 위에 올려진 자동차의 뼈대에, 공정마다 부품을 추가한다.
- 모든 부품이 추가되면 완성된 자동차가, 어딘가 문제가 있다면 불량품이 결과물로 나오게 된다.
- 컨베이어 벨트 위에 올라가 있는 요청(Request)에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 한다.
- 미들웨어는 express의 가장 큰 장점이라고 할 수 있다.
미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수이다.
그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시되며 다음과 같은 태스크를 수행할 수 있다.
1. 모든 코드를 실행
2. 요청 및 응답 오브젝트에 대한 변경을 실행
3. 요청-응답 주기를 종료
4. 스택 내의 그 다음 미들웨어를 호출.
현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 한다. (그렇지 않으면 해당 요청은 정지된 채로 방치)
** 단순 요청(Simple Request)은 프리플라이트 요청(Preflight Request)을 생략하는 요청으로 Express 미들웨어를 사용하는 상황으로 보기는 어렵다.
자주 사용하는 미들웨어
미들웨어를 사용하는 상황은 다음과 같다.
- POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
- 모든 요청/응답에 CORS 헤더를 붙여야 할 때
- 모든 요청에 대해 url이나 메서드를 확인할 때
- 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때
미들웨어를 이용하면 Node.js 만으로 구현한 서버에서는 번거로울 수 있는 작업을 보다 쉽게 적용할 수 있다.
Express로 구현한 서버에서 흔하게 사용하는 미들웨어인 1번과 2번의 경우는 아래와 같다.
1. POST 요청 등에 포함된 body(payload)를 구조화할 때
- Node.js로 HTTP body(payload)를 받을 때는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻을 수 있다.
- 네트워크상의 chunk를 합치고, buffer를 문자열로 변환하는 작업이 필요하다.
- 자세한 설명은 'HTTP 모듈을 이용한 서버 다뤄보기' 포스팅 참고
Node.js로 HTTP 요청 body를 받는 코드
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// body 변수에는 문자열 형태로 payload가 담겨져 있다.
});
- body-parser 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.
- Express 내장 미들웨어인 express.json을 이용하여 요청 body(payload)를 받는 작업을 하면 된다.
body-parser 미들웨어를 이용한 코드 (이전 버전 : 참고만 하고 아래로 내려간다.)
// ** 우선 npm으로 body-parser를 설치
npm install body-parser
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
// 생략
app.post('/users', jsonParser, function (req, res) {
})
Express v4.16.0부터는 body-parser를 따로 설치하지 않고, Express 내장 미들웨어인 express.json()을 사용한다.
const jsonParser = express.json();
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})
express.json() 미들웨어 사용에 에러가 난다면?
- express.json([options])의 options에 해답이 있다.
- This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on body-parser.
- Object 형태가 아닌 String 등 형태도 받을 수 있게 하려면 options에 {strict: false}를 추가한다.
const jsonParser = express.json({strict: false});
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})
Property | Description | Type | Default |
strict | Enables or disables only accepting arrays and objects; when disabled will accept anything JSON.parse accepts. -> request로부터 온 어떤 데이터(예: String)든지 parse해준다. |
Boolean | true |
2. 모든 요청/응답에 CORS 헤더를 붙일 때
- Node.js HTTP 모듈을 이용한 코드에 CORS 헤더를 붙이려면, 응답 객체의 writeHead 메서드를 이용할 수 있다.
- Node.js에서는 이 메서드 등을 이용하여 라우팅마다 헤더를 매번 넣어주어야 한다.
- 그뿐만 아니라, OPTIONS 메서드에 대한 라우팅도 따로 구현해야 한다.
Node.js에 CORS를 적용하는 예시
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(200, defaultCorsHeader);
res.end()
}
cors 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.
(시간이 되면, 깃헙 cors 미들웨이 코드를 찬찬히 살펴봐도 좋을 것 같다.)
- Express에서 출처 접근 권한을 설정하려면 cors 미들웨어를 사용하면 된다.
- 세부적으로 출처를 설정해주기 위해서는 cors의 전달인자에 Configuration Options를 설정해야 한다.
- Configuration Options의 origin을 설정한다.
npm install cors
모든 요청에 대해 CORS 허용
const 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'}) // JSON 응답을 전송
})
3. 모든 요청에 대해 url이나 메서드를 확인할 때
미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행한다.
- Express에서 제공되는 미들웨어도 사용할 수 있으며, 미들웨어를 작성하여 사용할 수도 있다.
- 수많은 미들웨어가 있지만, 가장 단순한 미들웨어 함수인
로거(logger)를 예로 들겠다. 로거는 디버깅이나, 서버 관리에 도움이 되기 위해 console.log로 적절한 데이터나 정보를 출력한다.- 데이터가 여러 미들웨어를 거치는 동안 응답할 결과를 만들어야 한다면, 미들웨어 사이사이에
로거를 삽입하여 현재 데이터를 확인하거나, 디버깅에 사용할 수 있다. - 이런 미들웨어는 일반적으로 다음과 같이 구성되어 있다.
- 위 그림은 endpoint가 /이면서, 클라이언트로부터 GET 요청을 받았을 때 적용되는 미들웨어이다.
- 파라미터의 순서에 유의해야 한다. req, res는 요청(request), 응답(response)이고 next는 다음 미들웨어를 실행하는 역할을 한다.
[ next() ]
이 함수를 호출하면 앱 내의 그 다음 미들웨어 함수가 호출된다.
next() 함수는 Node.js 또는 Express API의 일부가 아니지만, 미들웨어 함수에 전달되는 세 번째 인수이다.
next() 함수에는 어떠한 이름을 지정해도 좋지만, 일반적으로 항상 “next”라는 이름을 갖는다.
만약 특정 enpoint가 아니라, 모든 요청에 동일한 미들웨어를 적용하려면?
메서드 app.use 를 사용하면 된다. 아래 코드를 직접 실행해 보면, 모든 요청에 대해 'LOGGED'가 출력되는 걸 확인할 수 있다.
use 메서드로 모든 요청에 대하여 미들웨어를 적용할 수 있다.
const express = require('express');
const app = express();
const myLogger = function (req, res, next) { // 이 미들웨어 함수는 myLogger라는 이름의 변수에 지정되어 있다.
console.log('LOGGED');
next();
};
app.use(myLogger);
// 루트 경로에 대한 라우팅 이후에 myLogger가 로드되면, 루트 경로의 라우트 핸들러가
// 요청-응답 주기를 종료하므로 요청은 절대로 myLogger에 도달하지 못하며 앱은 “LOGGED”를 인쇄하지 않는다.
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.get('/mango', (req, res) => {
res.send('Love you Mango!')
})
app.listen(3000);
만약 아래 그림과 같이, 모든 요청에 대해 메서드와 url을 출력하려면?
모든 요청에 대해 메서드와 url을 출력
const express = require('express')
const app = express()
const port = 3000
const myLogger = function (req, res, next) {
console.log(req.method, req.url); // 요청에 대한 메서드와 url
next();
};
app.use(myLogger);
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.get('/mango', (req, res) => {
res.send('Love you Mango!')
})
app.listen(3000);
4. 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때
다음은 HTTP 요청에서 토큰(주로 사용자 인증에 사용)이 있는지를 판단하여,
- 이미 로그인한 사용자일 경우 성공,
- 아닐 경우 에러를 보내는 미들웨어 예제이다.
토큰을 통해 로그인 여부를 확인하는 미들웨어 예시
app.use((req, res, next) => {
// 토큰이 있는지 확인, 없으면 받아줄 수 없음.
if(req.headers.token){
req.isLoggedIn = true;
next();
} else {
res.status(400).send('invalid user')
}
})
로그인 없이 웹사이트에 접근을 시도했을 때, 로그인 창으로 되돌려 보내는 경우를 경험해 본 적이 있을 것이다.
서버에서는 요청에 포함된 데이터를 통해 미들웨어가 요구하는 조건에 맞지 않으면, 불량품으로 판단하고 돌려보내도록 구현할 수 있다.
728x90
'FE > Network' 카테고리의 다른 글
[네트워크 계층 모델] OSI 7계층 & TCP/IP 4계층 모델 (0) | 2023.04.27 |
---|---|
IP Packet과 TCP/UDP Packet (0) | 2023.04.27 |
(비)[Network] HTTP모듈을 Express로 리팩토링 (0) | 2023.04.05 |
[Network] SOP & CORS (0) | 2023.04.04 |
[Network] Effect Hook & Ajax 요청 (0) | 2023.04.01 |