현재 프로젝트 화면 구현, 개발하지 않은 상태이다.HTTP Method 중 GET, DELETE는 URL을 통해 필요한 데이터를 받을 수 있어 API 구현 테스트를 할 수 있었다.하지만 POST, PUT은 웹페이지 body(본문)에 데이터를 담아 요청을 전송하기때문에 화면 구현 없이 API 테스트를 할 수 없었다.
그래서
특정 주소로 정보를 보내줄 클라이언트(프론트 엔드)가 아직 개발되지 않았다면, 서버 프로그램이 들어온 API 요청에 대해서 제대로 동작하는지 확인할 수 있게 도와주는 API 플랫폼인 POSTMAN을 설치했다.
POSTMAN 설치
Postman API Platform | Sign Up for Free
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
www.postman.com
express.js 공식 홈페이지에서 더 찾아보고싶은 메소드와 속성들을 찾아봤다.
Express 4.x - API Reference
Express 4.x API express() Creates an Express application. The express() function is a top-level function exported by the express module. var express = require('express') var app = express() Methods express.json([options]) This middleware is available in Ex
expressjs.com
app.use([path,] callback [, callback...])
미들웨어를 등록할 때 사용된다. (여기서 callback function = middleware function)
미들웨어는 app.use와 함께 사용된다. => app.use(미들웨어)
경로가 기본적으로 "/"로 설정되어 있으므로 경로 없이 작성된 미들웨어는 앱에 대한 모든 요청에 대해 실행됩된다. 예를 들어, 다음과 같은 미들웨어 함수는 앱에 대한 모든 요청에 대해 실행된다.
app.use(function (req, res, next) {
console.log('모든 요청에서 미들웨어 실행')
next()
})
app.use('/abc', function (req, res, next) {
console.log('/abc로 시작하는 요청에서 미들웨어 실행')
next()
})
미들웨어 함수는 순차적(위->아래)으로 실행되기 때문에 미들웨어를 포함하는 순서는 중요합니다.
next 매개변수는 다음 미들웨어로 넘어가는 함수이다. next를 실행하지 않으면 다음 미들웨어가 실행되지 않는다.
// this middleware will not allow the request to go beyond it
app.use(function (req, res, next) {
res.send('Hello World')
})
// requests will never reach this route
app.get('/', function (req, res) {
res.send('Welcome')
})
Error-handling middleware
에러 처리 미들웨어는 항상 네 개의 매개변수(err, req, res, next)를 가진다. 에러 처리 미들웨어 함수로 식별하려면 네 개의 매개변수를 제공해야 한다. next 객체를 사용할 필요가 없더라도 함수 시그니처를 유지하기 위해 명시해야 한다. 그렇지 않으면 next 객체가 일반적인 미들웨어로 해석되어 에러를 처리하지 못할 수 있다.
즉, 모든 매개변수를 사용하지 않더라고 매개변수가 반드시 네 개여야한다.
에러 처리 미들웨어 함수는 다른 미들웨어 함수와 마찬가지로 정의되지만, 세 개 대신 네 개의 매개변수를 사용하여 (err, req, res, next)의 시그니처를 가져야 합니다.
app.use(function (err, req, res, next) {
console.error(err)
res.status(500).send('Something broke!')
})
express.json([options])
Express.js에서 제공하는 내장 미들웨어 중 하나로, 들어오는 요청의 본문(body)을 JSON 형식으로 파싱하는 역할이다.
주로 클라이언트에서 서버로 전송된 JSON 형식의 데이터를 서버에서 사용하기 위해 필요하다.
그래서 POST 요청 및 PUT 요청 처리할 때 사용된다.
클라이언트가 POST 또는 PUT 요청으로 서버에 데이터를 전송할 때, 데이터는 요청의 본문(body)에 담겨 온다. 이 데이터를 서버에서 사용하려면 express.json()을 사용하여 JSON 형식으로 파싱할 필요가 있다.
Request
req 객체는 HTTP 요청을 나타내며, 요청의 쿼리 문자열, 매개변수, 본문, HTTP 헤더 등에 대한 속성을 가지고 있다. 이이 객체는 항상 `req`로 불리지만 실제 이름은 작업 중인 콜백 함수의 매개변수에 따라 결정된다.
❓ req 객체? 어떻게 생겼는지 궁금해서 콘솔창에 찍어봤다.
//유튜버 개별 등록
app.use(express.json());
app.post("/youtubers", (req, res) => {
// console.log(req.body); //{"channelTitle" : "멍멍왈왈", "sub" : 0, "videoNum" : 0}
console.log(req);
const channelTitle = req.body.channelTitle;
if (channelTitle) {
//등록 : Map(db)에 저장(set)
db.set(id++, req.body);
// console.log(db);
res.status(201).json({
message: `${db.get(id - 1).channelTitle}님의 유튜브 시작을 응원합니다!`,
});
} else {
res.status(400).json({
message: "입력을 완성해주세요.",
});
}
});
// body에 데이터를 등록할 때 console.log(req) 결과
<ref *2> IncomingMessage {
_readableState: ReadableState {
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
awaitDrainWriters: null,
[Symbol(kState)]: 60037142
},
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
awaitDrainWriters: null,
[Symbol(kState)]: 59773016
},
_events: [Object: null prototype] {
end: [Array],
timeout: [Function: socketOnTimeout],
data: [Function: bound socketOnData],
error: [Function: socketOnError],
close: [Array],
drain: [Function: bound socketOnDrain],
resume: [Function: onSocketResume],
pause: [Function: onSocketPause]
},
_eventsCount: 8,
_maxListeners: undefined,
_writableState: WritableState {
highWaterMark: 16384,
length: 0,
corked: 0,
onwrite: [Function: bound onwrite],
writelen: 0,
bufferedIndex: 0,
pendingcb: 0,
[Symbol(kState)]: 34340940,
[Symbol(kBufferedValue)]: null
},
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 99,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
_server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 99,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
parser: HTTPParser {
'0': null,
'1': [Function: parserOnHeaders],
'2': [Function: parserOnHeadersComplete],
'3': [Function: parserOnBody],
'4': [Function: parserOnMessageComplete],
'5': [Function: bound onParserExecute],
'6': [Function: bound onParserTimeout],
_headers: [],
_url: '',
socket: [Circular *1],
incoming: [Circular *2],
outgoing: null,
maxHeaderPairs: 2000,
_consumed: true,
onIncoming: [Function: bound parserOnIncoming],
joinDuplicateHeaders: null,
[Symbol(resource_symbol)]: [HTTPServerAsyncResource]
},
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: ServerResponse {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: [Circular *1],
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [Circular *2],
_sent100: false,
_expect_continue: false,
_maxRequestsPerSocket: 0,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null
},
[Symbol(async_id_symbol)]: 9,
[Symbol(kHandle)]: TCP {
reading: true,
onconnection: null,
_consumed: true,
[Symbol(owner_symbol)]: [Circular *1]
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [
'Content-Type',
'application/json',
'User-Agent',
'PostmanRuntime/7.35.0',
'Accept',
'*/*',
'Postman-Token',
'700730ef-b00d-4ee9-99b8-f81bf6536eca',
'Host',
'localhost:3123',
'Accept-Encoding',
'gzip, deflate, br',
'Connection',
'keep-alive',
'Content-Length',
'75'
],
rawTrailers: [],
joinDuplicateHeaders: null,
aborted: false,
upgrade: false,
url: '/youtubers',
method: 'POST',
statusCode: null,
statusMessage: null,
client: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
awaitDrainWriters: null,
[Symbol(kState)]: 59773016
},
_events: [Object: null prototype] {
end: [Array],
timeout: [Function: socketOnTimeout],
data: [Function: bound socketOnData],
error: [Function: socketOnError],
close: [Array],
drain: [Function: bound socketOnDrain],
resume: [Function: onSocketResume],
pause: [Function: onSocketPause]
},
_eventsCount: 8,
_maxListeners: undefined,
_writableState: WritableState {
highWaterMark: 16384,
length: 0,
corked: 0,
onwrite: [Function: bound onwrite],
writelen: 0,
bufferedIndex: 0,
pendingcb: 0,
[Symbol(kState)]: 34340940,
[Symbol(kBufferedValue)]: null
},
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 99,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
_server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 99,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
parser: HTTPParser {
'0': null,
'1': [Function: parserOnHeaders],
'2': [Function: parserOnHeadersComplete],
'3': [Function: parserOnBody],
'4': [Function: parserOnMessageComplete],
'5': [Function: bound onParserExecute],
'6': [Function: bound onParserTimeout],
_headers: [],
_url: '',
socket: [Circular *1],
incoming: [Circular *2],
outgoing: null,
maxHeaderPairs: 2000,
_consumed: true,
onIncoming: [Function: bound parserOnIncoming],
joinDuplicateHeaders: null,
[Symbol(resource_symbol)]: [HTTPServerAsyncResource]
},
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: ServerResponse {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: [Circular *1],
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [Circular *2],
_sent100: false,
_expect_continue: false,
_maxRequestsPerSocket: 0,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null
},
[Symbol(async_id_symbol)]: 9,
[Symbol(kHandle)]: TCP {
reading: true,
onconnection: null,
_consumed: true,
[Symbol(owner_symbol)]: [Circular *1]
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_consuming: true,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/youtubers',
_parsedUrl: Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: '/youtubers',
path: '/youtubers',
href: '/youtubers',
_raw: '/youtubers'
},
params: {}, <====================================================== req.params
query: {},
res: <ref *3> ServerResponse {
_events: [Object: null prototype] { finish: [Function: bound resOnFinish] },
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [Circular *3],
[Symbol(async_id_symbol)]: 9,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [Circular *2],
_sent100: false,
_expect_continue: false,
_maxRequestsPerSocket: 0,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] { 'x-powered-by': [Array] },
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null
},
body: { channelTitle: '멍멍왈왈', sub: 0, videoNum: 0 }, <================ req.body
_body: true,
length: undefined,
route: Route {
path: '/youtubers',
stack: [ [Layer] ],
methods: { post: true }
},
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
'content-type': 'application/json',
'user-agent': 'PostmanRuntime/7.35.0',
accept: '*/*',
'postman-token': '700730ef-b00d-4ee9-99b8-f81bf6536eca',
host: 'localhost:3123',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive',
'content-length': '75'
},
[Symbol(kHeadersCount)]: 16,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
}
req.body
req.body는 Express.js에서 사용되는 객체로, HTTP 요청의 본문(body)에 포함된 데이터를 나타낸다.
기본적으로 Express는 요청의 본문을 해석(parse)하지 않기 때문에 req.body를 직접 사용하려면 express.json() 또는 express.urlencoded()와 같은 본문 구문 분석(body parsing) 미들웨어를 사용해야한다.
Response
res 객체는 Express 앱이 HTTP 요청을 받을 때 보내는 HTTP 응답을 나타냅니다.
❓res 객체가 어떻게 생겼나?
response의 속성으로 res.app, res.headersSent, res.locals가 있다.
app.get("/user/:id", function (req, res) {
console.log(res);
res.send("user " + req.params.id);
});
//console.log(res) 출력
<ref *2> ServerResponse {
_events: [Object: null prototype] { finish: [Function: bound resOnFinish] },
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: ReadableState {
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
awaitDrainWriters: null,
[Symbol(kState)]: 59773016
},
_events: [Object: null prototype] {
end: [Array],
timeout: [Function: socketOnTimeout],
data: [Function: bound socketOnData],
error: [Function: socketOnError],
close: [Array],
drain: [Function: bound socketOnDrain],
resume: [Function: onSocketResume],
pause: [Function: onSocketPause]
},
_eventsCount: 8,
_maxListeners: undefined,
_writableState: WritableState {
highWaterMark: 16384,
length: 0,
corked: 0,
onwrite: [Function: bound onwrite],
writelen: 0,
bufferedIndex: 0,
pendingcb: 0,
[Symbol(kState)]: 34340940,
[Symbol(kBufferedValue)]: null
},
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 89,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
_server: Server {
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
requestTimeout: 300000,
headersTimeout: 60000,
keepAliveTimeout: 5000,
connectionsCheckingInterval: 30000,
requireHostHeader: true,
joinDuplicateHeaders: undefined,
rejectNonStandardBodyWrites: false,
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_connections: 1,
_handle: [TCP],
_usingWorkers: false,
_workers: [],
_unref: false,
allowHalfOpen: true,
pauseOnConnect: false,
noDelay: true,
keepAlive: false,
keepAliveInitialDelay: 0,
highWaterMark: 16384,
httpAllowHalfOpen: false,
timeout: 0,
maxHeadersCount: null,
maxRequestsPerSocket: 0,
_connectionKey: '6::::3123',
[Symbol(IncomingMessage)]: [Function: IncomingMessage],
[Symbol(ServerResponse)]: [Function: ServerResponse],
[Symbol(kCapture)]: false,
[Symbol(async_id_symbol)]: 3,
[Symbol(kUniqueHeaders)]: null,
[Symbol(http.server.connections)]: ConnectionsList {},
[Symbol(http.server.connectionsCheckingInterval)]: Timeout {
_idleTimeout: 30000,
_idlePrev: [TimersList],
_idleNext: [TimersList],
_idleStart: 89,
_onTimeout: [Function: bound checkConnections],
_timerArgs: undefined,
_repeat: 30000,
_destroyed: false,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 5,
[Symbol(triggerId)]: 4
}
},
parser: HTTPParser {
'0': null,
'1': [Function: parserOnHeaders],
'2': [Function: parserOnHeadersComplete],
'3': [Function: parserOnBody],
'4': [Function: parserOnMessageComplete],
'5': [Function: bound onParserExecute],
'6': [Function: bound onParserTimeout],
_headers: [],
_url: '',
socket: [Circular *1],
incoming: [IncomingMessage],
outgoing: null,
maxHeaderPairs: 2000,
_consumed: true,
onIncoming: [Function: bound parserOnIncoming],
joinDuplicateHeaders: null,
[Symbol(resource_symbol)]: [HTTPServerAsyncResource]
},
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [Circular *2],
[Symbol(async_id_symbol)]: 30,
[Symbol(kHandle)]: TCP {
reading: true,
onconnection: null,
_consumed: true,
[Symbol(owner_symbol)]: [Circular *1]
},
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: IncomingMessage {
_readableState: ReadableState {
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
awaitDrainWriters: null,
[Symbol(kState)]: 1185840
},
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
socket: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [Circular *2],
[Symbol(async_id_symbol)]: 30,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: false,
rawHeaders: [
'User-Agent',
'PostmanRuntime/7.35.0',
'Accept',
'*/*',
'Postman-Token',
'888c670d-24a9-407b-a963-ecf649524467',
'Host',
'localhost:3123',
'Accept-Encoding',
'gzip, deflate, br',
'Connection',
'keep-alive'
],
rawTrailers: [],
joinDuplicateHeaders: null,
aborted: false,
upgrade: false,
url: '/user/1',
method: 'GET',
statusCode: null,
statusMessage: null,
client: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
setEncoding: [Function: socketSetEncoding],
_paused: false,
_httpMessage: [Circular *2],
[Symbol(async_id_symbol)]: 30,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: true,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
},
_consuming: false,
_dumped: false,
next: [Function: next],
baseUrl: '',
originalUrl: '/user/1',
_parsedUrl: Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: null,
query: null,
pathname: '/user/1',
path: '/user/1',
href: '/user/1',
_raw: '/user/1'
},
params: { id: '1' },
query: {},
res: [Circular *2],
body: {},
route: Route { path: '/user/:id', stack: [Array], methods: [Object] },
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: {
'user-agent': 'PostmanRuntime/7.35.0',
accept: '*/*',
'postman-token': '888c670d-24a9-407b-a963-ecf649524467',
host: 'localhost:3123',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive'
},
[Symbol(kHeadersCount)]: 12,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
_sent100: false,
_expect_continue: false,
_maxRequestsPerSocket: 0,
locals: [Object: null prototype] {},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
'x-powered-by': [ 'X-Powered-By', 'Express' ]
},
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null
}
Method
res.end([data] [, encoding])
응답 프로세스를 종료한다.
이 메소드를 사용하면 어떠한 데이터도 전달하지 않고 빠르게 응답을 종료할 수 있다. 만약 인수가 있다면 그 데이터도 클라이언트로 보내고 응답을 종료한다.
res.end()
res.status(404).end()
res.end()나 res.send(), res.json()도 데이터를 보내고 응답을 종료하는 것은 같다.
그렇다면 각각 언제 사용하는 것이 좋나?
res.end()는 보내줄 아무 데이터도 없는데 response를 끝내고 싶을 때 사용한다.
res.send()나 res.json()과 같은 메소드는 데이터를 포함한 응답을 보내야 하는 경우에 사용한다.
res.json([body])
JSON 형식의 응답을 보낸다. 이 메소드는 매개변수를 JSON 문자열로 변환하여 응답을 보냅니다. (내부적으로 JSON.stringify()를 사용하여 JSON 문자열로 변환해준다.)
매개변수는 객체, 배열, 문자열, 불리언, 숫자, 또는 null과 같은 모든 JSON 타입이 될 수 있다.
또한 이 메소드를 사용해 다른 값들을 JSON으로 변환할 수 있다.
즉, 객체가 아닌 것도 JSON 형식으로 바꾸어서 보내준다.
res.json(null)
res.json({ user: 'tobi' })
res.status(500).json({ error: 'message' })
res.send([body])
HTTP 응답을 보낸다.
body 매개변수는 Buffer 객체, 문자열, 객체, 불리언, 또는 배열이 될 수 있다.
res.send(Buffer.from('whoop'))
res.send({ some: 'json' })
res.send('<p>some html</p>')
res.status(404).send('Sorry, we cannot find that!')
res.status(500).send({ error: 'something blew up' })
이 메소드는 non-streaming responses에 대해 많은 유용한 작업을 수행한다. 예를 들어 Content-Length HTTP 응답 헤더 필드를 자동으로 할당하며(이전에 정의되지 않은 경우), 자동으로 HEAD and HTTP cache freshness support을 제공한다.
❓이해 안가는 용어 정리
| non-streaming responses | 데이터를 한 번에 전송하고 처리하는 방식이 아니라, 데이터를 여러 패킷이 아닌 하나의 응답으로 전송하는 방식 |
| 데이터를 한 번에 전송하고 처리하는 방식 | 클라이언트가 서버에 요청을 보내면, 서버는 해당 요청에 대한 응답을 한 번에 생성하여 클라이언트에게 전송. 이 응답은 데이터가 모두 포함된 완전한 응답이며, 클라이언트는 이 데이터를 받아 처리한다. |
| 데이터를 여러 패킷이 아닌 하나의 응답으로 전송하는 방식 | 데이터가 여러 패킷으로 나뉘어져 전송되지만 클라이언트에게는 하나의 응답으로 제공되는 방식 |
| streaming | 데이터를 조각조각으로 나누어 전송하는 방법을 의미 |
매개변수가 Buffer 객체일 때, 이 메소드는 Content-Type 응답 헤더 필드를 "application/octet-stream"으로 설정한다.(이전에 정의되지 않은 경우)
res.set('Content-Type', 'text/html') //문자열 데이터를 전달하면 Content-Type은 text/html
res.send(Buffer.from('<p>some html</p>')) //Buffer 객체는 이진 데이터를 나타냄. Content-Type이 명시되어있지 않기때문에 application/octet-stream으로 자동 설정됨
매개변수가 문자열일 때, 이 메소드는 Content-Type을 “text/html”으로 설정한다.
res.send('<p>some html</p>')
매개변수가 객체나 배열일 때, JSON 표현으로 응답한다.
res.send({ user: 'tobi' })
res.send([1, 2, 3])
'Node.js' 카테고리의 다른 글
| 12/14~15 (0) | 2023.12.18 |
|---|---|
| 12/13 express.js ==과 ===의 차이, 예외처리 고도화 (1) | 2023.12.18 |
| 12/8 map object, express (0) | 2023.12.14 |
| 12/7 express.js - req.params 형변환, JS 비구조화, JS 네이밍, Map 객체 (0) | 2023.12.14 |
| 12/6 express.js - res.json, req.params (0) | 2023.12.14 |