CodeStates/Training

S2) Unit 10. [์‹ค์Šต] Mini Node Server

Jieunny 2023. 2. 6. 17:00

๐Ÿ“ฃ  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/