Express를 이용하여 응답을 보낼 때 ‘response’ 객체에 여러 메서드들이 존재한다.
만약 ‘JSON’형식으로 응답을 한다면 send()
, json()
두 개의 메서드가 가능하다.
app.get('/', function(req, res){
res.json({ user: 'geek' });
});
app.get('/', function(req, res){
res.send({ user: 'geek' });
});
오늘은 GitHub에 올려진 ‘Express’ 프레임워크 오픈소스 코드를 이용하여 두 가지 메서드가 내부에서 어떻게 동작하는지 알아보고자 한다.
express/response.js at master · expressjs/express
res.send()
먼저 소스에 보면 간략하게 파라미터에 대한 설명이 주석으로 되어 있다.
/**
* Send a response.
*
* Examples:
*
* res.send(Buffer.from('wahoo'));
* res.send({ some: 'json' });
* res.send('<p>some html</p>');
*
* @param {string|number|boolean|object|Buffer} body
* @public
*/
아래부터 코드에 나오는 ‘this’는 res.send()
Dot notation으로 호출하였으므로 ‘res’ 객체다.
그리고 코드를 보고 send에 대해서 새롭게 안 사실인데, 인자에 순서 상관없이 ‘stauts’를 넣을 수 있다.
// allow status / body
if (arguments.length === 2) {
// res.send(body, status) backwards compat
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
this.statusCode = arguments[0];
chunk = arguments[1];
}
}
이제 중요한 body의 타입 관련 로직이 나온다.
switch (typeof chunk) {
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.type('html');
}
break;
case 'boolean':
case 'number':
case 'object':
if (chunk === null) {
chunk = '';
} else if (Buffer.isBuffer(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
} else {
return this.json(chunk);
}
break;
}
send()
를 호출 전 직접적으로 Content-Type 명시하지 않았다면 ‘html’타입으로 설정된다.json()
을 호출하는 것을 볼 수 있다.그럼 우리가 res.send({ user: 'geek' })
를 호출하면 send 메서드 내부에서 type을 확인하고 ‘object’이니 res.json({ user: 'geek' })
을 호출한다.
이제 마지막 부분이다.
if (req.method === 'HEAD') {
// skip body for HEAD
this.end();
} else {
// respond
this.end(chunk, encoding);
}
send메서드와 마찬가지로 간략하게 파라미터에 대한 설명이 주석으로 되어 있다.
/**
* Send JSON response.
*
* Examples:
*
* res.json(null);
* res.json({ user: 'tj' });
*
* @param {string|number|boolean|object} obj
* @public
*/
아래부터 코드에 나오는 ‘this’는 res.json()
Dot notation으로 호출하였으므로 ‘res’ 객체다.
send와 마찬가지로 json메서드에서도 인자에 순서 상관없이 ‘stauts’를 넣을 수 있다.
// allow status / body
if (arguments.length === 2) {
// res.json(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
그리고 인자로 넣어준 데이터를 JSON 문자열로 변환하여 body에 담게된다.
var body = stringify(val, replacer, spaces, escape)
이 부분이 중요하다.
// content-type
if (!this.get('Content-Type')) {
this.set('Content-Type', 'application/json');
}
return this.send(body);
res.send(body)
를 호출한다.하지만 이 때 body는 stringify
메서드를 통해 반환된 ‘string’ 타입이다.
두개의 메서드가 연결되어 있는 것을 보았다.
그럼 이제 res.json({ user: 'geek' })
, res.send({ user: 'geek' }
를 호출했을 때 각각의 실행흐름을 살펴보자.
res.send({ user: 'geek' })
res.json({ user: 'geek' })
각각의 메서드를 직접 오프소스를 확인하여 어떻게 동작하는지 살펴보았다.
실행 흐름을 보아서 알겠지만, send()는 범용적으로 다양한 데이터 type에 대응하는 것을 볼 수 있고, json()은 JSON 데이터 type에 특화되어 있는 걸 볼 수 있다.
또 두 개의 메서드는 유기적으로 연결되어 있다.
만약 내가 응답으로 보내고 싶은 데이터가 ‘JSON’이 확실하다면 불필요하게 함수를 한번 더 호출하는 send() 보다는 json()이 적절할 거 같다!