goal
SPRINT
- http 서버 만들기
- 각 요청 routing하기
- routing에 따른 API제공하기
- 서버를 활용하기 위한 API 문서 작성하기
HTTP Transaction
- 요청 핸들러 함수로 HTTP 서버의 인스턴스를 생성하고 특정 포트로 서버를 열 수 있습니다.
- request 객체에서 헤더, URL, 메서드, 바디 데이터를 가져올 수 있습니다.
- URL이나 request 객체의 데이터에 기반을 둬서 라우팅을 할 수 있습니다.
- response 객체로 헤더, HTTP 상태 코드, 바디 데이터를 보낼 수 있습니다.
- request 객체에서 response 객체로 데이터를 파이프로 연결할 수 있습니다.
- request와 response 스트림 모두에서 스트림 오류를 처리할 수 있습니다.
http서버란?
: http protocol을 통해 통신해서 API를 제공하는 주체
routing
: 조건에 따라 분기한다
👉️ GET/POST 요청에서 url에따라 즉, 클라이언트의 요청에 따라 routing하고, 그 요청에 따라 서버는 필요한 데이터를 만든다
데이터는 저장, 불러오기가 가능해야한다. => (일단은) JS의 object나 array에 저장할 수 있다. (=> 이 경우, 서버를 끄면 사라진다. => node.js의 filesystem을 통해서 서버를 꺼도 보존할 수 있다.(db를 통해서)(later))
API 문서 작성하기
const http = require('http'); // http module
const PORT = 5000;
const ip = 'localhost';
const server = http.createServer((req, res) => {
if (req.method === 'POST') {
if (req.url === '/lower') {
let data = '';
req.on('data', chunk => {
data = data + chunk;
});
req.on('end', () => {
data = data.toLowerCase();
res.writeHead(201, defaultCorsHeader);
res.end(data);
});
}
else if (req.url === '/upper') {
let data = '';
req.on('data', chunk => {
data = data + chunk;
});
req.on('end', () => {
data = data.toUpperCase();
res.writeHead(201, defaultCorsHeader);
res.end(data);
});
}
else {
res.writeHead(404, defaultCorsHeader);
res.end();
}
}
if (req.method === 'OPTIONS') {
res.writeHead(200, defaultCorsHeader);
res.end();
}
});
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
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
};
- Node.js 공식문서는 👉️ 여기를 참고
- HTTP header의 종류 및 항목에 대한 블로그는 👉️ 여기를 참고
createServer
로 서버 생성- 이 서버로 오는 HTTP 요청마다 createServer
에 전달된 함수가 한 번씩 호출된다.request
와 response
객체를 전달하며 요청 핸들러 함수를 호출한다. (이 요청을 실제로 처리하기 위해서는 listen
메서드가 server
객체에서 호출되어야 한다.)request
객체를 통해 적절한 작업을 실행한다.request
객체는 ImcomingMessage
의 인스턴스다.request
객체에 headers
라는 전용 객체가 있다.request
객체는 ReadableStream
인터페이스를 구현한다. > 이 스트림에 이벤트리스너를 등록하거나, 다른 스트림에 파이프로 연결할 수 있다. let data = '';
req.on('data', chunk => {
data = data + chunk;
});
req.on('end', () => {
data = data.toLowerCase();
res.writeHead(201, headers);
res.end(data);
});
on
메소드로 이벤트를 등록했다.)data
와 end
이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있다.data
이벤트에서 발생시킨 청크는 Buffer
이다.end
이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋다.)**concat-stream
나 body
** 같은 모듈로 이 로직을 감출 수 있다.request
객체가 ReadableStream
이므로 EventEmitter
이기도 하고 오류가 발생했을 때 EventEmitter
처럼 동작한다.request
스트림의 오류가 발생하면 스트림에서 error
이벤트가 발생하면서 오류를 전달한다. error
리스너를 추가해야 한다.)request.on('error', (err) => {
// 여기서 `stderr`에 오류 메시지와 스택 트레이스를 출력합니다.
console.error(err.stack);
});
const http = require('http');
http.createServer((request, response) => {
const { headers, method, url } = request;
let body = [];
request.on('error', (err) => {
console.error(err);
}).on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// 여기서 헤더, 메서드, url, 바디를 가지게 되었고
// 이 요청에 응답하는 데 필요한 어떤 일이라도 할 수 있게 되었습니다.
});
}).listen(8080); // 이 서버를 활성화하고 8080 포트로 받습니다.
👉️ 위의 예제는 요청(request)만 받을 수 있고, 응답(response)하지는 않는다.
response
객체는 ServerResponse
의 인스턴스 && WritableStream
이다.statusCode
프로퍼티를 설정해야 한다.response.statusCode = 404; // 클라이언트에게 리소스를 찾을 수 없다고 알려줍니다.
setHeader
메서드로 헤더를 설정한다.response.setHeader('Content-Type', 'application/json');
statusCode
, setHeader
는 바디 데이터를 보내기 전 적절한 순간에 헤더를 보내는 일을 노드에 의존하고 있다.writeHead
메서드를 통해 명시적으로 응답 스트림에 헤더를 작성할 수 있다.writeHead
메서드는 스트림에 상태코드와 헤더를 작성한다.response.writeHead(200, {
'Content-Type': 'application/json',
'X-Powered-By': 'bacon'
});
response
객체는 WritableStream
이므로 클라이언트로 보내는 응답 바디는 일반적인 스트림 메서드를 사용해서 작성한다.end
함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있다.response.end('<html><body><h1>Hello, World!</h1></body></html>');
request
와 같다 (위의 내용 참고)// 클라이언트가 서버에 보낸 모든 데이터를 다시 보내는 서버
// JSON.stringify 를 이용하여 데이터를 JSON방식으로 formating한다.
http.createServer((request, response) => {
const { headers, method, url } = request;
let body = [];
request.on('error', (err) => {
console.error(err);
}).on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// from here
response.on('error', (err) => {
console.error(err);
});
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json');
// 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
// response.writeHead(200, {'Content-Type': 'application/json'})
const responseBody = { headers, method, url, body };
response.write(JSON.stringify(responseBody));
response.end();
// 주의: 위 두 줄은 다음 한 줄로 대체할 수도 있습니다.
// response.end(JSON.stringify(responseBody))
// 새로운 부분이 끝났습니다.
});
}).listen(8080);
const http = require('http');
http.createServer((request, response) => {
if (request.method === 'POST' && request.url === '/echo') {
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
response.end(body);
});
} else {
response.statusCode = 404;
response.end();
}
}).listen(8080);
👉️ 위의 예제에서 url을 검사하며, "라우팅(routing)"을 하고 있다. (switch
문으로도 할 수 있으며, express
와 같은 프레임워크로도 할 수 있다.)
👉️ request
객체는 ReadableStream
이고 response
객체는 WritableStream
이다. (=== 데이터를 한 스트림에서 다른 스트림으로 직접 연결하는 pipe를 사용할 수 있음을 의미)
const http = require('http');
http.createServer((request, response) => {
if (request.method === 'POST' && request.url === '/echo') {
request.pipe(response);
} else {
response.statusCode = 404;
response.end();
}
}).listen(8080);
👉️ THIS IS "Stream(스트림)!"
출처 : https://gmlwjd9405.github.io/2019/01/28/http-header-types.html 💚️