-
[ Electrion + Vue ] 커스텀 Navigation Bar ( Title Bar 만들기 )Desktop App/Electron + Vue 2025. 6. 15. 01:41반응형
electron app을 만들다 보면 맥과 윈도우 둘다 고려했을때 윗 부분이 다르기때문에 똑같이 만들어주기 위해 직접 네비게이션바를 만들어주어야한다.
Title Bar Component
우선 타이틀바 컴포넌트를 만들어 앱의 메인 화면들이 나오는 곳에 맨 상단에 붙힌다. 항상 상단에는 타이틀이 나오도록
<script setup lang="ts"> </script> <template> <div class="app-title-wrapper"> <div class="title-name"> 타이틀 </div> <div class="title-control"> <div class="symbol yellow" @click="onClickMinimize"/> <div class="symbol red" @click="onClickClose"/> </div> </div> </template> <style scoped lang="scss"> .app-title-wrapper { width: 100%; height: 100%; display: flex; align-items: center; padding: 0 10px; background: var(--app-theme-background); color: var(--app-theme-titlebar-text-color); border-bottom: 0.5px solid var(--app-theme-titlebar-background); .title-name { flex: 1; display: flex; align-items: center; justify-content: center; } .title-control { display: flex; gap: 6px; flex-shrink: 0; .symbol { width: 11px; height: 11px; border-radius: 50%; cursor: pointer; &.red { background: #FE5F57; } &.yellow { background: #FEBC2E; } } } } </style>
최대화 아이콘은 사용하지않고, 최소화와 닫기 아이콘만 사용할려고 해서 두개만 추가했다.
preload.js
electron의 닫기와 최소화 기능 구현을 위해 preload를 만들어 electron config에 붙혀준다.
더보기Electron에서 preload.js는 렌더러 프로세스(=브라우저 환경, 예: Vue/React)와 메인 프로세스(Node.js/Electron API) 사이를 안전하게 연결해주는 브리지 역할을 한다.
따라서 preload에 최소화와 닫기 아이콘을 눌렀을때 실행될 브리지를 만들어준다.
이러면 vue에서 minimizeWindow를 실행시키면 electron의 'minimize-window'로 메시지를 보내준다.
const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { minimizeWindow: () => ipcRenderer.send('minimize-window'), closeWindow: () => ipcRenderer.send('close-window') });
이때 preload는 js다 보니 실제 사용할때 vue에서는 타입스크립트 에러가 발생한다.
따라서 types 폴더 안에 electron 타입을 만들어서 타입 에러가 안나오도록 타입을 지정
export interface ElectronAPI { minimizeWindow: () => void; closeWindow: () => void; } // 전역 선언 declare global { interface Window { electronAPI: ElectronAPI; } }
이후 electron의 main.js에 해당 preload.js를 추가시켜준다.
import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); let MainWin; function createWindow() { MainWin = new BrowserWindow({ width: 1024, height: 512, webPreferences: { preload: join(__dirname, 'preload.js'), // preload 위치 확인 contextIsolation: true, nodeIntegration: false, }, //... } }
그럼 이제 electron에서 최소화와 닫기 기능인 ipc를 등록한다.
이러면 preload에서 ipcRenderer.send가 실행되면 각각의 맞는 명칭의 ipc에 실행된다.
이제 여기서 frame: false로 설정하면 기존의 타이틀 바가 사라지게 된다. 타이틀 바를 커스텀을 할때는 이 속성을 무조건 false로 해야한다.
import { app, ipcMain ,BrowserWindow } from 'electron'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); let MainWin; function createWindow() { MainWin = new BrowserWindow({ width: 1024, height: 512, webPreferences: { preload: join(__dirname, 'preload.js'), // preload 위치 확인 contextIsolation: true, nodeIntegration: false, }, frame: false // 타이틀바 커스텀 시 필요 }); const isDev = !app.isPackaged; if (isDev) { MainWin.loadURL('http://localhost:5000'); // Vite dev server } else { MainWin.loadFile(join(__dirname, '../dist/index.html')); // 정적 파일 } ipcMain.on('minimize-window', () => { if (MainWin) MainWin.minimize(); }); ipcMain.on('close-window', () => { if (MainWin) MainWin.close(); }); }
Vue
렌더러 프로세스인 vue에서 preload에 등록한 minimize와 close를 실행시키면 electron app 최소화와 닫기 기능이 정상적으로 작동한다.
<script setup lang="ts"> const onClickMinimize = () => { window.electronAPI?.minimizeWindow() } const onClickClose = () => { window.electronAPI?.closeWindow() } </script> <template> <div class="app-title-wrapper"> <div class="title-name"> 타이틀 </div> <div class="title-control"> <div class="symbol yellow" @click="onClickMinimize"/> <div class="symbol red" @click="onClickClose"/> </div> </div> </template>
이제 기존의 타이틀 바가 사라졌기때문에 만든 타이틀바를 눌러서 electron app을 드래그 앤 드롭을 할 수 있도록 처리를해야한다.
이때 electron의 main에서 frame을 false가 필수인 이유이다.
-webkit-app-region: drag;
style에 -webkit-app-region: drag; 만 넣어주면 해당 영역을 눌러서 드래그 앤 드롭을 할 수 있다.
이때 닫기나 최소화 아이콘을 눌렀을때는 드래그 앤 드롭을 막기위해 no-drag로 드래그 앤 드롭을 막는다.
<template> <div class="app-title-wrapper"> <div class="title-name"> 타이틀 </div> <div class="title-control"> <div class="symbol yellow" @click="onClickMinimize"/> <div class="symbol red" @click="onClickClose"/> </div> </div> </template> <style scoped lang="scss"> .app-title-wrapper { width: 100%; height: 100%; display: flex; align-items: center; padding: 0 10px; background: var(--app-theme-background); color: var(--app-theme-titlebar-text-color); border-bottom: 0.5px solid var(--app-theme-titlebar-background); -webkit-app-region: drag; .title-name { flex: 1; display: flex; align-items: center; justify-content: center; } .title-control { display: flex; gap: 6px; flex-shrink: 0; .symbol { width: 11px; height: 11px; border-radius: 50%; cursor: pointer; -webkit-app-region: no-drag; &.red { background: #FE5F57; } &.yellow { background: #FEBC2E; } } } } </style>
그럼 최종적으로 기존의 타이틀 바는 사라지고 직접 만든 타이틀 바가 생성되며 최소화 닫기 그리고 드래그 앤 드롭 기능이 정상적으로 된다.
728x90반응형'Desktop App > Electron + Vue' 카테고리의 다른 글
[ Electron + Vue ] 초기 환경 세팅 (0) 2025.05.06