TCP Socket Router 예제를 통한 Electron Application 작성방법에 대한 연재가 완료되었습니다.
Electron + Vue.js 애플리케이션 만들기
1. Electron 프로젝트 생성하기(Create Electron Application Project)
2. Electron IPC 통신(Electron Architecture, IPC Main/IPC Renderer)
3. TCP Router 기능 구현(Implements TCP Router Communication)
5. 환경설정 구현하기(Preferences Window)
6. 메뉴 사용하기(Application Menu & Context Menu)
7. 시스템 트레이 사용 및 창 최소화(System Tray & Minimize to Tray)
8. Bootstrap Vue와 Font Awesome 사용하기(Using Bootstrap Vue & Font Awesome)
9. Dark Layout과 Frameless Window(Dark Layout & Frameless Window)
10. 빌드 및 배포, 자동 업데이트(Build, Auto Updater)
이번 포스트에서는 환경설정을 화면을 구성하고 환경설정을 처리하는 로직을 구현합니다. 환경설정정보를 간단하게 구성해보고 추후 GUI 라이브러리(Bootstrap Vue) 설치 후에 내용을 확대하여 적용하겠습니다.
환경설정 창 띄우기
환경설정 관리(main 프로세스) 객체생성하기
환경설정을 관리하는 객체를 아래와 같이 생성합니다. 환경설정 저장은 preferences 모듈을 추가하였습니다.
import { BrowserWindow, ipcMain, dialog } from "electron";
import CommonUtils from "../shared/common-utils";
const iswin = process.platform === "win32";
// https://www.npmjs.com/package/preferences
const Preferences = require("preferences");
const winURL = process.env.NODE_ENV === "development"
? `http://localhost:9080/#/preferences`
: `file://${__dirname}/preferences.html`;
export default (() => {
class PreferencesManager {
constructor(win) {
this.win = win;
this.prefsWindow = null;
this.preferences = new Preferences("kr.ejsoft.tcp.router", {
common : {
autostartup: true,
minimizestart: false,
autoservice: true,
minimizeToTray: true,
closeToTray: true,
},
routers : [
{
listen : 3307,
host : "192.168.1.24",
port : 3306
},
{
listen : 9010,
host : "localhost",
port : 9090
},
{
listen : 9020,
host : "127.0.0.1",
port : 9090
}
]
}, {
encrypt: true
});
ipcMain.on("open-preperences", (event, data) => {
this.show();
});
ipcMain.on("request-preferences", (event, data) => {
// const target = (data.target === "prefs") ? this.prefsWindow : this.win;
const target = event.sender;
// console.log("REQUEST_PREFERENCES", prefs);
target.webContents.send("response-preferences", this.preferences);
});
ipcMain.on("save-preferences", (event, data) => {
Object.keys(data).forEach((key) => {
this.preferences[key] = data[key];
});
// console.log("SAVE_PREFERENCES", prefs);
this.preferences.save();
this.win.webContents.send("changed-preferences", this.preferences);
this.prefsWindow.webContents.send("changed-preferences", this.preferences);
});
}
get() {
return this.preferences;
}
show() {
if(this.visible === true && this.prefsWindow) {
this.prefsWindow.show();
return;
}
const appicon = CommonUtils.icon(64);
const style = {
parent: this.win,
icon: appicon,
modal: true,
width: 600,
height: 360,
// frame: false,
resizable: false,
minimizable: false,
maximizable: false,
useContentSize: true,
webPreferences : {
// devTools : false,
}
};
switch(process.platform) {
case "darwin":
style.frame = true;
style.maximizable = false;
break;
default:
}
this.prefsWindow = new BrowserWindow(style);
this.prefsWindow.loadURL(winURL);
// dialog.once('ready-to-show', () => {
// dialog.show();
// });
this.prefsWindow.on("closed", () => {
// null;
this.visible = false;
this.prefsWindow = null;
});
this.visible = true;
}
}
return {
getInstance(win) {
if(!PreferencesManager.instance) {
if(!win) {
throw new Error("윈도우 객체의 인스턴스가 필요합니다.");
}
PreferencesManager.instance = new PreferencesManager(win);
}
return PreferencesManager.instance;
},
get() {
if(!PreferencesManager.instance) {
throw new Error("환경설정관리자의 초기화가 먼저 필요합니다.");
}
const inst = PreferencesManager.instance;
return inst.get();
},
show() {
if(!PreferencesManager.instance) {
throw new Error("환경설정관리자의 초기화가 먼저 필요합니다.");
}
const inst = PreferencesManager.instance;
return inst.show();
}
};
})();
환경설정 화면(renderer 프로세스) 생성하기
환경설정 읽기, 저장, 수정하는 화면을 아래와 같이 구성합니다.
<template>
<div id="wrapper">
<main>
<div class="left-side">
<span class="title">TCP Router</span>
<div class="items">
<div class="item" v-for="(item, index) in preferences.routers"
:key="index"
:class="{'selected': selectedIndex == index}"
@click.prevent.stop="setselectedIndex(index)"
>
<div class="name">{{ item.listen }}, {{item.host}}:{{item.port}}</div>
</div>
</div>
</div>
<div class="right-side">
<div class="title">Router Information</div>
<div class="information" ref="information">
<div class="info-row">
<div class="info-title">Listen</div>
<div class="info-value"><input type="text" name="listen" v-model="selectedItem.listen" /></div>
</div>
<div class="info-row">
<div class="info-title">Host</div>
<div class="info-value"><input type="text" name="host" v-model="selectedItem.host" /></div>
</div>
<div class="info-row">
<div class="info-title">Port :</div>
<div class="info-value"><input type="text" name="port" v-model="selectedItem.port" /></div>
</div>
</div>
<div class="info-control">
<button class="alt" @click="handleAdd" v-if="selectedIndex >= 0">Modify</button>
<button class="alt" @click="handleAdd" v-else>Add</button>
<button class="alt" @click="handleDelete" v-if="selectedIndex >= 0">Delete</button>
<button @click="handleReset">Reset</button>
</div>
</div>
</main>
<footer class="controlbox">
<div class="left-side">
<div class="doc">
<button class="alt" @click="handleNew">New Router</button>
</div>
</div>
<div class="right-side">
<div class="doc">
<button @click="handleSave">Save</button>
<button class="alt" @click="handleClose">Close</button>
</div>
</div>
</footer>
</div>
</template>
<script>
import { ipcRenderer, remote } from 'electron'
export default {
name: 'preferences-page',
components: {
},
data() {
return {
selectedIndex: -1,
selectedItem: {},
preferences: {},
}
},
created() {
console.log("created....");
ipcRenderer.on("response-preferences", (event, args) => {
console.log(args);
this.preferences = args;
});
ipcRenderer.on("changed-preferences", (event, args) => {
console.log(args);
this.preferences = args;
});
ipcRenderer.send("request-preferences");
},
mounted() {
},
destroyed() {
},
methods: {
setselectedIndex(index) {
this.selectedIndex = index;
this.bindData(index);
},
clearselectedIndex() {
this.selectedIndex = -1;
this.selectedItem = {};
},
bindData(index) {
let router = {};
if(index >= 0) {
router = this.preferences.routers ? this.preferences.routers[index] : null;
}
router = router ? router : {listen:0, host:"", port:0}
const {listen, host, port} = router;
this.selectedItem = {listen, host, port};
},
handleNew() {
this.selectedIndex = -1;
this.selectedItem = {};
},
handleAdd() {
if(!this.preferences.routers) {
this.preferences.routers = [];
}
const {listen, host, port} = this.selectedItem;
if(this.selectedIndex >= 0) {
this.preferences.routers.splice(
this.selectedIndex,
1, {
listen, host, port
}
);
} else {
this.preferences.routers.push({
listen, host, port
});
}
this.selectedIndex = -1;
this.selectedItem = {};
},
handleDelete() {
if(this.selectedIndex >= 0) {
this.preferences.routers.splice(this.selectedIndex, 1);
}
this.selectedIndex = -1;
this.selectedItem = {};
},
handleReset() {
this.bindData(this.selectedIndex);
},
handleSave() {
ipcRenderer.send("save-preferences", this.preferences);
this.handleClose();
},
handleClose() {
const window = remote.getCurrentWindow();
window.close();
},
}
}
</script>
<style scoped>
......(이하생략)......
라우팅경로 추가
환경설정화면을 라우팅경로에 추가합니다. /src/renderer/router/index.js 에 환경설정 화면을 등록하고 meta에 다이얼로그의 제목을 등록하여 창이름을 변경합니다.
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
let router = new Router({
routes: [
{
path: '/',
name: 'main-page',
meta: {
title : "TCP Socket Router"
},
component: require('@/views/MainPage').default
},
{
path: "/preferences",
name: "preferences-page",
meta: {
layout: "dialog",
title : "Preferences"
},
component: require("@/views/Preperences").default
},
]
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title
next()
})
export default router;
환경설정 초기화 코드 등록
/src/main/index.js 에서 init() 함수에 환경설정 초기화코드를 삽입니다. 초기화이후에는 애플리케이션에서 싱글톤으로 환경설정 인스턴스가 존재합니다.
import { app, ipcMain } from 'electron'
import MainWindow from './window/main-window'
import PreferencesManager from "./window/preferences-window";
import launchTCPRouter from './shared/tcp-router-launcher'
let mainWindow = null;
function init() {
mainWindow = MainWindow.create();
mainWindow.on('closed', () => {
mainWindow = null
});
// // 환경설정 관리자
app.preference = PreferencesManager.getInstance(mainWindow);
TCPRouterLauncher.getInstance().execute();
......(이하생략)......
환경설정 창 구성
아래의 그림과 같이 왼쪽에는 TCP Router 목록을 오른쪽에는 정보를 편집하는 화면으로 구성합니다. 일반적인 UI입니다.설명은 생략하도록 하겠습니다.
참고자료
소스코드
본 포스트 관련 소스코드는 여기에서 다운로드 가능합니다.
'Development > Node.js, Vue.js, Electron.js' 카테고리의 다른 글
[TCP Socket Router #07] System Tray & Minimize to Tray (0) | 2020.02.28 |
---|---|
[TCP Socket Router #06] Application Menu & Context Menu (0) | 2020.02.27 |
[TCP Socket Router #04] Main Window GUI (0) | 2020.02.26 |
[TCP Socket Router #03] Implements TCP Router Communication (0) | 2020.02.25 |
[TCP Socket Router #02] Electron Architecture, IPC Main/IPC Renderer (0) | 2020.02.24 |