node.js express 미들웨어
bodyParser
은 요청의 본문을 해석해주는 node.js 모듈이다.
클라이언트가 보내는 post와 put request의 body로부터 파라미터를 편리하게 추출한다.
우선, bodyParser가 없을 때의 문제점을 살펴보자.
아래와 같은 데이터를 body에 담아 post request를 보내고자 한다.
{
name: 'miniddo',
age: 20
}
// main.js
import express from 'express';
const app = express();
app.post('/connect', function(req, res) => {
console.log(req.body);
});
<output>
undefined
우리는 req.body
에 body에 담은 내용이 나오기를 기대지만, undefined
가 찍힌 것을 볼 수 있다.
디폴트 값으로 undefined
가 설정되어 있기 때문이다.
Contains key-value pairs of data submitted in the request body.
By default, it is undefined, and is populated
when you use body-parsing middleware such as body-parser and multer.
express 공식 문서에 따르면, 미들웨어 없이 req.body
에 접근하는 경우 디폴트로 undefined
가 설정되어 있고, body-parser
과 multer
같은 미들웨어를 사용하여 요청 데이터 값에 접근해야 한다고 한다.
즉, 클라이언트에서 보낸 body 값을 서버 내에서 해석 가능한 형태로 변형해야 사용 가능하다는 것이다. 이때 API 요청에서 받은 body 값을 파싱하는 역할을 수행하는 것이 body-parser
미들웨어이다.
body-parser 설치
npm install body-parser --save
위의 명령어로 bodyParser을 설치한 후, 이를 사용하여 코드를 변경해보자.
그럼 아래와 같이 req.body
안에 클라이언트가 보낸 값이 들어가 있을 것이다.
// main.js
import express from 'express';
import bodyParser from 'body-parser';
const app = express();
app.use(bodyParser().json());
app.post('/connect', function(req, res) => {
console.log(req.body);
});
<output>
{
name: 'miniddo',
age: 20
}
express 4.16.0 버전에서는 bodyParser가 express generator에 내장되어 있어 따로 설치하지 않아도 된다.
unlencoded()
의 extended
옵션은 중첩된 객체를 허용할지 말지를 결정한다.
객체 안에 객체를 parsing 할 수 있게 하려면 true
값을 넣어준다.
app.use(express.json());
app.use(express.unlencoded({extended: false}));
bodyParser는 Meteor
에서도 사용된다.
bodyParser와 함께 사용하는 것이 Picker
인데, Meteor를 위한 서버 사이드 라우터 패키지이다.
Picker
은 내부적으로 path-to-regexp npm package를 사용하므로 이에 맞게 수정해준다.
meteor add meteorhacks:picker
// main.js
import { Meteor } from 'meteor/meteor';
import bodyParser from 'body-parser';
import '/server/route';
Picker.middleware(bodyParser().json());
Picker.middleware(bodyParser().urlencoded({extended: false}));
...
// server/route.js
import { Picker } from 'meteor/meteorhacks:picker';
Picker.route('/connnect', (req, res) => {
console.log(req.body);
});
<output>
{
name: 'miniddo',
age: 20
}
클라이언트에서 post와 put 메소드로 body 객체의 내용을 보내면 request
로 요청 받는다.
요청 받은 후, reponse
로 요청에 대한 응답을 보낼 수 있다.
응답 시, HTTP 상태 코드를 함께 보낸다.
따로 설정하지 않으면, 상태 코드는 항상 200이며, 이를 변경하려면 statusCode
프로퍼티를 설정한다. 200 / 400 / 403 / 500 등 알맞는 상태코드를 설정하면 API 요청하는 곳에서 상태 코드만으로 어떤 에러인지 쉽게 파악이 가능하다.
reponse.statusCode = 404;
또한, 설정을 위해 응답 헤더를 작성할 수 있다.
이때 헤어 이름의 대소문자는 중요하지 않다.
response.setHeader('Content-Type', 'application/json');
위의 상태 변경과 응답 헤더 설정은 '암묵적인 헤더'를 사용한다.
writeHead
메소드를 통해 '명시적인 헤더'를 작성할 수 있다.
response.writeHead(200, {
"Content-Type" : "applicaion/json; charset=UTF-8"
});
end
메소드에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있다.
즉, 응답 본문을 작성하여 응답할 데이터를 보내는 역할을 한다.
response.end(JSON.stringify(result));
만약, 에러 상황에서도 정상 통신코드(200)을 돌려주고 싶다면, 응답 데이터에 상태 코드에 대한 스펙을 명시하여 전달하는 것이 좋다.
// server/router.js
import { Picker } from 'meteor/meteorhacks:picker';
Picker.router('/connect', (req, res) => {
let result = {
statusCode: '',
message: ''
};
if(...) {
result.statusCode = 200;
result.message = 'success';
}
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result));
});
지금까지 공부한 내용을 바탕으로 API를 만들어보자.
// main.js
import { Meteor } from 'meteor/meteor';
import bodyParser from 'body-parser';
import '/server/route';
Picker.middleware(bodyParser.json());
Picker.middleware(bodyParser.urlencoded({extended: false}));
...
// server/router.js
import { Picker } from 'meteor/meteorhacks:picker';
Picker.route('/api/connect/log/:id/data', (params, req, res) => { --- (1)
let result = {
statusCode: '',
message: ''
}
if(req.method == 'POST') { --- (2)
let data = req.body; --- (3)
let { id } = params; --- (4)
if(id == 'A') {
result.statusCode = 200;
result.message = 'success';
res.writeHead(200, {
"Content-Type" : "applicaion/json; charset=UTF-8"
});
} else {
result.statusCode = 400;
result.message = 'fail';
res.writeHead(400, {
"Content-Type" : "applicaion/json; charset=UTF-8"
});
}
res.end(JSON.stringify(result));
}
});
(1)
API URL 주소를 정의할 때, 컬렉션 종속성을 나타낼 수 있도록 정의한다. /api/connect/log/:id/data
"connect 하위 log의 특정 id의 데이터 값을 업데이트한다" 처럼 상위, 하위 종속성을 나타내도록 정의하는 것이 일반적이다.
(2)
API Method 유효성 검사를 한다.
(3)
rea.body 안에 요청 받은 값이 들어 있다.
(4)
route의 두 번째 인자로 받은 params
에는 주소에 가변적으로 변하는 값이 들어있다. 예제와 같이 앞에 :
를 붙여준다. ex) params = { id: '입력한 값' }
Postman 툴로 간단하게 API 테스트를 해보았다.
첫 번째는 :id
에 A
를 넣었고, 두 번째는 B
를 넣었다. 코드에 각 id 값에 따라 다른 result
값으로 응답해줬기 때문에 응답 내용은 각 조건에 맞게 나온다.
다음달이면 개발을 시작한지 딱 1년이 된다.
Front-end를 하고 싶다가도, Back-end를 하고 싶다가도 아직 고민의 시기인 것 같다. 아마 평생 고민할지도 모르겠다 ㅎㅎ 최근에는 Back-end 쪽에 좀 더 관심을 가지기 시작했는데 나한테 있어 그 첫 걸음이 API인 것 같다. 열심히 포스터 작성했으니 이제 까먹지 않고 잘 써먹어야겠다!! >0<