npm install express
npm
으로 express
를 설치한다.Express 공식 문서의 시작하기 - Hello world 예제 를 참고한다.
const express = require('express') // Express 모듈을 불러온다.
const app = express() // Express 애플리케이션 객체를 생성한다.
const port = 3000 // 사용할 포트 번호를 설정한다.
app.get('/', (req, res) => { // GET 요청에 대한 라우트 핸들러를 등록한다.
res.send('Hello World!') // 클라이언트로 "Hello World!" 문자열을 보낸다.
})
app.listen(port, () => { // 지정한 포트 번호로 HTTP 서버를 시작한다.
console.log(`Example app listening on port ${port}`) // 서버 시작 시 로그를 출력한다.
})
'Hello World!'
를 보내는 Express 서버 코드Express 공식 문서의 시작하기 - 기본 라우팅을 참고한다.
메서드와 url(/lower
, /upper
등)로 분기점을 만드는 것을 라우팅(Routing)이라고 한다.
클라이언트는 특정한 HTTP 요청 메서드(GET
, POST
등)와 함께 서버의 특정 URI(또는 경로)로 HTTP 요청을 보낸다.
라우팅은 클라이언트의 요청에 해당하는 Endpoint
에 따라 서버가 응답하는 방법을 결정하는 것이다.
추가적인 라이브러리를 사용하지 않고, 순수한 Node.js로 코드를 작성하면, 다음과 같이 작성할 수 있다.
const requestHandler = (req, res) => {
// URL 경로가 '/lower'인 경우
if(req.url === '/lower') {
// GET 요청 처리
if (req.method === 'GET') {
res.end(data) // data를 응답으로 보냄
}
// POST 요청 처리
else if (req.method === 'POST') {
req.on('data', (req, res) => {
// 데이터를 읽어들임
// do something ...
})
}
}
}
반면 Express는 프레임워크 자체에서 라우터 기능을 제공한다.
Express의 라우터를 활용하면 아래와 같이 직관적인 코드를 작성할 수 있다.
Express로 라우팅을 구현한 코드
// express.Router()를 이용하여 새로운 라우터 객체를 생성한다.
const router = express.Router()
// GET 메소드로 /lower 경로에 접근하면 data를 응답으로 보내준다.
router.get('/lower', (req, res) => {
res.send(data);
})
// POST 메소드로 /lower 경로에 접근하면 어떤 작업을 수행한다.
router.post('/lower', (req, res) => {
// do something
})
- 자동차 공장에서는 컨베이어 벨트 위에 올려진 자동차의 뼈대에, 공정마다 부품을 추가한다.
모든 부품이 추가되면 완성된 자동차가, 어딘가 문제가 있다면 불량품이 결과물로 나오게 된다.
미들웨어(Middleware)는 자동차 공장의 공정과 비슷하다.
컨베이어 벨트 위에 올라가 있는 요청(Request)에 필요한 기능을 더하거나, 문제가 발견된 불량품을 밖으로 걷어내는 역할을 한다.
미들웨어는 express의 가장 큰 장점이라고 할 수 있다.
미들웨어를 사용하는 상황은 다음과 같다.
- POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)
- 모든 요청/응답에 CORS 헤더를 붙여야 할 때
- 모든 요청에 대해 url이나 메서드를 확인할 때
- 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때
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가 담겨져 있습니다.
});
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) {
})
Mini-Node Server 리팩토링을 진행하다가 express.json()
미들웨어 사용에 에러가 난다면? express.json([options])
의 options
에 해답이 있다.
options에 {strict: false}
를 추가했다.
const jsonParser = express.json({strict: false});
// 생략
app.post('/api/users', jsonParser, function (req, res) {
})
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 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.
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: 'This is CORS-enabled for a Single Route'})
})
console.log
로 적절한 데이터나 정보를 출력한다.
- 미들웨어의 구성
![]()
- 위 그림은
endpoint
가/
이면서, 클라이언트로부터GET
요청을 받았을 때 적용되는 미들웨어이다.- 파라미터의 순서에 유의해야 하며,
req
,res
는 우리가 잘 아는 요청(request
), 응답(response
)이고next
는 다음 미들웨어를 실행하는 역할을 한다.
만약 특정 enpoint
가 아니라, 모든 요청에 동일한 미들웨어를 적용하려면 메서드 app.use
를 사용해야 한다.
use
메서드로 모든 요청에 대하여 미들웨어를 적용할 수 있다.
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를 활용한다.
console.log(req.method, req.url);
next();
};
app.use(myLogger);
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(3000);
다음은 HTTP 요청에서 토큰이 있는지를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어 예제이다.
app.use((req, res, next) => {
// 토큰이 있는지 확인, 없으면 받아줄 수 없음.
if(req.headers.token){
req.isLoggedIn = true;
next();
} else {
res.status(400).send('invalid user')
}
})
로그인 없이 웹사이트에 접근을 시도했을 때, 로그인 창으로 되돌려 보내는 경우를 경험해 본 적이 있을 것이다.
서버에서는 요청에 포함된 데이터를 통해 미들웨어가 요구하는 조건에 맞지 않으면, 불량품으로 판단하고 돌려보내도록 구현할 수 있다.