Skip to content

System Monitoring Application Created with vanilla JS + electron + D3.js

Notifications You must be signed in to change notification settings

Yanguk/Visual-system

Repository files navigation

System Monitoring Application Created with vanilla JS + electron + D3.js

์˜์ƒ ๋ณด๊ธฐ
visual-system.mov

๋ชฉ์ฐจ


์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์—ˆ๋‚˜

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋„์ž…

// ./src/mainProcess/osUtil/getCPUInstance
const idle = _.go(
  cpuIter,
  _.map(bValue('idleSum')),
  _.reduce(subtract),
);

// ./src/mainProcess/osUtil/getMemoryInstance
const vmStatInfoArr = _.go(
  vmStatStr,
  str => str.split('\n'),
  L.filter(str => str !== ''),
  L.map(str => str.split(':')),
  _.map(_.pipe(
    L.getIndex,
    _.map(([str, idx]) => (idx
      ? str.replace('.', '').trim()
      : str)),
  )),
);

์ดํ„ฐ๋Ÿฌ๋ธ” ํ”„๋กœํ† ์ฝœ์— ๋”ฐ๋ฅธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋„์ž…์„ ์ธํ•˜์—ฌ ์ฝ”๋“œ์— for ๋ฌธ์ด ์—†์–ด์ง€๊ณ , ์ฝ”๋“œ๊ฐ€ ์„ ์–ธ์ ์ด๊ฒŒ ๋˜์–ด์„œ ํ•œ๋ˆˆ์— ์–ด๋–ค ๋กœ์ง์ธ ํŒŒ์•…ํ•˜๊ธฐ๊ฐ€ ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค.


ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋ฉด์„œ ๋Š๋‚€์  ๋ถ€๊ฐ€ ์„ค๋ช…

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์žฅ์ 

  • ์ง€์—ฐ์„ฑ ํ‰๊ฐ€์™€ ์ปค๋ง์„ ์ด์šฉํ•˜์—ฌ์„œ ํ•จ์ˆ˜์˜ ํ‰๊ฐ€ ์‹œ์ ์„ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ปจํŠธ๋กค์ด ๊ฐ€๋Šฅํ•˜๊ณ , ํŒŒ์ดํ”„ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•จ์ˆ˜ ํ•ฉ์„ฑ์œผ๋กœ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜๊ฐ€ ์žˆ์–ด์„œ ํ•จ์ˆ˜์˜ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋” ์ข‹์•„์ง€๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • for ๋ฌธ์ด ์—†์œผ๋ฏ€๋กœ ์‹ค์ˆ˜ํ•  ํ™•๋ฅ ์ด ์ ์–ด์ง€๊ณ  ์ฝ”๋“œ ์œ ์ง€ ๋ณด์ˆ˜๋„ ์‰ฌ์›Œ์ง„ ๋Š๋‚Œ์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋ฉด์„œ ํž˜๋“ค์—ˆ๋˜์  ๋ฐ ์•„์‰ฌ์šด์ 

  • ํ•จ์ˆ˜๋ฅผ ์ž˜๊ฒŒ ์ชผ๊ฐœ๊ณ  ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ฝ”๋“œ๋ฅผ ๋ดค์„ ๋•Œ ์ง€๋‚˜์นœ ์ถ”์ƒํ™”๋กœ ์ธํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธธ ๊ฒƒ ๊ฐ™์€ ๋Š๋‚Œ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ต์ €๋ฒ„ํŒจํ„ด์„ ์ ์šฉํ•œ ์ธํ„ฐ๋ฒŒ ๊ด€๋ฆฌ

์ด๋ฒคํŠธ ๊ตฌ๋… ๋ฐ ์•Œ๋ฆผ

/** ์ฝ”๋“œ ๋‚ด์šฉ ์ผ๋ถ€ ๋ฐœ์ทŒ **/
// ์ด๋ฒคํŠธ ๊ตฌ๋…ํ•˜๊ธฐ
const onCPUUsageEvent = receiveChannel(channelEnum.CPU.USAGE);
onCPUUsageEvent(intervalUpdateCPU);

// interval ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์‹œ์ž‘
const getCPUInstance = makeSingleTonFactory(CPUInfo);
const cpuInfo = getCPUInstance();
cpuInfo.startInterval(INTERVAL_TIME_500);

// ์•Œ๋ฆผ ์ฃผ๊ธฐ
startInterval(time) {
  this.interval = setInterval(() => {
    this.data.push(os.cpus());
    this.notify('interval', this);
  }, time);
}

main Process์—์„œ ์ธํ„ฐ๋ฒŒ๋งˆ๋‹ค ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ง‘ํ•  ๋•Œ๋งˆ๋‹ค ๊ตฌ๋…์ž์—๊ฒŒ ์•Œ๋ฆผ์„ ์ฃผ๋Š” ํ˜•์‹์„ ์ทจํ•˜์—ฌ์„œ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ธํ„ฐ๋ฒŒ์„ ํ•œ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ˜น์‹œ๋‚˜ ์ธํ„ฐ๋ฒŒ์ด ์—ฌ๋Ÿฌ ๊ฐœ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ๊ด€๋ จ๋œ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


์ด๋ฒคํŠธ ๊ด€๋ฆฌ

// ์ด๋ฒคํŠธ๋ฅผ ๋“ฑ๋กํ•˜๋ฉด ํด๋ฆฌ์–ดํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
export const customAddEventListener = (eventName, f, parent = window) => {
  const handler = () => f();

  parent.addEventListener(eventName, handler);

  return () => parent.removeEventListener(eventName, handler);
};

// unmountํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ด๋ฒคํŠธ ๊ด€๋ฆฌ
unmount(customAddEventListener('resize', reSizing));

unmount(onProcessEvent(processList.render.bind(processList)));

renderer Process์—์„œ main Process์— ์ด๋ฒคํŠธ ๊ตฌ๋…์„ ํ•˜์—ฌ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์„ ์ทจํ•˜์—ฌ์„œ ํŽ˜์ด์ง€๊ฐ€ ๋ณ€๊ฒฝ๋  ์‹œ ๊ธฐ์กด์˜ ์ด๋ฒคํŠธ๋ฅผ ๋Š์–ด ์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ๋กœ์ง๋“ค์„ ์†์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ unmount ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ ์–ด๋–ค ๋กœ์ง์ด ์ด๋ฒคํŠธ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ๊ฑด์ง€ ํŒŒ์•…ํ•˜๊ธฐ๋„ ์‰ฝ๊ณ  ๋”ฐ๋กœ ๊ด€๋ฆฌํ•ด ์ค„ ํ•„์š” ์—†์ด ์•Œ์•„์„œ ํ•ด๋‹น ํŽ˜์ด์ง€๊ฐ€ ์—†์–ด์งˆ ์‹œ ํด๋ฆฌ์–ด ํ•จ์ˆ˜๋“ค์ด ์‹คํ–‰๋˜๊ฒŒ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


Vanilla JS๋กœ SPA ๋งŒ๋“ค๊ธฐ

// ./lib/simpleDom.js
const template = (tag, value) => `<${tag}>${value}</${tag}>`;

const el = html => {
  const wrapper = document.createElement('div');
  wrapper.insertAdjacentHTML('afterbegin', html);

  return wrapper.children[0];
};

const $ = { template, el, ... };

// ./src/rendererProcess/pages/cpuPage.js
const container = _.go(
  $.template('article', ''),
  $.el,
  $.addClass('flex-container'),
  $.addClass('cpu-page'),
);

unmount(renderDom(container));

const coreTemplate = _.go(
  L.range(cpuInfo.length),
  _.map(index => `
    <div class="cpu-core-wrapper">
      <p>CPU CORE ${index + 1} usage <span class="cpu-text"></span></p>
      <div class="cpu-core item"></div>
    </div>
  `),
  _.join('\n'),
);

$.afterBeginInnerHTML(container, coreTemplate);

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ์œ ํ‹ธ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์„œ ๊ฐ„๋‹จํ•œ dom ์ œ์–ด ํ•จ์ˆ˜๋“ค์„ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
html ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“ค๊ณ  ํ˜„์žฌ innerHtml ๋ณด๋‹ค ๋น ๋ฅด๋‹ค๊ณ  ํ•˜๋Š” insertAdjacentHTML๋ฅผ ์ด์šฉํ•˜์—ฌ HTML๋ฅผ ๋„ฃ์–ด์ฃผ์–ด์„œ ๋”์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.


D3.js๋กœ SVG ํƒœ๊ทธ ๋‹ค๋ฃจ๊ธฐ

svgElement.selectAll('g')
  .data(data)
  .join(
    enter => enter...,
    update => update...,
    exit => exit...,
  );

D3 ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ณ , join ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ถ”๊ฐ€๋˜๋Š” ๋ฐ์ดํ„ฐ, ์—…๋ฐ์ดํŠธ๋˜๋Š” ๋ฐ์ดํ„ฐ, ์‚ฌ๋ผ์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ๊ฐ ํ•œ ๋ฒˆ์— ์ปจํŠธ๋กค์ด ๊ฐ€๋Šฅํ•˜์˜€์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ปค์Šคํ…€์„ ์šฉ์ดํ•˜๊ฒŒ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


electron์—์„œ์˜ main๊ณผ renderer์˜ ์†Œํ†ต ๋ฐฉ์‹

// ./src/preload.js
contextBridge.exposeInMainWorld('api', {
  cpu: () => ipcRenderer.invoke('cpu'),
});

contextBridge.exposeInMainWorld('connect', {
  on: curry((channel, fn) => {
    const subscription = (event, ...args) => fn(...args);

    ipcRenderer.on(channel, subscription);

    return () => ipcRenderer.removeListener(channel, subscription);
  }),
});

๋ณด์•ˆ ์ด์Šˆ์— ์˜ํ•ด ํ˜„์žฌ ์ผ๋ ‰ํŠธ๋ก (version: 21.2.2)์—์„œ๋Š” main(node.js)๊ณผ renderer(chromium)์˜ ์†Œํ†ต ๋ฐฉ์‹์ด ์˜ค์ง preload๋ฅผ ํ†ตํ•œ ipc ํ†ต์‹ ๋งŒ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋ฅผ ์ทจํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ์ด ๊ทœ์น™์— ๋”ฐ๋ผ ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜๋“ค์€ invoke ํ˜•์‹์„ ๊ฑด๋„ค์ฃผ์—ˆ๊ณ , ์ด๋ฒคํŠธ ๊ตฌ๋… ๋ฐฉ์‹์€ on ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ preload๋กœ ์ œ๊ณตํ•˜๊ณ , renderer์—์„œ ์ด๋ฒคํŠธ ๊ตฌ๋…๊ณผ ์ทจ์†Œ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


์‹œ์Šคํ…œ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ

  • node.js์—์„œ ์ œ๊ณต๋˜๋Š” os ๋ชจ๋“ˆ์—์„œ ๊ธฐ๋ณธ ์‹œ์Šคํ…œ ์ •๋ณด ๋ฐ ๋ฉ”๋ชจ๋ฆฌ, cpu์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•œ ๋””ํ…Œ์ผํ•œ ์‚ฌ์šฉ๋Ÿ‰ ๋ฐ ํ”„๋กœ์„ธ์Šค ๋ฐ ๋””์Šคํฌ์— ๋Œ€ํ•œ ์ •๋ณด๋Š” node.js์—์„œ ์ œ๊ณต๋˜๋Š” ๋ชจ๋“ˆ๋กœ๋Š” ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜๊ฐ€ ์—†์–ด์„œ child-process ๋ชจ๋“ˆ์„ ํ†ตํ•ด, ํ„ฐ๋ฏธ๋„์— ์œ ๋‹‰์Šค ํ„ฐ๋ฏธ๋„ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰์‹œ์ผœ ์–ป์€ stdout ๊ฐ’์œผ๋กœ ๊ฐ€๊ณตํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ธํŒ…

  • electron ๊ณต์‹ ๋ฌธ์„œ์—์„œ ๊ถŒ์žฅํ•˜๋Š” electron-forge๋ฅผ ์ด์šฉํ•˜์—ฌ์„œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์„ธํŒ…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    electron-forge๋ฅผ ์ด์šฉํ•˜๋ฉด mainProcess์™€ renderer์— ์†์‰ฝ๊ฒŒ ๋‹ค๋ฅธ webpack ์„ธํŒ… ๊ฐ’์„ ์ ์šฉํ•  ์ˆ˜๊ฐ€ ์žˆ๊ณ , webpack dev server ์—ฌ๋Š” ๋™์‹œ์— main Process๋ฅผ ์—ฐ๊ฒฐ์‹œํ‚ค๊ณ  ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰์‹œ์ผœ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœํ•˜๊ธฐ๊ฐ€ ํŽธ๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • webpack์˜ loader์™€ plugin์€ ํ•„์š”ํ•œ ๊ฒƒ ๋งŒ ์ตœ์†Œํ•œ์œผ๋กœ ๋„ฃ์–ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ธํŒ…ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.(renderer ํ”„๋กœ์„ธ์Šค์˜ css loader, scss loader)

๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‚ฌ์šฉ๋ฒ•

// webpack dev server๋ฅผ ์ด์šฉํ•œ ๊ฐœ๋ฐœ์„œ๋ฒ„ ์‹คํ–‰
$ npm start

// App ํŒจํ‚ค์ง•
$ npm run make

ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„

๊ธฐ๊ฐ„: 2022. 11. 07 ~ 11. 25

  • 1์ฃผ์ฐจ: ์•„์ด๋””์–ด ์„ ์ • ํ•˜์—ฌ ๊ธฐํš ๋ฐ POC

  • 2์ฃผ์ฐจ: ๊ธฐํš ๋‹จ๊ณ„์— ๊ณ„ํš๋œ ์นธ๋ฐ˜์— ๋”ฐ๋ผ ๊ธฐ๋Šฅ๊ฐœ๋ฐœ

  • 3์ฃผ์ฐจ: ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ๋งˆ๋ฌด๋ฆฌ ๋ฐ ๋ฐฐํฌํ•˜๊ธฐ


ํ”„๋กœ์ ํŠธ๋ฅผ ๊ธฐํšํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ

ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด๋ฅผ ์งœ๋˜ ์ค‘์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์‹œ์Šคํ…œ(os)์— ์ ‘๊ทผํ•ด ๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ๋งˆ์Œ์ด ์ƒ๊ธฐ์–ด ๊ธฐํšํ•ด ๋ณด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค


ํšŒ๊ณ 

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

About

System Monitoring Application Created with vanilla JS + electron + D3.js

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages