๐Ÿ”Žhttp ๋ชจ๋“ˆ๋กœ ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

์„œ๊ฐ€ํฌยท2021๋…„ 10์›” 31์ผ
0

Node.js

๋ชฉ๋ก ๋ณด๊ธฐ
3/15
post-thumbnail

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

1. ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ

์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ๊ด€๊ณ„

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„๋กœ ์š”์ฒญ(request)์„ ๋ณด๋ƒ„
  • ์„œ๋ฒ„๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌ
  • ์ฒ˜๋ฆฌ ํ›„ ํด๋ผ์ด์–ธํŠธ๋กœ ์‘๋‹ต(response)์„ ๋ณด๋ƒ„

2. ๋…ธ๋“œ๋กœ http ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

http ์š”์ฒญ์— ์‘๋‹ตํ•˜๋Š” ๋…ธ๋“œ ์„œ๋ฒ„

  • createServer๋กœ ์š”์ฒญ ์ด๋ฒคํŠธ์— ๋Œ€๊ธฐ
  • req ๊ฐ์ฒด๋Š” ์š”์ฒญ์— ๊ด€ํ•œ ์ •๋ณด๊ฐ€, res ๊ฐ์ฒด๋Š” ์‘๋‹ต์— ๊ด€ํ•œ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ ์žˆ์Œ

๐Ÿ”ปcreateServer.js

const http = require('http');

http.createServer((req, res) => {
  // ์—ฌ๊ธฐ์— ์–ด๋–ป๊ฒŒ ์‘๋‹ตํ•  ์ง€ ์ ์–ด์ค๋‹ˆ๋‹ค.
});
  1. 8080 ํฌํŠธ์— ์—ฐ๊ฒฐํ•˜๊ธฐ
    res ๋ฉ”์„œ๋“œ๋กœ ์‘๋‹ต ๋ณด๋ƒ„
  • write๋กœ ์‘๋‹ต ๋‚ด์šฉ์„ ์ ๊ณ 
  • end๋กœ ์‘๋‹ต ๋งˆ๋ฌด๋ฆฌ(๋‚ด์šฉ์„ ๋„ฃ์–ด๋„ ๋จ)

listen(ํฌํŠธ) ๋ฉ”์„œ๋“œ๋กœ ํŠน์ • ํฌํŠธ์— ์—ฐ๊ฒฐ
๐Ÿ”ปserver1.js

const http = require('http');

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) //html์„ string์œผ๋กœ ์ธ์‹ํ•˜๋Š” ๊ฒฝ์šฐ์— ์ฝ”๋“œ ์ถ”๊ฐ€, ํ•œ๊ธ€ ์ธ์‹ํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ ์ถ”๊ฐ€;
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(8080, () => { // ์„œ๋ฒ„ ์—ฐ๊ฒฐ
    console.log('8080๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

4. 8080 ํฌํŠธ๋กœ ์ ‘์†ํ•˜๊ธฐ

์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด 8080 ํฌํŠธ์— ์—ฐ๊ฒฐ๋จ


localhost:8080 ๋˜๋Š” http://127.0.0.1:8080์— ์ ‘์†

โœ”๊ฒฐ๊ณผ


5. localhost์™€ ํฌํŠธ

localhost๋Š” ์ปดํ“จํ„ฐ ๋‚ด๋ถ€ ์ฃผ์†Œ

  • ์™ธ๋ถ€์—์„œ๋Š” ์ ‘๊ทผ ๋ถˆ๊ฐ€๋Šฅ

ํฌํŠธ๋Š” ์„œ๋ฒ„ ๋‚ด์—์„œ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฒˆํ˜ธ

  • ๊ธฐ๋ณธ์ ์œผ๋กœ http ์„œ๋ฒ„๋Š” 80๋ฒˆ ํฌํŠธ ์‚ฌ์šฉ(์ƒ๋žต๊ฐ€๋Šฅ, https๋Š” 443)
  • ์˜ˆ) www.gilbut.com:80 -> www.github.com
  • ๋‹ค๋ฅธ ํฌํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ๋‹ค๋ฅธ ์„œ๋ฒ„ ๋™์‹œ์— ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ

6. ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋ถ™์ด๊ธฐ

listening๊ณผ error ์ด๋ฒคํŠธ๋ฅผ ๋ถ™์ผ ์ˆ˜ ์žˆ์Œ.

๐Ÿ”ปserver1-1.js

const http = require('http');

const server = 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>');
});
server.listen(8080);

server.on('listening', () => {
  console.log('8080๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
});
server.on('error', (error) => {
  console.error(error);
});

7. ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ

createServer๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฉด ๋จ.

  • ๋‹จ, ๋‘ ์„œ๋ฒ„์˜ ํฌํŠธ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ง€์ •ํ•ด์•ผ ํ•จ.
  • ๊ฐ™๊ฒŒ ์ง€์ •ํ•˜๋ฉด EADDRINUSE ์—๋Ÿฌ ๋ฐœ์ƒ

๐Ÿ”ปserver1-2.js

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((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('8081๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

8. html ์ฝ์–ด์„œ ์ „์†กํ•˜๊ธฐ

write์™€ end์— ๋ฌธ์ž์—ด์„ ๋„ฃ๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ 

  • fs ๋ชจ๋“ˆ๋กœ html์„ ์ฝ์–ด์„œ ์ „์†กํ•˜์ž
  • write๊ฐ€ ๋ฒ„ํผ๋„ ์ „์†ก ๊ฐ€๋Šฅ

๐Ÿ”ปserver2.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
  //ํ•œ๊ธ€ ๋‚˜์˜ค๊ฒŒ ํ•˜๋ ค๋ฉด utf-8์„ ์ ์–ด์คŒ.
    <title>Node.js ์›น ์„œ๋ฒ„</title>
</head>
<body>
    <h1>Node.js ์›น ์„œ๋ฒ„</h1>
    <p>๋งŒ๋“ค ์ค€๋น„๋˜์…จ๋‚˜์š”?</p>
</body>
</html>

๐Ÿ”ปserver2.js

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

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

โœ”๊ฒฐ๊ณผ

REST API์™€ ๋ผ์šฐํŒ…

1. REST API

์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ๋Š” ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด ์š”์ฒญ์˜ ๋‚ด์šฉ์„ ํ‘œํ˜„
/index.html์ด๋ฉด index.html์„ ๋ณด๋‚ด๋‹ฌ๋ผ๋Š” ๋œป
ํ•ญ์ƒ html์„ ์š”๊ตฌํ•  ํ•„์š”๋Š” ์—†์Œ
์„œ๋ฒ„๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์ฃผ์†Œ๊ฐ€ ์ข‹์Œ
REST API(Representational State Transfer)
์„œ๋ฒ„์˜ ์ž์›์„ ์ •์˜ํ•˜๊ณ  ์ž์›์— ๋Œ€ํ•œ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•
/user์ด๋ฉด ์‚ฌ์šฉ์ž ์ •๋ณด์— ๊ด€ํ•œ ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ
/post๋ฉด ๊ฒŒ์‹œ๊ธ€์— ๊ด€๋ จ๋œ ์ž์›์„ ์š”์ฒญํ•˜๋Š” ๊ฒƒ
HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ
GET: ์„œ๋ฒ„ ์ž์›์„ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•  ๋•Œ ์‚ฌ์šฉ
POST: ์„œ๋ฒ„์— ์ž์›์„ ์ƒˆ๋กœ ๋“ฑ๋กํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ(๋˜๋Š” ๋ญ˜ ์จ์•ผํ•  ์ง€ ์• ๋งคํ•  ๋•Œ)
PUT: ์„œ๋ฒ„์˜ ์ž์›์„ ์š”์ฒญ์— ๋“ค์–ด์žˆ๋Š” ์ž์›์œผ๋กœ ์น˜ํ™˜ํ•˜๊ณ ์žํ•  ๋•Œ ์‚ฌ์šฉ
PATCH: ์„œ๋ฒ„ ์ž์›์˜ ์ผ๋ถ€๋งŒ ์ˆ˜์ •ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉ
DELETE: ์„œ๋ฒ„์˜ ์ž์›์„ ์‚ญ์ œํ•˜๊ณ ์žํ•  ๋•Œ ์‚ฌ์šฉ

2. HTTP ํ”„๋กœํ† ์ฝœ

ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ๋“  ์„œ๋ฒ„์™€ HTTP ํ”„๋กœํ† ์ฝœ๋กœ ์†Œํ†ต ๊ฐ€๋Šฅ

  • iOS, ์•ˆ๋“œ๋กœ์ด๋“œ, ์›น์ด ๋ชจ๋‘ ๊ฐ™์€ ์ฃผ์†Œ๋กœ ์š”์ฒญ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ
  • ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ๋ถ„๋ฆฌ

    RESTful
  • REST API๋ฅผ ์‚ฌ์šฉํ•œ ์ฃผ์†Œ ์ฒด๊ณ„๋ฅผ ์ด์šฉํ•˜๋Š” ์„œ๋ฒ„
  • GET /user๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์กฐํšŒํ•˜๋Š” ์š”์ฒญ, POST /user๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋“ฑ๋กํ•˜๋Š” ์š”์ฒญ

3. REST ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

GitHub ์ €์žฅ์†Œ(https://github.com/zerocho/nodejsbook) ch4 ์†Œ์Šค ์ฐธ์กฐ
๐Ÿ”ปresFront.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8" />
  <title>RESTful SERVER</title>
  <link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
<div>
  <form id="form">
    <input type="text" id="username">
    <button type="submit">๋“ฑ๋ก</button>
  </form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./restFront.js"></script>
</body>
</html>

๐Ÿ”ปresFront.css

a { color: blue; text-decoration: none; }

๐Ÿ”ปabout.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>RESTful SERVER</title>
  <link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
<div>
  <h2>์†Œ๊ฐœ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.</h2>
  <p>์‚ฌ์šฉ์ž ์ด๋ฆ„์„ ๋“ฑ๋กํ•˜์„ธ์š”!</p>
</div>
</body>
</html>

๐Ÿ”ปresFront.js

async function getUser() { // ๋กœ๋”ฉ ์‹œ ์‚ฌ์šฉ์ž ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
  try {
    const res = await axios.get('/users');
    const users = res.data;
    const list = document.getElementById('list');
    list.innerHTML = '';
    // ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๋ฐ˜๋ณต์ ์œผ๋กœ ํ™”๋ฉด ํ‘œ์‹œ ๋ฐ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
    Object.keys(users).map(function (key) {
      const userDiv = document.createElement('div');
      const span = document.createElement('span');
      span.textContent = users[key];
      const edit = document.createElement('button');
      edit.textContent = '์ˆ˜์ •';
      edit.addEventListener('click', async () => { // ์ˆ˜์ • ๋ฒ„ํŠผ ํด๋ฆญ
        const name = prompt('๋ฐ”๊ฟ€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”');
        if (!name) {
          return alert('์ด๋ฆ„์„ ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅํ•˜์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค');
        }
        try {
          await axios.put('/user/' + key, { name });
          getUser();
        } catch (err) {
          console.error(err);
        }
      });
      const remove = document.createElement('button');
      remove.textContent = '์‚ญ์ œ';
      remove.addEventListener('click', async () => { // ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ
        try {
          await axios.delete('/user/' + key);
          getUser();
        } catch (err) {
          console.error(err);
        }
      });
      userDiv.appendChild(span);
      userDiv.appendChild(edit);
      userDiv.appendChild(remove);
      list.appendChild(userDiv);
      console.log(res.data);
    });
  } catch (err) {
    console.error(err);
  }
}

window.onload = getUser; // ํ™”๋ฉด ๋กœ๋”ฉ ์‹œ getUser ํ˜ธ์ถœ
// ํผ ์ œ์ถœ(submit) ์‹œ ์‹คํ–‰
document.getElementById('form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const name = e.target.username.value;
  if (!name) {
    return alert('์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”');
  }
  try {
    await axios.post('/user', { name });
    getUser();
  } catch (err) {
    console.error(err);
  }
  e.target.username.value = '';
});

๐Ÿ”ปrestServer.js์— ์ฃผ๋ชฉ

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

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

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET')//์ฃผ์†Œ์ฐฝ์— ์น˜๋Š” ๊ฒƒ-> GET์š”์ฒญ {
      if (req.url === '/') {
        const data = await fs.readFile('./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('./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(`.${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' });
          //201-์ƒ์„ฑ๋จ
          res.end('ok');
        });
      }
    } 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('ok');
        });
      }
    } 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('ok');
      }
    }
    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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค');
  });

GET ๋ฉ”์„œ๋“œ์—์„œ /, /about ์š”์ฒญ ์ฃผ์†Œ๋Š” ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ HTML ํŒŒ์ผ์„ ์ฝ์–ด์„œ ์ „์†ก.
AJAX ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” /users์—์„œ๋Š” users ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก.
JSON ํ˜•์‹์œผ๋กœ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด JSON.stringify๋ฅผ ํ•ด์คŒ.
๊ทธ ์™ธ์˜ GET ์š”์ฒญ์€ CSS๋‚˜ JS ํŒŒ์ผ์„ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ์ฐพ์•„์„œ ๋ณด๋‚ด์ฃผ๊ณ , ์—†๋‹ค๋ฉด 404 NOT FOUND ์—๋Ÿฌ๋ฅผ ์‘๋‹ต.
POST์™€ PUT ๋ฉ”์„œ๋“œ๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฏ€๋กœ ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”. req.on('data', ์ฝœ๋ฐฑ)๊ณผ req.on('end', ์ฝœ๋ฐฑ) ๋ถ€๋ถ„์ธ๋ฐ์š”. 3.6.2์ ˆ์˜ ๋ฒ„ํผ์™€ ์ŠคํŠธ๋ฆผ์—์„œ ๋ฐฐ์› ๋˜ readStream์ž…๋‹ˆ๋‹ค. readStream์œผ๋กœ ์š”์ฒญ๊ณผ ๊ฐ™์ด ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ ๋ณธ๋ฌธ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ.
๋‹จ, ๋ฌธ์ž์—ด์ด๋ฏ€๋กœ JSON์œผ๋กœ ๋งŒ๋“œ๋Š” JSON.parse ๊ณผ์ •์ด ํ•œ ๋ฒˆ ํ•„์š”.
DELETE ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ์ด ์˜ค๋ฉด ์ฃผ์†Œ์— ๋“ค์–ด ์žˆ๋Š” ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ œ๊ฑฐ.
ํ•ด๋‹นํ•˜๋Š” ์ฃผ์†Œ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ 404 NOT FOUND ์—๋Ÿฌ๋ฅผ ์‘๋‹ต.
. REST ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ
localhost:8085์— ์ ‘์†

5. REST ์š”์ฒญ ํ™•์ธํ•˜๊ธฐ

๊ฐœ๋ฐœ์ž๋„๊ตฌ(F12) Network ํƒญ์—์„œ ์š”์ฒญ ๋‚ด์šฉ ์‹ค์‹œ๊ฐ„ ํ™•์ธ ๊ฐ€๋Šฅ

  • Name์€ ์š”์ฒญ ์ฃผ์†Œ, Method๋Š” ์š”์ฒญ ๋ฉ”์„œ๋“œ, Status๋Š” HTTP ์‘๋‹ต ์ฝ”๋“œ
  • Protocol์€ HTTP ํ”„๋กœํ† ์ฝœ, Type์€ ์š”์ฒญ ์ข…๋ฅ˜(xhr์€ AJAX ์š”์ฒญ)

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

1. ์ฟ ํ‚ค์˜ ํ•„์š”์„ฑ

์š”์ฒญ์—๋Š” ํ•œ ๊ฐ€์ง€ ๋‹จ์ ์ด ์žˆ์Œ

  • ๋ˆ„๊ฐ€ ์š”์ฒญ์„ ๋ณด๋ƒˆ๋Š”์ง€ ๋ชจ๋ฆ„(IP ์ฃผ์†Œ์™€ ๋ธŒ๋ผ์šฐ์ € ์ •๋ณด ์ •๋„๋งŒ ์•Ž)
  • ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜๋ฉด ๋จ
  • ์ฟ ํ‚ค์™€ ์„ธ์…˜์ด ํ•„์š”
  • ์ฟ ํ‚ค: ํ‚ค=๊ฐ’์˜ ์Œ
    name=zerocho
  • ๋งค ์š”์ฒญ๋งˆ๋‹ค ์„œ๋ฒ„์— ๋™๋ด‰ํ•ด์„œ ๋ณด๋ƒ„
  • ์„œ๋ฒ„๋Š” ์ฟ ํ‚ค๋ฅผ ์ฝ์–ด ๋ˆ„๊ตฌ์ธ์ง€ ํŒŒ์•…

2. ์ฟ ํ‚ค ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

์ฟ ํ‚ค ๋„ฃ๋Š” ๊ฒƒ์„ ์ง์ ‘ ๊ตฌํ˜„

  • writeHead: ์š”์ฒญ ํ—ค๋”์— ์ž…๋ ฅํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • Set-Cookie: ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ์ฟ ํ‚ค๋ฅผ ์„ค์ •ํ•˜๋ผ๊ณ  ๋ช…๋ น

์ฟ ํ‚ค: ํ‚ค=๊ฐ’์˜ ์Œ

  • name=zerocho
  • ๋งค ์š”์ฒญ๋งˆ๋‹ค ์„œ๋ฒ„์— ๋™๋ด‰ํ•ด์„œ ๋ณด๋ƒ„
    ๐Ÿ”ปcookie.js
const http = require('http');

http.createServer((req, res) => {
  console.log(req.url, req.headers.cookie);
  res.writeHead(200, { 'Set-Cookie': 'mycookie=test' });
  res.end('Hello Cookie');
})
  .listen(8083, () => {
    console.log('8083๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });
 

โœ”๊ฒฐ๊ณผ

3. ์ฟ ํ‚ค ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ

req.headers.cookie: ์ฟ ํ‚ค๊ฐ€ ๋ฌธ์ž์—ด๋กœ ๋‹ด๊ฒจ์žˆ์Œ
req.url: ์š”์ฒญ ์ฃผ์†Œ


localhost:8082์— ์ ‘์†

  • ์š”์ฒญ์ด ์ „์†ก๋˜๊ณ  ์‘๋‹ต์ด ์™”์„ ๋•Œ ์ฟ ํ‚ค๊ฐ€ ์„ค์ •๋จ
  • favicon.ico๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž๋™์œผ๋กœ ๋ณด๋‚ด๋Š” ์š”์ฒญ
  • ๋‘ ๋ฒˆ์งธ ์š”์ฒญ์ธ favicon.ico์— ์ฟ ํ‚ค๊ฐ€ ๋„ฃ์–ด์ง

4. ํ—ค๋”์™€ ๋ณธ๋ฌธ

http ์š”์ฒญ๊ณผ ์‘๋‹ต์€ ํ—ค๋”์™€ ๋ณธ๋ฌธ์„ ๊ฐ€์ง

  • ํ—ค๋”๋Š” ์š”์ฒญ ๋˜๋Š” ์‘๋‹ต์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง
  • ๋ณธ๋ฌธ์€ ์ฃผ๊ณ ๋ฐ›๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ
  • ์ฟ ํ‚ค๋Š” ๋ถ€๊ฐ€์ ์ธ ์ •๋ณด์ด๋ฏ€๋กœ ํ—ค๋”์— ์ €์žฅ![]

5. http ์ƒํƒœ ์ฝ”๋“œ

writeHead ๋ฉ”์„œ๋“œ์— ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋„ฃ์€ ๊ฐ’
์š”์ฒญ์ด ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ์‹คํŒจํ–ˆ๋Š”์ง€๋ฅผ ์•Œ๋ ค์คŒ
2XX: ์„ฑ๊ณต์„ ์•Œ๋ฆฌ๋Š” ์ƒํƒœ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ 200(์„ฑ๊ณต), 201(์ž‘์„ฑ๋จ)์ด ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
3XX: ๋ฆฌ๋‹ค์ด๋ ‰์…˜(๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์ด๋™)์„ ์•Œ๋ฆฌ๋Š” ์ƒํƒœ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ–ˆ๋Š”๋ฐ ๋‹ค๋ฅธ ์ฃผ์†Œ์˜ ํŽ˜์ด์ง€๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ ์ด ์ฝ”๋“œ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ 301(์˜๊ตฌ ์ด๋™), 302(์ž„์‹œ ์ด๋™)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
4XX: ์š”์ฒญ ์˜ค๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์š”์ฒญ ์ž์ฒด์— ์˜ค๋ฅ˜๊ฐ€ ์žˆ์„ ๋•Œ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ 401(๊ถŒํ•œ ์—†์Œ), 403(๊ธˆ์ง€๋จ), 404(์ฐพ์„ ์ˆ˜ ์—†์Œ)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
5XX: ์„œ๋ฒ„ ์˜ค๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์š”์ฒญ์€ ์ œ๋Œ€๋กœ ์™”์ง€๋งŒ ์„œ๋ฒ„์— ์˜ค๋ฅ˜๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๊ฐ€ ๋œจ์ง€ ์•Š๊ฒŒ ์ฃผ์˜ํ•ด์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๋ฅผ ํด๋ผ์ด์–ธํŠธ๋กœ res.writeHead๋กœ ์ง์ ‘ ๋ณด๋‚ด๋Š” ๊ฒฝ์šฐ๋Š” ์—†๊ณ , ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์„œ๋ฒ„๊ฐ€ ์•Œ์•„์„œ 5XX๋Œ€ ์ฝ”๋“œ๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค. 500(๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜), 502(๋ถˆ๋Ÿ‰ ๊ฒŒ์ดํŠธ์›จ์ด), 503(์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ)์ด ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

6. ์ฟ ํ‚ค๋กœ ๋‚˜๋ฅผ ์‹๋ณ„ํ•˜๊ธฐ

์ฟ ํ‚ค์— ๋‚ด ์ •๋ณด๋ฅผ ์ž…๋ ฅ

  • parseCookies: ์ฟ ํ‚ค ๋ฌธ์ž์—ด์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  • ์ฃผ์†Œ๊ฐ€ /login์ธ ๊ฒฝ์šฐ์™€ /์ธ ๊ฒฝ์šฐ๋กœ ๋‚˜๋‰จ
  • /login์ธ ๊ฒฝ์šฐ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์œผ๋กœ ์˜จ
    ์ด๋ฆ„์„ ์ฟ ํ‚ค๋กœ ์ €์žฅ
  • ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ ์ฟ ํ‚ค๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€ ํŒ๋‹จ
    ์žˆ์œผ๋ฉด ํ™˜์˜ ์ธ์‚ฌ
    ์—†์œผ๋ฉด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

๐Ÿ”ปcookie2.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>์ฟ ํ‚ค&์„ธ์…˜ ์ดํ•ดํ•˜๊ธฐ</title>
</head>
<body>
<form action="/login">
    <input id="name" name="name" placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”" />
    <button id="login">๋กœ๊ทธ์ธ</button>
</form>
</body>
</html>

๐Ÿ”ปcookie2.js

const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');

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

http.createServer(async (req, res) => {
  const cookies = parseCookies(req.headers.cookie); // { mycookie: 'test' }
  // ์ฃผ์†Œ๊ฐ€ /login์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ
  if (req.url.startsWith('/login')) {
    const { query } = url.parse(req.url);
    const { name } = qs.parse(query);
    const expires = new Date();
    // ์ฟ ํ‚ค ์œ ํšจ ์‹œ๊ฐ„์„ ํ˜„์žฌ์‹œ๊ฐ„ + 5๋ถ„์œผ๋กœ ์„ค์ •
    expires.setMinutes(expires.getMinutes() + 5);
    res.writeHead(302, {//302๋Š” redirection
      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('./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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

7. ์ฟ ํ‚ค ์˜ต์…˜

Set-Cookie ์‹œ ๋‹ค์–‘ํ•œ ์˜ต์…˜์ด ์žˆ์Œ

  • ์ฟ ํ‚ค๋ช…=์ฟ ํ‚ค๊ฐ’: ๊ธฐ๋ณธ์ ์ธ ์ฟ ํ‚ค์˜ ๊ฐ’์ž…๋‹ˆ๋‹ค. mycookie=test ๋˜๋Š” name=zerocho ๊ฐ™์ด ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • Expires=๋‚ ์งœ: ๋งŒ๋ฃŒ ๊ธฐํ•œ์ž…๋‹ˆ๋‹ค. ์ด ๊ธฐํ•œ์ด ์ง€๋‚˜๋ฉด ์ฟ ํ‚ค๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€์ž…๋‹ˆ๋‹ค.
  • Max-age=์ดˆ: Expires์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋‚ ์งœ ๋Œ€์‹  ์ดˆ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ดˆ๊ฐ€ ์ง€๋‚˜๋ฉด ์ฟ ๊ธฐ๊ฐ€ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค. Expires๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • Domain=๋„๋ฉ”์ธ๋ช…: ์ฟ ํ‚ค๊ฐ€ ์ „์†ก๋  ๋„๋ฉ”์ธ์„ ํŠน์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ ํ˜„์žฌ ๋„๋ฉ”์ธ์ž…๋‹ˆ๋‹ค.
  • Path=URL: ์ฟ ํ‚ค๊ฐ€ ์ „์†ก๋  URL์„ ํŠน์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ โ€˜/โ€™์ด๊ณ  ์ด ๊ฒฝ์šฐ ๋ชจ๋“  URL์—์„œ ์ฟ ํ‚ค๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Secure: HTTPS์ผ ๊ฒฝ์šฐ์—๋งŒ ์ฟ ํ‚ค๊ฐ€ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.
  • HttpOnly: ์„ค์ • ์‹œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ฟ ํ‚ค์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ฟ ํ‚ค ์กฐ์ž‘์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

8. ์ฟ ํ‚ค ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ

localhost:8084 ํฌํŠธ์— ์ ‘์†

  • Application ํƒญ(F12) ์—ด๊ธฐ
  • ๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด ์ฟ ํ‚ค๊ฐ€ ์ƒ์„ฑ๋จ

9. ์„ธ์…˜ ์‚ฌ์šฉํ•˜๊ธฐ

์ฟ ํ‚ค์˜ ์ •๋ณด๋Š” ๋…ธ์ถœ๋˜๊ณ  ์ˆ˜์ •๋˜๋Š” ์œ„ํ—˜์ด ์žˆ์Œ

  • ์ค‘์š”ํ•œ ์ •๋ณด๋Š” ์„œ๋ฒ„์—์„œ ๊ด€๋ฆฌํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—๋Š” ์„ธ์…˜ ํ‚ค๋งŒ ์ œ๊ณต
  • ์„œ๋ฒ„์— ์„ธ์…˜ ๊ฐ์ฒด(session) ์ƒ์„ฑ ํ›„, uniqueInt(ํ‚ค)๋ฅผ ๋งŒ๋“ค์–ด ์†์„ฑ๋ช…์œผ๋กœ ์‚ฌ์šฉ
  • ์†์„ฑ ๊ฐ’์— ์ •๋ณด ์ €์žฅํ•˜๊ณ  uniqueInt๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋ƒ„
    ๐Ÿ”ปsession.js
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');

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 { query } = url.parse(req.url);
    const { name } = qs.parse(query);
    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('./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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

10. ์„ธ์…˜ ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ

localhost:8085


์‹ค ์„œ๋ฒ„์—์„œ๋Š” ์„ธ์…˜์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜์ง€ ๋ง๊ธฐ X
6์žฅ์—์„œ ๋‚˜์˜ค๋Š” express-session ์‚ฌ์šฉํ•˜๊ธฐ

https์™€ http2

1. https

์›น ์„œ๋ฒ„์— SSL ์•”ํ˜ธํ™”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ชจ๋“ˆ

  • ์˜ค๊ณ  ๊ฐ€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ์ค‘๊ฐ„์— ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๋”๋ผ๋„ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์—†์Œ
  • ์š”์ฆ˜์—๋Š” https ์ ์šฉ์ด ํ•„์ˆ˜(๊ฐœ์ธ ์ •๋ณด๊ฐ€ ์žˆ๋Š” ๊ณณ์€ ํŠนํžˆ)

2. https ์„œ๋ฒ„

http ์„œ๋ฒ„๋ฅผ https ์„œ๋ฒ„๋กœ

  • ์•”ํ˜ธํ™”๋ฅผ ์œ„ํ•ด ์ธ์ฆ์„œ๊ฐ€ ํ•„์š”ํ•œ๋ฐ ๋ฐœ๊ธ‰๋ฐ›์•„์•ผ ํ•จ
    createServer๊ฐ€ ์ธ์ž๋ฅผ ๋‘ ๊ฐœ ๋ฐ›์Œ
  • ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋Š” ์ธ์ฆ์„œ์™€ ๊ด€๋ จ๋œ ์˜ต์…˜ ๊ฐ์ฒด
  • pem, crt, key ๋“ฑ ์ธ์ฆ์„œ๋ฅผ ๊ตฌ์ž…ํ•  ๋•Œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ํŒŒ์ผ ๋„ฃ๊ธฐ
  • ๋‘ ๋ฒˆ์งธ ์ธ์ž๋Š” ์„œ๋ฒ„ ๋กœ์ง

๐Ÿ”ปserver1.js

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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });


๐Ÿ”ปserver1-3.js

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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

3. http2

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

  • ์š”์ฒญ ๋ฐ ์‘๋‹ต ๋ฐฉ์‹์ด ๊ธฐ์กด http/1.1๋ณด๋‹ค ๊ฐœ์„ ๋จ
  • ์›น์˜ ์†๋„๋„ ๊ฐœ์„ ๋จ

4. http2 ์ ์šฉ ์„œ๋ฒ„

https ๋ชจ๋“ˆ์„ http2๋กœ, createServer ๋ฉ”์„œ๋“œ๋ฅผ createSecureServer ๋ฉ”์„œ๋“œ๋กœ

๐Ÿ”ปserver1-4.js

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

http2.createSecureServer({
  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๋ฒˆ ํฌํŠธ์—์„œ ์„œ๋ฒ„ ๋Œ€๊ธฐ ์ค‘์ž…๋‹ˆ๋‹ค!');
  });

๊ฒฐ๊ณผ

cluster

1. cluster

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

  • ํฌํŠธ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋‘˜ ์ˆ˜ ์žˆ์Œ
  • ์š”์ฒญ์ด ๋งŽ์ด ๋“ค์–ด์™”์„ ๋•Œ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋œ ์„œ๋ฒ„์˜ ๊ฐœ์ˆ˜๋งŒํผ ์š”์ฒญ์ด ๋ถ„์‚ฐ๋จ
  • ์„œ๋ฒ„์— ๋ฌด๋ฆฌ๊ฐ€ ๋œ ๊ฐ
  • ์ฝ”์–ด๊ฐ€ 8๊ฐœ์ธ ์„œ๋ฒ„๊ฐ€ ์žˆ์„ ๋•Œ: ๋ณดํ†ต์€ ์ฝ”์–ด ํ•˜๋‚˜๋งŒ ํ™œ์šฉ
  • cluster๋กœ ์ฝ”์–ด ํ•˜๋‚˜๋‹น ๋…ธ๋“œ ํ”„๋กœ์„ธ์Šค ํ•˜๋‚˜๋ฅผ ๋ฐฐ์ • ๊ฐ€๋Šฅ
  • ์„ฑ๋Šฅ์ด 8๋ฐฐ๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ๊ฐœ์„ ๋จ
  • ๋‹จ์ : ์ปดํ“จํ„ฐ ์ž์›(๋ฉ”๋ชจ๋ฆฌ, ์„ธ์…˜ ๋“ฑ) ๊ณต์œ  ๋ชป ํ•จ
  • Redis ๋“ฑ ๋ณ„๋„ ์„œ๋ฒ„๋กœ ํ•ด๊ฒฐ

2. ์„œ๋ฒ„ ํด๋Ÿฌ์Šคํ„ฐ๋ง

๋งˆ์Šคํ„ฐ ํ”„๋กœ์„ธ์Šค์™€ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค

  • ๋งˆ์Šคํ„ฐ ํ”„๋กœ์„ธ์Šค๋Š” CPU ๊ฐœ์ˆ˜๋งŒํผ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค๋ฅผ ๋งŒ๋“ฆ(worker_threads๋ž‘ ๊ตฌ์กฐ ๋น„์Šท)

๐Ÿ”ปcluster.js

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}๋ฒˆ ์›Œ์ปค ์‹คํ–‰`);
}

3.์›Œ์ปค ํ”„๋กœ์„ธ์Šค ๊ฐœ์ˆ˜ ํ™•์ธํ•˜๊ธฐ

์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„ ์ข…๋ฃŒ๋˜๋„๋ก ์„ค์ •

  • ์‹คํ–‰ํ•œ ์ปดํ“จํ„ฐ์˜ ์ฝ”์–ด๊ฐ€ 8๊ฐœ์ด๋ฉด 8๋ฒˆ ์š”์ฒญ์„ ๋ฐ›๊ณ  ์ข…๋ฃŒ๋จ

4. ์›Œ์ปค ํ”„๋กœ์„ธ์Šค ๋‹ค์‹œ ์‚ด๋ฆฌ๊ธฐ

์›Œ์ปค๊ฐ€ ์ฃฝ์„ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์›Œ์ปค๋ฅผ ์ƒ์„ฑ

  • ์ด ๋ฐฉ์‹์€ ์ข‹์ง€ ์•Š์Œ
  • ์˜ค๋ฅ˜ ์ž์ฒด๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ์•Š๋Š” ํ•œ ๊ณ„์† ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ
  • ํ•˜์ง€๋งŒ ์„œ๋ฒ„๊ฐ€ ์ข…๋ฃŒ๋˜๋Š” ํ˜„์ƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ์–ด ์ฐธ๊ณ ํ•  ๋งŒํ•จ.

0๊ฐœ์˜ ๋Œ“๊ธ€