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 ํจ์๋ฅผ ๋ง๋ค์ด์ ์ด๋ค ๋ก์ง์ด ์ด๋ฒคํธ๊ฐ ๊ฑธ๋ฆฌ๋ ๊ฑด์ง ํ์
ํ๊ธฐ๋ ์ฝ๊ณ ๋ฐ๋ก ๊ด๋ฆฌํด ์ค ํ์ ์์ด ์์์ ํด๋น ํ์ด์ง๊ฐ ์์ด์ง ์ ํด๋ฆฌ์ด ํจ์๋ค์ด ์คํ๋๊ฒ ํ์์ต๋๋ค.
// ./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๋ฅผ ๋ฃ์ด์ฃผ์ด์ ๋์ ๋ง๋ค์์ต๋๋ค.
svgElement.selectAll('g')
.data(data)
.join(
enter => enter...,
update => update...,
exit => exit...,
);
D3 ๊ฐ์ฒด๋ฅผ ํ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ๊ณ , join ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ถ๊ฐ๋๋ ๋ฐ์ดํฐ, ์
๋ฐ์ดํธ๋๋ ๋ฐ์ดํฐ, ์ฌ๋ผ์ง๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ฐ ํ ๋ฒ์ ์ปจํธ๋กค์ด ๊ฐ๋ฅํ์์ต๋๋ค.
์ด๋ฅผ ํ์ฉํ์ฌ ์ํ๋ ๋๋ก ์ปค์คํ
์ ์ฉ์ดํ๊ฒ ํ์์ต๋๋ค.
// ./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, ...)๋ค์ ์ฐธ๊ณ ํ์ฌ, ์ ์ฉํ ํจ์๋ค์ด ๋ ์ด๋ค ๊ฒ ์์์ง ๊ณต๋ถํด ๋ณด๊ณ ์ถ์ต๋๋ค.
์ด์์
๋๋ค. ๊ฐ์ฌํฉ๋๋ค.