์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[๋…ธ๋“œ 2] 4์žฅ. http ๋ชจ๋“ˆ๋กœ ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

23-24/Node.js 2

by _๋„๋‹ด 2023. 11. 3. 10:00

๋ณธ๋ฌธ

728x90

๐ŸŒŸ4์žฅ ํ‚ค์›Œ๋“œ๐ŸŒŸ

http ๋ชจ๋“ˆ

header, body

REST

์ฟ ํ‚ค, ์„ธ์…˜

http2

ํด๋Ÿฌ์Šคํ„ฐ

 

์‹ค์ œ๋กœ ๋Œ์•„๊ฐ€๋Š” ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค๊ณ , ์„œ๋ฒ„ ๋™์ž‘์— ํ•„์š”ํ•œ ์ฟ ํ‚ค์™€ ์„ธ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์‚ดํŽด๋ณด๊ณ  ์š”์ฒญ ์ฃผ์†Œ ๋ผ์šฐํŒ… ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

4.1 ์š”์ฒญ๊ณผ ์‘๋‹ต ์ดํ•ดํ•˜๊ธฐ

const http = require('http');

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(8080, () => { // ์„œ๋ฒ„ ์—ฐ๊ฒฐ
    console.log('8080๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });
  • http ๋ชจ๋“ˆ ์‚ฌ์šฉ
    • createServer ๋ฉ”์„œ๋“œ
      • ์ธ์ˆ˜๋กœ ์š”์ฒญ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—๋Š” ์‘๋‹ต์„ ๋„ฃ์Šต๋‹ˆ๋‹ค. (์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์‹คํ–‰)
    • listen ๋ฉ”์„œ๋“œ
      • ๊ณต๊ฐœํ•  ํฌํŠธ ๋ฒˆํ˜ธ๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค. 
      • ํฌํŠธ ์—ฐ๊ฒฐ ์™„๋ฃŒ ํ›„ ์‹คํ–‰๋  ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค.
    • res ๊ฐ์ฒด
      • writeHead : ์‘๋‹ต์— ๋Œ€ํ•œ ์ •๋ณด ๊ธฐ๋กํ•˜๋Š” ๋ฉ”์„œ๋“œ
        โœ” 200, HTML, utf-8 ⇒ ํ—ค๋”(header)์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.
      • write : ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ
        โœ” ๋ฌธ์ž์—ด, ๋ฒ„ํผ ⇒ ๋ณธ๋ฌธ(body)์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.
      • end : ์‘๋‹ต ์ข…๋ฃŒ ๋ฉ”์„œ๋“œ
        โœ” ์ธ์ˆ˜๊ฐ€ ์žˆ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ์ฝ˜์†”์ฐฝ
http://localhost:8080 &nbsp;๋˜๋Š” http://127.0.0.1:8080์— ์ ‘์†

๐Ÿ“Œ localhost

localhost๋Š” ํ˜„์žฌ ์ปดํ“จํ„ฐ์˜ ๋‚ด๋ถ€ ์ฃผ์†Œ์ด๋ฉฐ, ์ž์‹ ์˜ ์ปดํ“จํ„ฐ์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 127.0.0.1์„ ์ฃผ์†Œ๋กœ ์‚ฌ์šฉํ•ด๋„ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ํฌํŠธ

์„œ๋ฒ„ ๋‚ด์—์„œ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฒˆํ˜ธ์ด๋ฉฐ, 80๋ฒˆ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฃผ์†Œ์—์„œ ํฌํŠธ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๐Ÿ“ HTML ํŒŒ์ผ fs ๋ชจ๋“ˆ๋กœ ์ฝ์–ด์„œ ์ „์†ก

const http = require('http');
const fs = require('fs').promises;

http.createServer(async (req, res) => {  // ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด
  try {
    const data = await fs.readFile('./server2.html');  // fs ๋ชจ๋“ˆ๋กœ HTML ํŒŒ์ผ์„ ์ฝ๋Š”๋‹ค
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end(data);
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8081, () => {
    console.log('8081๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

 

4.2 REST์™€ ๋ผ์šฐํŒ… ์‚ฌ์šฉํ•˜๊ธฐ

๐Ÿ“Œ REST(REpresentational State Transfer)

  • ์„œ๋ฒ„์˜ ์ž์›์„ ์ •์˜ํ•˜๊ณ  ์ž์›์— ๋Œ€ํ•œ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•ฉ๋‹ˆ๋‹ค.
  • HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ ๋™์ž‘
GET ์„œ๋ฒ„ ์ž์›์„ ๊ฐ€์ ธ์˜ค๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
๋ณธ๋ฌธ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์ง€ ์•Š์Œ
๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ๋ณด๋‚ผ ๋•Œ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ์‚ฌ์šฉ
POST ์„œ๋ฒ„์— ์ž์›์„ ์ƒˆ๋กœ ๋“ฑ๋กํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
์š”์ฒญ์˜ ๋ณธ๋ฌธ(body)์— ์ƒˆ๋กœ ๋“ฑ๋กํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ๋ณด๋ƒ„
PUT ์„œ๋ฒ„์˜ ์ž์›์„ ์š”์ฒญ์— ๋“ค์–ด ์žˆ๋Š” ์ž์›์œผ๋กœ ์น˜ํ™˜
์š”์ฒญ์˜ ๋ณธ๋ฌธ(body)์— ์น˜ํ™˜ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ๋ณด๋ƒ„
PATCH ์„œ๋ฒ„ ์ž์›์˜ ์ผ๋ถ€๋งŒ ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
์š”์ฒญ์˜ ๋ณธ๋ฌธ์— ์ผ๋ถ€ ์ˆ˜์ •ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ๋ณด๋ƒ„
DELETE ์„œ๋ฒ„์˜ ์ž์›์„ ์‚ญ์ œํ•  ๋•Œ ์‚ฌ์šฉ
OPTIONS ์š”์ฒญ์„ ํ•˜๊ธฐ ์ „์— ํ†ต์‹  ์˜ต์…˜์„ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
  • ์ฃผ์†Œ ํ•˜๋‚˜๋Š” ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๐Ÿ“/user ์ฃผ์†Œ๋กœ GET ๋ฉ”์„œ๋“œ(์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ), POST ๋ฉ”์„œ๋“œ(์‚ฌ์šฉ์ž ๋“ฑ๋กํ•˜๊ธฐ)
  • ์ฃผ์†Œ์™€ ๋ฉ”์„œ๋“œ๋งŒ ๋ณด๊ณ  ์š”์ฒญ์˜ ๋‚ด์šฉ์„ ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • GET ๋ฉ”์„œ๋“œ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์บ์‹ฑ(๊ธฐ์–ต)ํ•  ์ˆ˜๋„ ์žˆ์–ด GET ์š”์ฒญ ๋•Œ ์บ์‹œ์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ๋“  ์ƒ๊ด€์—†์ด ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์„œ๋ฒ„์™€ ์†Œํ†ตํ•ฉ๋‹ˆ๋‹ค. (iOS, ์•ˆ๋“œ๋กœ์ด๋“œ, ์›น ๋“ฑ)

REST API

const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const users = {}; // ๋ฐ์ดํ„ฐ ์ €์žฅ์šฉ

http.createServer(async (req, res) => {   // HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ ๊ตฌ๋ถ„
  try {
    if (req.method === 'GET') {  // ๋‹ค์‹œ ๊ตฌ๋ถ„
      if (req.url === '/') {
        const data = await fs.readFile(path.join(__dirname, 'restFront.html'));
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/about') {
        const data = await fs.readFile(path.join(__dirname, 'about.html'));
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/users') {
        res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
        return res.end(JSON.stringify(users));
      }
      // /๋„ /about๋„ /users๋„ ์•„๋‹ˆ๋ฉด
      try {
        const data = await fs.readFile(path.join(__dirname, req.url));
        return res.end(data);
      } catch (err) {
        // ์ฃผ์†Œ์— ํ•ด๋‹นํ•˜๋Š” ๋ผ์šฐํŠธ๋ฅผ ๋ชป ์ฐพ์•˜๋‹ค๋Š” 404 Not Found error ๋ฐœ์ƒ
      }
    } else if (req.method === 'POST') {
      if (req.url === '/user') {
        let body = '';
        // ์š”์ฒญ์˜ body๋ฅผ stream ํ˜•์‹์œผ๋กœ ๋ฐ›์Œ
        req.on('data', (data) => {
          body += data;
        });
        // ์š”์ฒญ์˜ body๋ฅผ ๋‹ค ๋ฐ›์€ ํ›„ ์‹คํ–‰๋จ
        return req.on('end', () => {
          console.log('POST ๋ณธ๋ฌธ(Body):', body);
          const { name } = JSON.parse(body);
          const id = Date.now();
          users[id] = name;
          res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
          res.end('๋“ฑ๋ก ์„ฑ๊ณต');
        });
      }
    } else if (req.method === 'PUT') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        let body = '';
        req.on('data', (data) => {
          body += data;
        });
        return req.on('end', () => {
          console.log('PUT ๋ณธ๋ฌธ(Body):', body);
          users[key] = JSON.parse(body).name;
          res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
          return res.end(JSON.stringify(users));
        });
      }
    } else if (req.method === 'DELETE') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        delete users[key];
        res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
        return res.end(JSON.stringify(users));
      }
    }
    res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค');
  });

POST๋กœ ์ด๋ฆ„ ๋“ฑ๋ก, PUT์œผ๋กœ ์ˆ˜์ •

 

4.3 ์ฟ ํ‚ค์™€ ์„ธ์…˜ ์ดํ•ดํ•˜๊ธฐ

๐Ÿช ์ฟ ํ‚ค

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•  ๋•Œ ๋ณด๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ์œ ํšจ๊ธฐ๊ฐ„์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ‚ค-๊ฐ’์˜ ์Œ์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•ด ๋’€๋‹ค๊ฐ€ ๋‹ค์Œ์— ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค ์ฟ ํ‚ค๋ฅผ ๋™๋ด‰ํ•ด์„œ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ์š”์ฒญ์˜ ํ—ค๋”(Cookie)์— ๋‹ด๊ฒจ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €๋Š” ์‘๋‹ต์˜ ํ—ค๋”(Set-Cookie)์— ๋”ฐ๋ผ ์ฟ ํ‚ค๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
const http = require('http');

http.createServer((req, res) => {     // req ๊ฐ์ฒด์— ๋‹ด๊ฒจ ์žˆ๋Š” ์ฟ ํ‚ค๋ฅผ ๊ฐ€์ ธ์˜ด 
  console.log(req.url, req.headers.cookie);  // ์ฟ ํ‚ค๊ฐ€ ๋“ค์–ด์žˆ๋Š” ์œ„์น˜
  res.writeHead(200, { 'Set-Cookie': 'mycookie=test' });  // ์‘๋‹ต์˜ ํ—ค๋”์— ์ฟ ํ‚ค๋ฅผ ๊ธฐ๋ก
  res.end('Hello Cookie');
})
  .listen(8083, () => {
    console.log('8083๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

  • favicon(ํŒŒ๋น„์ฝ˜) : ์›น ์‚ฌ์ดํŠธ ํƒญ์— ๋ณด์ด๋Š” ์ด๋ฏธ์ง€

๐Ÿ“Œ / undefinded, /favicon.ico mycookie=test ๋‘ ๊ฐœ๊ฐ€ ๊ธฐ๋ก๋œ ์ด์œ 

์ฒซ ๋ฒˆ์งธ ์š”์ฒญ(/)์„ ๋ณด๋‚ด๊ธฐ ์ „์—๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์–ด๋– ํ•œ ์ฟ ํ‚ค ์ •๋ณด๋„ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š์•„ undefined๊ฐ€ ๊ธฐ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ €๋Š” HTML์—์„œ ํŒŒ๋น„์ฝ˜์ด ๋ญ”์ง€ ์œ ์ถ”ํ•  ์ˆ˜ ์—†์œผ๋ฉด ์„œ๋ฒ„์— ํŒŒ๋น„์ฝ˜ ์ •๋ณด์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ์š”์ฒญ(/favicon.ico)์˜ ํ—ค๋”์— ์ฟ ํ‚ค๊ฐ€ ๋“ค์–ด ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฟ ํ‚ค๋กœ ์‚ฌ์šฉ์ž ์‹๋ณ„

const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const parseCookies = (cookie = '') =>   // ์ฟ ํ‚ค ๋ฌธ์ž์—ด์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด ํ˜•์‹์œผ๋กœ ๋ณ€๊ฒฝ
  cookie
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, [k, v]) => {
      acc[k.trim()] = decodeURIComponent(v);
      return acc;
    }, {});

// GET /login ์ฒ˜๋ฆฌ ๋ถ€๋ถ„
http.createServer(async (req, res) => {
  const cookies = parseCookies(req.headers.cookie); // { mycookie: 'test' }
  // ์ฃผ์†Œ๊ฐ€ /login์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ
  if (req.url.startsWith('/login')) {
    const url = new URL(req.url, '<http://localhost:8084>');
    const name = url.searchParams.get('name');  // name์ด๋ผ๋Š” Params๊ฐ€ ์žˆ๋Š”์ง€ ์ฐพ๊ณ  name์— ๋„ฃ๋Š”๋‹ค.
    const expires = new Date();
    // ์ฟ ํ‚ค ์œ ํšจ ์‹œ๊ฐ„์„ ํ˜„์žฌ์‹œ๊ฐ„ + 5๋ถ„์œผ๋กœ ์„ค์ •
    expires.setMinutes(expires.getMinutes() + 5);
    res.writeHead(302, {       // 302 ์‘๋‹ต์ฝ”๋“œ, ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฃผ์†Œ์™€ ์ฟ ํ‚ค๋ฅผ ํ—ค๋”์— ๋„ฃ๋Š”๋‹ค.
      Location: '/',
      'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
    });
    res.end();
  // name์ด๋ผ๋Š” ์ฟ ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ
  } else if (cookies.name) {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(`${cookies.name}๋‹˜ ์•ˆ๋…•ํ•˜์„ธ์š”`);
  } else {  // ์ฟ ํ‚ค๊ฐ€ ์—†๋‹ค.
    try {
      const data = await fs.readFile(path.join(__dirname, 'cookie2.html'));
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
      res.end(data);
    } catch (err) {
      res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
      res.end(err.message);
    }
  }
})
  .listen(8084, () => {
    console.log('8084๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

์ฟ ํ‚ค์—๋Š” ํ•œ๊ธ€๊ณผ ์ค„ ๋ฐ”๊ฟˆ์„ ๋„ฃ์„ ์ˆ˜ ์—†์œผ๋ฉฐ, ํ•œ๊ธ€์€ encodeURIComponent๋กœ ๊ฐ์‹ธ์„œ ๋„ฃ์Šต๋‹ˆ๋‹ค.

๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ์ ‘์†ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ๋จผ์ € ์ฟ ํ‚ค๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ฟ ํ‚ค๊ฐ€ ์—†๋‹ค๋ฉด ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ๋ณด๋‚ด๊ณ , ์žˆ๋‹ค๋ฉด ๋กœ๊ทธ์ธํ•œ ์ƒํƒœ๋กœ ๊ฐ„์ฃผํ•ด ์ธ์‚ฌ๋ง์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฐฉ์‹์€ ์ฟ ํ‚ค๊ฐ€ ๋…ธ์ถœ๋˜์–ด ์žˆ์œผ๋ฉฐ ์กฐ์ž‘๋  ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

session์„ ์‚ฌ์šฉํ•ด ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•ฉ์‹œ๋‹ค.

const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const parseCookies = (cookie = '') =>
  cookie
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, [k, v]) => {
      acc[k.trim()] = decodeURIComponent(v);
      return acc;
    }, {});

const session = {};

http.createServer(async (req, res) => {
  const cookies = parseCookies(req.headers.cookie);
  if (req.url.startsWith('/login')) {
    const url = new URL(req.url, '<http://localhost:8085>');
    const name = url.searchParams.get('name');
    const expires = new Date();
    expires.setMinutes(expires.getMinutes() + 5);
    const uniqueInt = Date.now();
    session[uniqueInt] = {
      name,
      expires,
    };
    res.writeHead(302, {
      Location: '/',
      'Set-Cookie': `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
    });
    res.end();
  // ์„ธ์…˜์ฟ ํ‚ค๊ฐ€ ์กด์žฌํ•˜๊ณ , ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์ด ์ง€๋‚˜์ง€ ์•Š์•˜๋‹ค๋ฉด
  } else if (cookies.session && session[cookies.session].expires > new Date()) {
    res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(`${session[cookies.session].name}๋‹˜ ์•ˆ๋…•ํ•˜์„ธ์š”`);
  } else {
    try {
      const data = await fs.readFile(path.join(__dirname, 'cookie2.html'));
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
      res.end(data);
    } catch (err) {
      res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
      res.end(err.message);
    }
  }
})
  .listen(8085, () => {
    console.log('8085๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });
  • ์ฟ ํ‚ค์— ์ด๋ฆ„์„ ๋‹ด๋Š” ๊ฒƒ ๋Œ€์‹  uniqueInt ์ˆซ์ž ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„์— ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ์™€๋Š” ์„ธ์…˜ ์•„์ด๋””๋กœ ์†Œํ†ตํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„๋Š” ์„ธ์…˜์„ ๋ ˆ๋””์Šค๋‚˜ ๋ฉค์บ์‹œ๋“œ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋„ฃ์Šต๋‹ˆ๋‹ค.

 

4.4 https์™€ http2

๐Ÿ“Œ https ๋ชจ๋“ˆ
์›น ์„œ๋ฒ„์— SSL ์•”ํ˜ธํ™”๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

์ธ์ฆํ•ด ์ฃผ๋Š” ๊ธฐ๊ด€์—์„œ ๊ตฌ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

const https = require('https');
const fs = require('fs');

https.createServer({
  cert: fs.readFileSync('๋„๋ฉ”์ธ ์ธ์ฆ์„œ ๊ฒฝ๋กœ'),
  key: fs.readFileSync('๋„๋ฉ”์ธ ๋น„๋ฐ€ํ‚ค ๊ฒฝ๋กœ'),
  ca: [
    fs.readFileSync('์ƒ์œ„ ์ธ์ฆ์„œ ๊ฒฝ๋กœ'),
    fs.readFileSync('์ƒ์œ„ ์ธ์ฆ์„œ ๊ฒฝ๋กœ'),
  ],
}, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(443, () => {
    console.log('443๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });
  • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜ : ์ธ์ฆ์„œ์— ๊ด€๋ จ๋œ ์˜ต์…˜ ๊ฐ์ฒด๋“ค
    ์ธ์ฆ์„œ๋ฅผ ๊ตฌ์ž…ํ•˜์—ฌ ๋ฐ›์€ ํŒŒ์ผ๋“ค์„ fs.readFileSync ๋ฉ”์„œ๋“œ๋กœ ์ฝ์–ด์„œ cert, key, ca ์˜ต์…˜์— ์•Œ๋งž๊ฒŒ ๋„ฃ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ http2 ๋ชจ๋“ˆ

๋…ธ๋“œ์˜ http2 ๋ชจ๋“ˆ์€ SSL ์•”ํ˜ธํ™”์™€ ๋”๋ถˆ์–ด ์ตœ์‹  HTTP ํ”„๋กœํ† ์ฝœ์ธ http/2๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

http/2๋Š” ์š”์ฒญ ๋ฐ ์‘๋‹ต ๋ฐฉ์‹์ด ๊ธฐ์กด http/1.1๋ณด๋‹ค ๊ฐœ์„ ๋˜์–ด ํ›จ์”ฌ ํšจ์œจ์ ์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

 

4.5 cluster

๐Ÿ“Œ cluster (ํด๋Ÿฌ์Šคํ„ฐ)

โœ”  cluster ๋ชจ๋“ˆ์€ ์‹ฑ๊ธ€ ํ”„๋กœ์„ธ์Šค๋กœ ๋™์ž‘ํ•˜๋Š” ๋…ธ๋“œ๊ฐ€ CPU ์ฝ”์–ด๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

โœ” ์ฝ”์–ด ํ•˜๋‚˜๋‹น ํ”„๋กœ์„ธ์Šค ํ•˜๋‚˜๊ฐ€ ๋Œ์•„๊ฐ€๊ฒŒ ํ•˜์—ฌ, ์ฝ”์–ด๋ฅผ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•  ๋•Œ์— ๋น„ํ•ด ์„ฑ๋Šฅ์ด ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค.

โœ” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜์ง€ ๋ชปํ•˜๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`๋งˆ์Šคํ„ฐ ํ”„๋กœ์„ธ์Šค ์•„์ด๋””: ${process.pid}`);
  // CPU ๊ฐœ์ˆ˜๋งŒํผ ์›Œ์ปค๋ฅผ ์ƒ์‚ฐ
  for (let i = 0; i < numCPUs; i += 1) {
    cluster.fork();
  }
  // ์›Œ์ปค๊ฐ€ ์ข…๋ฃŒ๋˜์—ˆ์„ ๋•Œ
  cluster.on('exit', (worker, code, signal) => {
    console.log(`${worker.process.pid}๋ฒˆ ์›Œ์ปค๊ฐ€ ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.`);
    console.log('code', code, 'signal', signal);
    cluster.fork();
  });
} else {
  // ์›Œ์ปค๋“ค์ด ํฌํŠธ์—์„œ ๋Œ€๊ธฐ
  http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Cluster!</p>');
    setTimeout(() => { // ์›Œ์ปค ์กด์žฌ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด 1์ดˆ๋งˆ๋‹ค ๊ฐ•์ œ ์ข…๋ฃŒ
      process.exit(1);
    }, 1000);
  }).listen(8086);

  console.log(`${process.pid}๋ฒˆ ์›Œ์ปค ์‹คํ–‰`);
}

ํด๋Ÿฌ์Šคํ„ฐ์—๋Š” ๋งˆ์Šคํ„ฐ ํ”„๋กœ์„ธ์Šค์™€ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์Šคํ„ฐ ํ”„๋กœ์„ธ์Šค๋Š” CPU ๊ฐœ์ˆ˜๋งŒํผ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค๋ฅผ ๋งŒ๋“ค๊ณ , 8086๋ฒˆ ํฌํŠธ์—์„œ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋งŒ๋“ค์–ด์ง„ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค์— ์š”์ฒญ์„ ๋ถ„๋ฐฐํ•ฉ๋‹ˆ๋‹ค.

 

 


๋นˆ์นธ ์ฑ„์šฐ๊ธฐ ๋ฌธ์ œ (๋นˆ์นธ์„ ๋“œ๋ž˜๊ทธํ•ด์„œ ์ •๋‹ต์„ ๋งžํ˜€ ๋ณด์„ธ์š”!)

1. http ๋ชจ๋“ˆ์˜ (createServer) ๋ฉ”์„œ๋“œ๋Š” ์ธ์ˆ˜๋กœ ์š”์ฒญ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

2. ์‘๋‹ต์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ๊ธฐ๋ก๋˜๋Š” ๋ถ€๋ถ„์„ (ํ—ค๋”)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. (ํ—ค๋”)์—๋Š” 200, ์ฝ˜ํ…์ธ  ํ˜•์‹, charset ๋“ฑ์ด ํฌํ•จ๋œ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ธฐ๋ก๋˜๋Š” ๋ถ€๋ถ„์€ (๋ณธ๋ฌธ(body))์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด, ๋ฒ„ํผ ๋“ฑ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. (REST)๋Š” ์„œ๋ฒ„์˜ ์ž์›์„ ์ •์˜ํ•˜๊ณ  ์ž์›์— ๋Œ€ํ•œ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.

5. ์ฃผ์†Œ ํ•˜๋‚˜๋Š” ์š”์ฒญ ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜๋งŒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (O/X) 
    ์ •๋‹ต : O

6. (์ฟ ํ‚ค)๋ž€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•  ๋•Œ ๋ณด๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

7. (cluster) ๋ชจ๋“ˆ์€ ์‹ฑ๊ธ€ ํ”„๋กœ์„ธ์Šค๋กœ ๋™์ž‘ํ•˜๋Š” ๋…ธ๋“œ๊ฐ€ CPU ์ฝ”์–ด๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค.

 

 

 

์ฝ”๋“œ ๋ฌธ์ œ

1. ๋นˆ์นธ์— ๋“ค์–ด๊ฐˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

const http = require('http');

http.createServer((req, res) => {
  res.{1๋ฒˆ ๋นˆ์นธ}(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.{2๋ฒˆ ๋นˆ์นธ}('<h1>Hello Node!</h1>');
  res.{3๋ฒˆ ๋นˆ์นธ}('<p>Hello Server!</p>');
})
  .listen(8080, () => { // ์„œ๋ฒ„ ์—ฐ๊ฒฐ
    console.log('8080๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

 ์ •๋‹ต : writeHead, write, end

 

2. ๋นˆ์นธ์— ๋“ค์–ด๊ฐˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์„ธ์š”.

const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const parseCookies = (cookie = '') =>
  cookie
    .split(';')
    .map(v => v.split('='))
    .reduce((acc, [k, v]) => {
      acc[k.trim()] = decodeURIComponent(v);
      return acc;
    }, {});

const session = {};

http.createServer(async (req, res) => {
  const cookies = parseCookies(req.{1๋ฒˆ ๋นˆ์นธ});
  if (req.url.startsWith('/login')) {
    const url = new URL(req.url, '<http://localhost:8085>');
    const name = url.searchParams.get('name');
    const expires = new Date();
    expires.setMinutes(expires.getMinutes() + 5);
    const uniqueInt = Date.now();
    session[uniqueInt] = {
      name,
      expires,
    };
    res.writeHead(302, {
      Location: '/',
      'Set-Cookie': `session=${{2๋ฒˆ ๋นˆ์นธ}}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
    });
    res.end();
  ... ์ƒ๋žต

์ •๋‹ต : header.cookie, uniqueInt


Node.js #2 

Editor : ํŒŒ์˜ค๋ฆฌ

728x90

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ