MERN stack
은 JavaScript
생태계에서 인기 있는 프레임워크인 MongoDB
, Express
, React
, Node.js
를 지칭하는 말이다.
이 중에서 Express
는 Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크이다.
Express
로 구현한 서버가 Node.js HTTP 모듈
로 작성한 서버와 다른 점은 다음과 같다.
->
라우팅(Routing) : 메서드와 url로 분기점을 만드는 것을 라우팅(Routing)이라고 한다.
이전에 미니노드서버
과제에서 분기(Routing)를 만들어 썻던 코드 ->
추가적인 라이브러리를 사용하지 않고 순수한 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() // express 내장 프로퍼티인 라우터() 사용
router.get('/lower', (req, res) => {
res.send(data);
})
router.post('/lower', (req, res) => {
// do something
})
자동차 공장에서 컨베이어 벨트 위에 올려진 자동차의 뼈대에, 각 공정마다 부품을 추가한다.
모든 부품이 추가되면 완성된 자동차가, 어딘가 문제가 있다면 불량품이 결과물로 나오게 된다.
미들웨어(Middleware)
는 자동차 공장의 공정과 비슷하다. 컨베이어 벨트 위에 올라가 있는 요청(Request)
에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 한다.
미들웨어는 express의 가장 큰 장점이라고 할 수 있다.
- POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
- 모든 요청/응답에 CORS 헤더를 붙여야 할 때
- 모든 요청에 대해 url이나 메서드를 확인할 때
- 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때
-> 미들웨어를 이용하면 Node.js만으로 구현한 서버에서는 번거로울 수 있는 작업을 보다 쉽게 적용할 수 있다.
Node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻을 수 있다.
네트워크 상의 chunk를 합치고, buffer를 문자열로 변환하는 작업이 필요함.
->
// Node.js로 HTTP 요청 body를 받는 코드
let body = [];
request.on('data', (chunk) => { // data가 있을때~
body.push(chunk); // 네트워크상의 chunk들을 합치고,
}).on('end', () => {
body = Buffer.concat(body).toString();
// body 변수에는 문자열 형태로 payload가 담겨져 있다.
});
body-parser 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.
npm으로 body-parser를 설치
->npm install body-parser
// 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()
을 사용할 수 있다.
// express.json()을 이용한 코드
const jsonParser = express.json();
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})
만약 express.json() 미들웨어 사용에 에러가 나면?
express.json([options])
의 options에 해결법이 있다.
위의 링크로 문서에 들어가보면 이런식으로 쓰여있다.
->
번역을 해보면 ->
req.body의 모양은 사용자 제어 입력을 기반으로 하므로 이 객체의 모든 속성과 값은 신뢰할 수 없으며 신뢰하기 전에 유효성을 검사해야 합니다. 예를 들어, req.body.foo.toString()은 여러 방식으로 실패할 수 있습니다. 예를 들어 foo가 없거나 문자열이 아닐 수 있으며, toString은 함수가 아니라 문자열 또는 기타 사용자 입력일 수 있습니다.
strict : strict 배열과 객체만 허용하거나 사용하지 않도록 설정합니다. 비활성화되면 JSON.parse가 허용하는 모든 것을 허용합니다.
이런 뜻이다.
// options에 {strict: false}를 추가 하였다.
const jsonParser = express.json({strict: false});
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})
Node.js HTTP 모듈을 이용한 코드에 CORS 헤더를 붙이려면,
응답 객체의 wirteHead 메서드를 이용할 수 있다.
하지만 Node.js 에서는 이 메서드 등을 라우팅마다 헤더를 매번 넣어주어야 했다.
그 뿐만 아니라, OPTIONS 메서드에 대한 라우팅도 따로 구현해야한다.
// Node.js에 CORS를 적용하는 예시
const defaultCorsHeader = { // CORS 헤더 만들기
'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') { // OPTIONS 메서드에 대한 라우팅
res.writeHead(201, defaultCorsHeader); // 라우팅마다 이렇게 writeHead메서드를 이용해 CORS헤더를 넣어줘야함.
res.end()
}
npm 으로 CORS를 설치
->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: '특정 요청에 대해 CORS 허용'})
})
미들웨어는 말 그대로 프로세스 중간에 관여하여 특정 역할을 수행하고,
수많은 미들웨어가 있지만, 단순한 미들웨어 로거(logger)를 예로 들어본다.
로거는 디버깅, 서버 관리에 도움이 되기 위해 console.log
로 적절한 데이터나 정보를 출력함.
데이터가 여러 미들웨어를 거치는 동안 응답할 결과를 만들어야 한다면, 미들웨어 사이사이에 로거를 삽입하여 현재 데이터를 확인하고 디버깅에 사용할 수 있다.
이런 미들웨어는 일반적으로 아래와 같은 구성을 가진다.
->
위 그림은 endpoint가 /
이면서, 클라이언트로부터 GET
요청을 받았을때 적용되는 미들웨어이다.
파라미터 순서에 유의해야한다.
-> req(요청),res(응답),next(다음 미들웨어를 실행하는 역할)
만약 특정 endpoint가 아니라 , 모든 요청에 동일한 미들웨어를 적용하려면 어떻게 해야할까??? -> app.use() 메서드를 사용하면 된다.
->
app.use(() => {console.log('hello')}) // 이렇게 써준다면 모든 요청에 console.log('hello')가 찍혀 나오는걸 볼 수있다.
아래와 같이 작성해보면 어떨까??
const logger = (req,res,next) => {
console.log(req.method, req.url); // req.method로 method가 뭔지 req.url로 url이 뭔지 모든 요청마다 출력됨을 적용시켜본다.
next(); // next(); 를 안써주면 각 분기점(라우팅)에 있는 console.log()는 출력 되지않았다. 써주면 같이 그다음에 출력된다.
}
app.use(logger);
우선 나중에 배울 내용이라 맛보기로 학습해본다.
아래에서 HTTP 요청에서 토큰이 있는지를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어예제를 다뤄보자.
토큰(Token)
은 주로 사용자 인증에 사용한다고 우선 알고있자.
// 토큰을 통해 로그인 여부를 확인하는 미들웨어 예시
app.use((req, res, next) => {
// 토큰이 있는지 확인, 없으면 받아줄 수 없음.
if(req.headers.token){
req.isLoggedIn = true;
next();
} else {
res.status(400).send('invalid user')
}
})
const express = require('express');
const port = 3000;
const app = express();
const jsonParser = express.json({strict: false});
const cors = require('cors');
app.use(jsonParser);
app.use(cors());
const logger = (req,res,next) => {
console.log(`http request method 는 ${req.method}이고 url은 ${req.url}이다`);
next();
}
app.use(logger);
app.get('/', (req,res) => {
res.send('hi');
})
app.post('/lower', (req,res) => {
console.log(req.body);
res.send(JSON.stringify(req.body.toLowerCase()))
})
app.post('/upper', (req, res) => {
console.log(req.body);
res.send(JSON.stringify(req.body.toUpperCase()));
})
app.listen(port, () => {
console.log('서버 열림?');
})
이것으로 express 학습을 마치겠다.