23-24/Node.js 2

[λ…Έλ“œ 2] 4μž₯. http λͺ¨λ“ˆλ‘œ μ„œλ²„ λ§Œλ“€κΈ°

도담_dodam 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