S2) Unit 10. [์ค์ต] Mini Node Server
๐ฃ Mini Node Server ๋ง๋ค๊ธฐ
1๏ธโฃ ์๋ฒ ์์ฑ
const http = require('http');
const server = http.createServer((request, response) => {
// ์ฌ๊ธฐ์ ์์
์ด ์งํ๋ฉ๋๋ค!
});
โฐ ๋ชจ๋ node ์น ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ์น ์๋ฒ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ผ ํ๋๋ฐ, ์ด ๋ createServer๋ฅผ ์ด์ฉํ๋ค.
โฐ ์ด ์๋ฒ๋ก ์ค๋ HTTP ์์ฒญ๋ง๋ค createServer์ ์ ๋ฌ๋ ํจ์๊ฐ ํ ๋ฒ์ฉ ํธ์ถ๋๋ค.
โฐ createServer๊ฐ ๋ฐํํ Server ๊ฐ์ฒด๋ EventEmitter์ด๋ค (Server ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ๋ ์ถ์ฝ ๋ฌธ๋ฒ ์ฌ์ฉ)
โ EventEmitter ๋?
โฐ ํน์ ์ด๋ฒคํธ์ ๋ฆฌ์ค๋ ํจ์๋ฅผ ๋ฌ์์, ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ์ด๋ฅผ ์บ์นํ ์ ์๋๋ก ๋ง๋ค์ด์ง api
โฐ HTTP ์์ฒญ์ด ์๋ฒ์ ์ค๋ฉด node๊ฐ ํธ๋์ญ์ ์ ๋ค๋ฃจ๋ ค๊ณ request์ response ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ฉฐ ์์ฒญ ํธ๋ค๋ฌ ํจ์๋ฅผ ํธ์ถํ๋ค.
2๏ธโฃ ์์ฒญ ๋ฐ๋
โฐ ํธ๋ค๋ฌ์ ์ ๋ฌ๋ request ๊ฐ์ฒด๋ ReadableStream ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๋ค.
โฐ ์ด ์คํธ๋ฆผ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋ฑ๋กํ๊ฑฐ๋ ๋ค๋ฅธ ์คํธ๋ฆผ์ ์ฐ๊ฒฐํ ์ ์๋ค.
โฐ ์คํธ๋ฆผ์ 'data' ์ 'end' ์ด๋ฒคํธ์ ์ด๋ฒคํธ ๋ฆฌ์คํฐ๋ฅผ ๋ฑ๋กํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์๋ค.
โ stream ์ด๋?
โฐ ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํฐ๋ก ์์ ํ๊ธฐ ์ํ ์ถ์์ ์ธ ์ธํฐํ์ด์ค
โฐ ํต์ผ๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ๊ฐ์์ ๊ฐ๋
โฐ stream์ ์ฝ์ ์ ์๊ฑฐ๋, ์ธ ์ ์๊ฑฐ๋, ๋ ๋ค ๊ฐ๋ฅํ ์ ์๋ค.
โฐ ๋ชจ๋ stream๋ค์ EventEmitter์ ์ธ์คํด์ค์ด๋ฏ๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์์ฑํ ์ ์๋ค.
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// ์ฌ๊ธฐ์ `body`์ ์ ์ฒด ์์ฒญ ๋ฐ๋๊ฐ ๋ฌธ์์ด๋ก ๋ด๊ฒจ์์ต๋๋ค.
});
โฐ ๊ฐ 'data' ์ด๋ฒคํธ์์ ๋ฐ์์ํจ ์ฒญํฌ๋ Buffet์ด๋ค.
โฐ ์ด ์ฒญํฌ๊ฐ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ผ๋ ๊ฒ์ ์๊ณ ์๋ค๋ฉด, ์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์ด์ ์์งํ ๋ค์ 'end' ์ด๋ฒคํธ์์ ์ด์ด ๋ถ์ธ ๋ค์ ๋ฌธ์์ด๋ก ๋ง๋๋ ๊ฒ์ด ๊ฐ์ฅ ์ข๋ค.
3๏ธโฃ ์ค๋ฅ์ ๋ํ ๊ฐ๋จํ ์ค๋ช
โฐ request ์คํธ๋ฆผ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์คํธ๋ฆผ์์ 'error' ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด์ ์ค๋ฅ๋ฅผ ์ ๋ฌํ๋.
โฐ ์ด๋ฒคํธ์ ๋ฆฌ์ค๋๊ฐ ๋ฑ๋ก๋์ด ์์ง ์๋ค๋ฉด Node.js ํ๋ก๊ทธ๋จ์ ์ข ๋ฃ์ํฌ ์๋ ์๋ ์ค๋ฅ๋ฅผ ๋์ง ๊ฒ์ด๋ค.
โฐ ๊ทธ๋ฌ๋ฏ๋ก ๋จ์ํ ์ค๋ฅ๋ฅผ ๋ก๊น ๋ง ํ๋๋ผ๋ ์์ฒญ ์คํธ๋ฆผ์ 'error' ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค.
request.on('error', (err) => {
// ์ฌ๊ธฐ์ `stderr`์ ์ค๋ฅ ๋ฉ์์ง์ ์คํ ํธ๋ ์ด์ค๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
console.error(err.stack);
});
4๏ธโฃ ๋ช ์์ ์ธ ํค๋ ๋ฐ์ดํฐ ์ ์ก
โฐ ํค๋๋ฅผ ์์ฑํ๋ writeHead ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ค.
โฐ ์ด ๋ฉ์ค๋๋ ์คํธ๋ฆผ์ ์ํ ์ฝ๋์ ํค๋๋ฅผ ์์ฑํ๋ค.
response.writeHead(200, {
'Content-Type': 'application/json',
'X-Powered-By': 'bacon'
});
5๏ธโฃ ์๋ต ๋ฐ๋ ์ ์ก
โฐ ์คํธ๋ฆผ์ end ํจ์์ ์คํธ๋ฆผ์ ๋ณด๋ผ ๋ฐ์ดํฐ์ ๋ง์ง๋ง ๋นํธ๋ฅผ ์ ํ์ ์ผ๋ก ์ ๋ฌ ํ ์ ์๋ค.
// 1
response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
// 2
response.end('<html><body><h1>Hello, World!</h1></body></html>');
๐ฃ basic-server.js
const http = require('http');
const PORT = 4999;
const ip = 'localhost';
const server = http.createServer((request, response) => {
if(request.method === 'OPTIONS'){
response.writeHead(200, defaultCorsHeader);
response.end();
}
else if(request.method === 'POST'){
if(request.url === '/upper'){
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString().toUpperCase();
// ์ฌ๊ธฐ์ `body`์ ์ ์ฒด ์์ฒญ ๋ฐ๋๊ฐ ๋ฌธ์์ด๋ก ๋ด๊ฒจ์์ต๋๋ค.
response.end(body);
});
console.log(body);
}
else if(request.url === '/lower'){
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString().toLowerCase();
response.end(body);
});
}
}
else{
request.on('error', (err) => {
// ์ฌ๊ธฐ์ `stderr`์ ์ค๋ฅ ๋ฉ์์ง์ ์คํ ํธ๋ ์ด์ค๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
console.error(err.stack);
});
}
// console.log(
// `http request method is ${request.method}, url is ${request.url}`
// );
response.writeHead(200, defaultCorsHeader);
// response.end('hello mini-server sprints');
});
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
};
๐ฃ ์์ฐ ํ๋ฉด

๐ ์ฐธ๊ณ
HTTP ํธ๋์ญ์ ํด๋ถ https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/