KDT/TIL

9/23 TIL : koa, pug, ์ฑ„ํŒ… ๊ธฐ๋Šฅ ๊ตฌํ˜„

ebulsok 2022. 9. 26. 13:44

๐Ÿ”Ž koa

  • express ํŒ€์—์„œ ๋งŒ๋“  ํ”„๋ ˆ์ž„์›Œํฌ
  • express ๋Œ€๋น„ ๋” ๊ฐ€๋ณ๊ณ  ๋น ๋ฆ„
  • ๋ฏธ๋“ค์›จ์–ด ๋ ˆ๋ฒจ์—์„œ๋„ async/await๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ํ”„๋ ˆ์ž„์›Œํฌ ์„ค์น˜: [ํ„ฐ๋ฏธ๋„] npm i koa
// @ts-check
const Koa = require('koa');

const app = new Koa();
const PORT = 4500;

app.use(async (ctx, next) => {
  console.log(ctx.request);
  console.log(ctx.response);
  ctx.body = 'Hello, koa world!';
});

app.listen(PORT, () => {
  console.log(`์„œ๋ฒ„๋Š” ${PORT}์—์„œ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.`);
});

 

๐Ÿšฉ Pug

  • node.js์šฉ view engine
  • html ๋Œ€๋น„ ๊ฐ„๋‹จํ•˜๊ฒŒ ํƒœ๊ทธ ์‚ฌ์šฉ
  • ํ…œํ”Œ๋ฆฟ ๊ธฐ๋Šฅ ์ œ๊ณต
  • ์„ค์น˜: [ํ„ฐ๋ฏธ๋„] npm i koa-pug -s
  • app.js์— pug ๋ฏธ๋“ค์›จ์–ด ์ ์šฉ
// @ts-check
const Koa = require('koa');

const Pug = require('koa-pug');
const path = require('path');

const app = new Koa();
const PORT = 4500;

const pug = new Pug({
  viewPath: path.resolve(__dirname, './views'),
  app,
});

app.use(async (ctx, next) => {
  await ctx.render('chat');
});

app.listen(PORT, () => {
  console.log(`์„œ๋ฒ„๋Š” ${PORT}์—์„œ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.`);
});
  • /views/chat.pug(+๋ถ€ํŠธ์ŠคํŠธ๋žฉ ์ ์šฉ)
html 
    head 
        link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous")
    body.d-flex.flex-column.text-center.align-items-center
        h1.w-100.p-3.bg-primary.text-white.font-bold Chat
        div.w-50.h-75.p-5.bg-secondary.text-bottom.overflow-auto 
            p.p-2.bg-warning.fw-bold.text-white ์ฑ„ํŒ… ์˜ˆ์‹œ
        form.w-50(action="/chat")
            input.w-75.m-3.p-3(type="text" placeholder="์ฑ„ํŒ…์„ ์ž…๋ ฅํ•˜์„ธ์š”")
            a.p-3.btn.btn-primary ๋ณด๋‚ด๊ธฐ

 

๐Ÿšฉ koa-web socket

  • ๋ชจ๋“ˆ ์„ค์น˜: [ํ„ฐ๋ฏธ๋„] npm i koa-websocket
  • [ํ„ฐ๋ฏธ๋„] npm i koa-route
  • /chat ์ด๋ผ๋Š” ๊ฒฝ๋กœ๋กœ ๋“ค์–ด์˜ค๋ฉด ์›น ์†Œ์ผ“ ํ†ต์‹ ์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฌธ๊ตฌ๋ฅผ ์ „์†ก
  • ํด๋ผ์ด์–ธํŠธ์—์„œ message๊ฐ€ ์˜ค๋ฉด ๋ฐ›์•„์„œ ์„œ๋ฒ„์— ์ถœ๋ ฅ
const websockify = require('koa-websocket');
const route = require('koa-route');

const app = websockify(new Koa());

app.ws.use(
  route.all('./chat', (ctx) => {
    ctx.websocket.send('์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.');
    ctx.websocket.on('message', (message) => {
      console.log(message.toString());
    });
  })
);

 

๐Ÿšฉ /public/chat.js ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ ์ฑ„ํŒ… ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„

  • express๋Š” ๋‚ด์žฅ ๋ชจ๋“ˆ๋กœ๋„ ํŠน์ • ์ฃผ์†Œ์— ๋Œ€ํ•œ static ํด๋” ์„ค์ • ๊ฐ€๋Šฅ
  • koa๋Š” koa-static๊ณผ koa-mount ๋ชจ๋“ˆ์„ ์„ค์น˜ํ•ด์•ผ ๊ฐ€๋Šฅ
  • [ํ„ฐ๋ฏธ๋„] npm i koa-static
  • [ํ„ฐ๋ฏธ๋„] npm i koa-mount
  • ํด๋ผ์ด์–ธํŠธ์—์„œ WebSocket ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์„œ๋ฒ„์— addEventListener-open์„ ๋“ฑ๋กํ•˜์—ฌ ์ด์ „์— ์ค€๋น„ํ•ด๋‘์—ˆ๋˜ ์„œ๋ฒ„๋กœ ํ†ต์‹  ๋ณด๋‚ด๊ธฐ

 

๐Ÿ”Ž IIFE(Immediately Invoked Function Expression)

  • ํ•จ์ˆ˜๊ฐ€ ์ •์˜๋˜์ž๋งˆ์ž ์‚ฌ์šฉ๋˜๋Š” ์ฆ‰์‹œ ์‹คํ–‰ ํ•จ์ˆ˜
  • ํ•จ์ˆ˜์˜ ์ •์˜ ๋ถ€๋ถ„์„ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ๊ฐ์ถ”๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ
// @ts-check
// IIFE
(() => {
  const socket = new WebSocket(`ws://${window.location.host}/chat`);

  socket.addEventListener('open', () => {
    socket.send('ํด๋ผ์ด์–ธํŠธ ์ž…๋‹ˆ๋‹ค.');
  });
})();

 

๐Ÿ”Ž Broadcast

  • ์›น ์†Œ์ผ“ ์„œ๋ฒ„๋Š” app.ws์— ํ• ๋‹น๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ server๋ผ๋Š” ๋ณ€์ˆ˜์— ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น ๋ฌธ๋ฒ•์œผ๋กœ ๋ฐ›์•„์˜ค๊ธฐ
app.ws.use(
  route.all('/chat', (ctx) => {
    const { server } = app.ws;

    server?.clients.forEach((client) => {
      client.send('๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ');
    });

    ctx.websocket.on('message', (message) => {
      console.log(message.toString());
    });
  })
);

 

๐Ÿ”Ž ์‹ค์ œ ์ฑ„ํŒ…์˜ ํ๋ฆ„

  • ํด๋ผ์ด์–ธํŠธ์˜ form์— ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜๊ณ  ๋ณด๋‚ด๊ธฐ๋ฅผ ๋ˆ„๋ฅด๋ฉด ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  • ์„œ๋ฒ„๋Š” ํ•ด๋‹น ์ •๋ณด๋ฅผ ๋‹ค์‹œ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋‚ด๋Š” ํ˜•ํƒœ
  • chat.pug์—์„œ id ๋ถ€์—ฌ
html 
    head 
        link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous")
    body.d-flex.flex-column.text-center.align-items-center
        h1.w-100.p-3.bg-primary.text-white.font-bold Chat
        div#chat.w-50.h-75.p-5.bg-secondary.text-bottom.overflow-auto 
            p.p-2.bg-warning.fw-bold.text-white ์ฑ„ํŒ… ์˜ˆ์‹œ
        form(action="/chat" onsubmit="return false;")#form.w-50
            input(type="text" placeholder="์ฑ„ํŒ…์„ ์ž…๋ ฅํ•˜์„ธ์š”")#input.w-75.m-3.p-3
            a#btn.p-3.btn.btn-primary ๋ณด๋‚ด๊ธฐ
    script(src="public/chat.js")

 

  • /public/chat.js
// @ts-check
// IIFE
(() => {
  const socket = new WebSocket(`ws://${window.location.host}/chat`);
  const btnEl = document.getElementById('btn');
  const inputEl = document.getElementById('input');
  const chatEl = document.getElementById('chat');

  btnEl?.addEventListener('click', () => {
    const msg = inputEl?.value;
    const data = {
      name: 'ebulsok',
      msg: msg,
    };
    socket.send(JSON.stringify(data));
    inputEl.value = '';
  });

  socket.addEventListener('open', () => {
    // socket.send('ํด๋ผ์ด์–ธํŠธ ์ž…๋‹ˆ๋‹ค.');
  });

  socket.addEventListener('message', (event) => {
    const { name, msg } = JSON.parse(event.data);

    const msgEl = document.createElement('p');
    msgEl.innerText = `${name}: ${msg}`;
    msgEl.classList.add('p-2');
    msgEl.classList.add('bg-warning');
    msgEl.classList.add('fw-bold');
    // msgEl.classList.add('text-white');
    chatEl?.appendChild(msgEl);
    chatEl.scrollTop = chatEl.scrollHeight - chatEl.clientHeight;
  });
})();
  • app.js
// @ts-check
const Koa = require('koa');
const websockify = require('koa-websocket');
const route = require('koa-route');
const serve = require('koa-static');
const mount = require('koa-mount');

const Pug = require('koa-pug');
const path = require('path');

const app = websockify(new Koa());
const PORT = 4500;

app.use(mount('/public', serve('public')));

const pug = new Pug({
  viewPath: path.resolve(__dirname, './views'),
  app,
});

app.ws.use(
  route.all('/chat', (ctx) => {
    const { server } = app.ws;

    server?.clients.forEach((client) => {
        // client.send('๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ');
    });

    ctx.websocket.on('message', (message) => {
      server?.clients.forEach((client) => {
        client.send(message.toString());
      });
    });
  })
);

app.use(async (ctx, next) => {
  await ctx.render('chat');
});

app.listen(PORT, () => {
  console.log(`์„œ๋ฒ„๋Š” ${PORT}์—์„œ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.`);
});

 

๐Ÿšฉ app.js์— ์ƒˆ๋กœ์šด ์œ ์ €๊ฐ€ ์ ‘์†ํ–ˆ๋‹ค๋Š” ๋ฉ”์‹œ์ง€, ๋‚˜๊ฐ”๋‹ค๋Š” ๋ฉ”์‹œ์ง€ ์•ˆ๋‚ด ์ฝ”๋“œ ์ถ”๊ฐ€

// @ts-check
const Koa = require('koa');
const websockify = require('koa-websocket');
const route = require('koa-route');
const serve = require('koa-static');
const mount = require('koa-mount');

const Pug = require('koa-pug');
const path = require('path');

const app = websockify(new Koa());
const PORT = 4500;

app.use(mount('/public', serve('public')));

const pug = new Pug({
  viewPath: path.resolve(__dirname, './views'),
  app,
});

app.ws.use(
  route.all('/chat', (ctx) => {
    const { server } = app.ws;

    server?.clients.forEach((client) => {
      client.send(
        JSON.stringify({
          name: 'system',
          msg: `์ƒˆ๋กœ์šด ์œ ์ €๊ฐ€ ์ฐธ์—ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์œ ์ € ์ˆ˜: ${server.clients.size}`,
          bg: 'bg-danger',
          textColor: 'text-white',
        })
      );
    });

    ctx.websocket.on('message', (message) => {
      server?.clients.forEach((client) => {
        client.send(message.toString());
      });
    });

    ctx.websocket.on('close', (message) => {
      server?.clients.forEach((client) => {
        client.send(
          JSON.stringify({
            name: 'server',
            msg: `์œ ์ €๊ฐ€ ํ‡ด์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์œ ์ € ์ˆ˜: ${server.clients.size}`,
            bg: 'bg-dark',
            textColor: 'text-white',
          })
        );
      });
    });
  })
);

app.use(async (ctx, next) => {
  await ctx.render('chat');
});

app.listen(PORT, () => {
  console.log(`์„œ๋ฒ„๋Š” ${PORT}์—์„œ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค.`);
});

 

๐Ÿšฉ chat.js ๋žœ๋คํ•œ ๋‹‰๋„ค์ž„ ์ •ํ•˜๊ธฐ

// @ts-check
const adj = [
  '๋ฉ‹์ง„',
  '์ž˜์ƒ๊ธด',
  '์˜ˆ์œ',
  '์กธ๋ฆฐ',
  '์šฐ์•„ํ•œ',
  'ํž™ํ•œ',
  '๋ฐฐ๊ณ ํ”ˆ',
  '์ง‘์— ๊ฐ€๊ธฐ ์‹ซ์€',
  '์ง‘์— ๊ฐ€๊ณ  ์‹ถ์€',
  '๊ท€์—ฌ์šด',
  '์ค‘ํ›„ํ•œ',
  '๋˜‘๋˜‘ํ•œ',
  '์ด๊ฒŒ ๋ญ”๊ฐ€ ์‹ถ์€',
  '๊นŒ๋ฆฌํ•œ',
  'ํ”„๋ก ํŠธ๊ฐ€ ํ•˜๊ณ  ์‹ถ์€',
  '๋ฐฑ์—”๋“œ๊ฐ€ ์žฌ๋ฏธ ์žˆ๋Š”',
  '๋ชฝ๊ณ  ๋””๋น„ ๋‚ ๋ ค ๋จน์€',
  '์—ด์‹ฌํžˆํ•˜๋Š”',
  'ํ”ผ๊ณคํ•œ',
  '๋ˆˆ๋น›์ด ์ดˆ๋กฑ์ดˆ๋กฑํ•œ',
  '์น˜ํ‚จ์ด ๋•ก๊ธฐ๋Š”',
  '์ˆ ์ด ๋•ก๊ธฐ๋Š”',
];

const member = [
  'a๋‹˜',
  'b๋‹˜',
  'c๋‹˜',
  'd๋‹˜',
  'e๋‹˜',
  'f๋‹˜',
  'g๋‹˜',
  'h๋‹˜',
  'i๋‹˜',
  'j๋‹˜',
  'k๋‹˜',
  'l๋‹˜',
  'm๋‹˜',
  'n๋‹˜',
  'o๋‹˜',
  '์„ธ์˜๋‹˜',
  '์˜์ง„๋‹˜',
  '์Šน์ˆ˜๋‹˜',
  'ํ•ด์„ฑ๋‹˜',
  'ํ—ˆ์›๋‹˜',
];

const bootColor = [
  { bg: 'bg-primary', text: 'text-white' },
  { bg: 'bg-success', text: 'text-white' },
  { bg: 'bg-warning', text: 'text-black' },
  { bg: 'bg-info', text: 'text-white' },
  { bg: 'alert-primary', text: 'text-black' },
  { bg: 'alert-secondary', text: 'text-black' },
  { bg: 'alert-success', text: 'text-black' },
  { bg: 'alert-danger', text: 'text-black' },
  { bg: 'alert-warning', text: 'text-black' },
  { bg: 'alert-info', text: 'text-black' },
];

// IIFE
(() => {
  const socket = new WebSocket(`ws://${window.location.host}/chat`);
  const btnEl = document.getElementById('btn');
  const inputEl = document.getElementById('input');
  const chatEl = document.getElementById('chat');

  function pickRandom(arr) {
    const index = Math.floor(Math.random() * arr.length);
    console.log(index);
    return arr[index];
  }

  const nickName = pickRandom(adj) + ' ' + pickRandom(member);
  const theme = pickRandom(bootColor);

  btnEl?.addEventListener('click', () => {
    const msg = inputEl?.value;
    const data = {
      name: nickName,
      msg: msg,
      bg: theme.bg,
      textColor: theme.text,
    };
    socket.send(JSON.stringify(data));
    inputEl.value = '';
  });

  socket.addEventListener('open', () => {
    // socket.send('ํด๋ผ์ด์–ธํŠธ ์ž…๋‹ˆ๋‹ค.');
  });

  socket.addEventListener('message', (event) => {
    const { name, msg, bg, textColor } = JSON.parse(event.data);

    const msgEl = document.createElement('p');
    msgEl.innerText = `${name}: ${msg}`;
    msgEl.classList.add('p-2');
    msgEl.classList.add(bg);
    msgEl.classList.add('fw-bold');
    msgEl.classList.add(textColor);
    chatEl?.appendChild(msgEl);
    chatEl.scrollTop = chatEl.scrollHeight - chatEl.clientHeight;
  });
})();

 

๐Ÿšฉ MongoDB ์—ฐ๊ฒฐ

  • ๋ชจ๋“ˆ ์„ค์น˜: [ํ„ฐ๋ฏธ๋„] npm i mongodb
  • public/mongo.js
// @ts-check
const { MongoClient, ServerApiVersion } = require('mongodb');

const uri =
  'mongodb://ebulsok:<password>@localhost:27017/?authMechanism=DEFAULT';

const client = new MongoClient(uri, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  serverApi: ServerApiVersion.v1,
});

module.exports = client;

 

๐Ÿšฉ ์ฑ„ํŒ… ๋‚ด์—ญ DB ์ €์žฅ

  • ํด๋ผ์ด์–ธํŠธ์—์„œ ์ „์†ก๋œ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด DB์— ์ €์žฅ
  • ์ „๋‹ฌ๋ฐ›์€ ๋ฉ”์‹œ์ง€๋ฅผ JSON.parseํ•˜์—ฌ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋ณ€๊ฒฝํ•œ ๋‹ค์Œ ๋ณ€์ˆ˜์— ๋„ฃ๊ธฐ
  • chat์„ ์ „๊ฐœ์—ฐ์‚ฐ์ž๋กœ ํ’€์–ด์„œ DB์— ๋„ฃ๊ธฐ
  • ์ฑ„ํŒ… ๋‚ด์—ญ ์‚ฝ์ž… ์‹œ ์‹œ๊ฐ„ ํ•ญ๋ชฉ ์ถ”๊ฐ€
ctx.websocket.on('message', async (message) => {
      const chat = JSON.parse(message.toString());
      const insertClient = await _client;
      const chatCursor = insertClient.db('KDT-1').collection('chats');
      await chatCursor.insertOne({
        ...chat,
        createdAt: new Date(),
      });

      server?.clients.forEach((client) => {
        client.send(
          JSON.stringify({
            type: 'chat',
            data: { ...chat },
          })
        );
      });
});

 

๐Ÿšฉ ์ฑ„ํŒ… ๋‚ด์—ญ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

  • ์ƒˆ๋กœ์šด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ ‘์†ํ•˜๋ฉด DB๋กœ๋ถ€ํ„ฐ ๋ชจ๋“  ์ฑ„ํŒ… ๋‚ด์—ญ์„ ๋ฐ›์•„์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌ
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„ ์ ‘์† ์‹œ ์ตœ์ดˆ์— ๋ฐœ์ƒํ•ด์•ผ ํ•จ, ์ ‘์†ํ•œ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ๋งŒ ์ „๋‹ฌ
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋‚ธ ์ฑ„ํŒ…๊ณผ ๊ตฌ๋ถ„๋  ํ•„์š”๊ฐ€ ์žˆ์Œ(type ํ‚ค ์ถ”๊ฐ€)
app.ws.use(
  route.all('/chat', async (ctx) => {
    const { server } = app.ws;

    const client = await _client;
    const cursor = client.db('KDT-1').collection('chats');
    const chats = cursor.find({}, { sort: { createdAt: 1 } });
    const chatsData = await chats.toArray();

    ctx.websocket.send(
      JSON.stringify({
        type: 'sync',
        data: { chatsData },
      })
    );

    server?.clients.forEach((client) => {
      client.send(
        JSON.stringify({
          type: 'chat',
          data: {
            name: 'system',
            msg: `์ƒˆ๋กœ์šด ์œ ์ €๊ฐ€ ์ฐธ์—ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์œ ์ € ์ˆ˜: ${server.clients.size}`,
            bg: 'bg-danger',
            textColor: 'text-white',
          },
        })
      );
    });

    ctx.websocket.on('message', async (message) => {
      const chat = JSON.parse(message.toString());
      const insertClient = await _client;
      const chatCursor = insertClient.db('KDT-1').collection('chats');
      await chatCursor.insertOne({
        ...chat,
        createdAt: new Date(),
      });

      server?.clients.forEach((client) => {
        client.send(
          JSON.stringify({
            type: 'chat',
            data: { ...chat },
          })
        );
      });
    });

    ctx.websocket.on('close', (message) => {
      server?.clients.forEach((client) => {
        client.send(
          JSON.stringify({
            type: 'chat',
            data: {
              name: 'server',
              msg: `์œ ์ €๊ฐ€ ํ‡ด์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์œ ์ € ์ˆ˜: ${server.clients.size}`,
              bg: 'bg-dark',
              textColor: 'text-white',
            },
          })
        );
      });
    });
  })
);

 

๐Ÿšฉ chat.js์—์„œ ์ฑ„ํŒ… ๋‚ด์—ญ๊ณผ ์‹ค์ œ ์ฑ„ํŒ…์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฒ˜๋ฆฌ

  • msgData์˜ type์ด sync์ด๋ฉด ์ด์ „ ์ฑ„ํŒ… ๋‚ด์—ญ์ด๋ฏ€๋กœ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋ถ€ chats ๋ฐฐ์—ด์— ํ‘ธ์‰ฌ
  • chat์ด๋ฉด ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋œ ์‹ค์ œ ์ฑ„ํŒ…์ด๋ฏ€๋กœ chats ๋ฐฐ์—ด์— ํ‘ธ์‰ฌ
  • ๋ถ„๊ธฐ ๋ณ„๋กœ ์ฑ„ํŒ… ๋‚ด์—ญ์„ ๊ทธ๋ ค์ฃผ๋Š” drawChats ํ•จ์ˆ˜ ๊ตฌํ˜„
function drawChats(type, data) {
    if (type === 'sync') {
      chatEl.innerHTML = '';
      chats.forEach(({ name, msg, bg, textColor }) => {
        const msgEl = document.createElement('p');
        msgEl.innerText = `${name}: ${msg}`;
        msgEl.classList.add('p-2');
        msgEl.classList.add(bg);
        msgEl.classList.add('fw-bold');
        msgEl.classList.add(textColor);
        chatEl?.appendChild(msgEl);
        chatEl.scrollTop = chatEl.scrollHeight - chatEl.clientHeight;
      });
    } else if (type === 'chat') {
      const msgEl = document.createElement('p');
      msgEl.innerText = `${data.name}: ${data.msg}`;
      msgEl.classList.add('p-2');
      msgEl.classList.add(data.bg);
      msgEl.classList.add('fw-bold');
      msgEl.classList.add(data.textColor);
      chatEl?.appendChild(msgEl);
      chatEl.scrollTop = chatEl.scrollHeight - chatEl.clientHeight;
    }
  }

  const nickName = pickRandom(adj) + ' ' + pickRandom(member);
  const theme = pickRandom(bootColor);

  btnEl?.addEventListener('click', () => {
    const msg = inputEl?.value;
    const data = {
      name: nickName,
      msg: msg,
      bg: theme.bg,
      textColor: theme.text,
    };
    socket.send(JSON.stringify(data));
    inputEl.value = '';
  });

  socket.addEventListener('message', (event) => {
    const msgData = JSON.parse(event.data);
    const { type, data } = msgData;

    if (type === 'sync') {
      const oldChats = data.chatsData;
      chats.push(...oldChats);
      drawChats(type, data);
    } else if (type === 'chat') {
      chats.push(data);
      drawChats(type, data);
    }
  });

 

๐Ÿšฉ ์—”ํ„ฐํ‚ค๋ฅผ ์ณค์„ ๋•Œ๋„ ์ฑ„ํŒ…์ด ์ „์†ก๋˜๊ฒŒ ์ˆ˜์ •

// chat.pug
form(action="/chat" onsubmit="return false;")#form.w-50
// chat.js
inputEl?.addEventListener('keyup', (event) => {
    if (event.keyCode === 13) btnEl.click();
});