Capacitor 앱에서 데스크톱 앱을 빌드하려면, 동일한 웹 빌드(webDir)를 Electron 셸로 감싸면 됩니다: 앱을 여는 메인 프로세스와 BrowserWindow, 작은 데스크톱 API를 노출하는 preload script, 그리고 macOS, Windows, Linux용 설치 프로그램을 만드는 electron-builder를 사용합니다. UI와 비즈니스 로직은 iOS, Android와 계속 공유하고, 데스크톱 전용 기능은 Electron main process에 있습니다.
이 가이드는 2026년 기준 Electron에서의 Capacitor 현황, Capacitor 8과 호환되는 보안 설정, 플러그인 동작 방식, 패키징 및 서명, 그리고 새 설치 프로그램 없이 웹 업데이트를 배포하는 방법을 다룹니다.
2026년 Electron에서의 Capacitor 현황
수년 동안 표준 답변은 @capacitor-community/electron였으며, 이는 Electron을 Capacitor CLI 플랫폼에 연결하는 방식이었습니다. 이 프로젝트는 이제 자체 README에서 유지보수 중단으로 표시됩니다. 마지막 npm 릴리스인 5.0.1은 2023년 9월에 출시됐고, Capacitor 5.4 이상이 필요하며 Electron 26을 기준으로 빌드됐습니다. 그 이후 Electron은 여러 메이저 버전을 출시했고, 최신 3개 메이저 버전만 보안 수정을 받습니다.
기존 프로젝트에서는 여전히 사용할 수 있지만, Capacitor 8 앱을 기준으로 비교하면 다음과 같습니다:
| 접근 방식 | 장점 | 단점 |
|---|---|---|
| 자체 Electron shell (이 가이드) | 최신 Electron, 완전한 제어, 추가 추상화 없음 | main과 preload code를 약 60줄 작성 |
@capacitor-community/electron |
익숙한 cap 명령어 |
유지보수 중단, 구버전 Capacitor 및 Electron에 고정됨 |
Shell 방식은 생각보다 간단합니다. Capacitor가 이미 정적 웹 빌드를 생성하기 때문에, Electron은 이를 로드하기만 하면 됩니다.
Electron에서 플러그인 동작 방식
Electron의 렌더러는 Chromium이므로, Capacitor 앱은 브라우저에서처럼 실행됩니다. Capacitor 코어는 네이티브 브릿지가 없음을 감지하고 각 플러그인의 웹 구현으로 폴백합니다.
| 플러그인 유형 | Electron 지원 여부 | 해결 방법 |
|---|---|---|
| 웹 구현이 있는 플러그인 (Preferences, Share, Clipboard, 다수의 Capgo 플러그인) | 네, 웹 버전 사용 | 웹 동작 테스트 |
| 네이티브 전용 플러그인 (웹 구현 없음) | 아니요, 미구현으로 거부 | preload API 뒤에 Electron 대응 구현을 작성하세요 |
| 순수 JS 라이브러리 | 지원 | 없음 |
Capacitor.getPlatform() 반환합니다 'web' 이 설정에서 Capacitor.isNativePlatform() 반환합니다 false. preload 스크립트에서 명시적인 데스크톱 플래그를 노출하여 code가 깔끔하게 분기 처리할 수 있도록 합니다.
사전 요구사항
- 다음과 같은 Capacitor 앱의
bun run build정적 폴더를 출력합니다 (webDir에서capacitor.config.ts, 종종dist또는www). - Bun 및 현재 LTS Node.js가 필요합니다. Electron 툴링은 여전히 Node에서 실행되기 때문입니다.
- macOS 배포를 위한 Apple Developer ID 인증서가 필요합니다. Windows의 경우 code 서명 인증서가 필요합니다.
1단계: Electron과 packager 설치
bun add -d electron electron-builder
다음을 추가하세요: electron/ 프로젝트 루트의 폴더:
my-app/
capacitor.config.ts
dist/ # web build, shared with iOS and Android
electron/
main.ts
preload.ts
electron-builder.yml
2단계: 웹 빌드가 디스크에서 로드되도록 설정
Electron은 프로덕션에서 파일을 로드할 때 file:// 프로토콜을 사용합니다. 두 가지 설정이 중요합니다:
- 상대 경로 자산. Vite에서
base: './'을(를) 설정하여 빌드된index.html이(가) 참조하도록./assets/...를 전달하고/assets/.... - 라우팅. 다음과 같은 딥 링크
/settings파일로 존재하지 않습니다. 데스크톱 빌드에는 해시 라우팅을 사용하거나, 로드 후 클라이언트 사이드에서만 라우터가 내비게이션하도록 하세요.index.html.
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
base: './',
});
상대 경로도 iOS와 Android에서 작동하므로 별도의 빌드가 필요하지 않습니다.
3단계: 메인 프로세스 작성
// electron/main.ts
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import path from 'node:path';
const isDev = !app.isPackaged;
async function createWindow() {
const win = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 800,
minHeight: 600,
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
});
win.once('ready-to-show', () => win.show());
// Open external links in the default browser, never inside the app.
// Only allow https: so a script cannot launch file: or other protocol handlers.
win.webContents.setWindowOpenHandler(({ url }) => {
try {
if (new URL(url).protocol === 'https:') void shell.openExternal(url).catch(() => {});
} catch {
// invalid URL: ignore
}
return { action: 'deny' };
});
if (isDev) {
await win.loadURL('http://localhost:5173');
win.webContents.openDevTools({ mode: 'detach' });
} else {
await win.loadFile(path.join(__dirname, '..', '..', 'dist', 'index.html'));
}
}
ipcMain.handle('desktop:get-version', () => app.getVersion());
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
유지하세요 contextIsolation: true, nodeIntegration: false, 그리고 sandbox: true. 웹 앱이 서드파티 스크립트를 로드할 수 있으며, 어떤 스크립트도 Node.js에 접근할 수 없어야 합니다.
4단계: 프리로드에서 작은 데스크톱 API 노출
// electron/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('desktop', {
isElectron: true,
platform: process.platform,
getVersion: () => ipcRenderer.invoke('desktop:get-version') as Promise<string>,
});
웹 code에 입력하고, 플러그인을 사용하는 것과 동일한 방식으로 사용하세요:
// src/desktop.ts
declare global {
interface Window {
desktop?: {
isElectron: boolean;
platform: string;
getVersion: () => Promise<string>;
};
}
}
export const isDesktop = () => Boolean(window.desktop?.isElectron);
데스크톱에서 필요한 네이티브 전용 기능은 각각 하나씩 ipcMain.handle 메인 프로세스에 하나, 프리로드에 하나의 함수로 구현합니다. 렌더러는 신뢰할 수 없으므로 메인 프로세스에서 인수를 검증하세요.
5단계: 빌드 및 실행
Electron 파일을 Bun로 컴파일합니다. 프리로드는 샌드박스에서 실행되므로 단일 CommonJS 파일로 번들링하세요:
{
"main": "electron/dist/main.js",
"scripts": {
"build": "vite build",
"electron:compile": "bun build electron/main.ts --outdir electron/dist --target node --format cjs --external electron && bun build electron/preload.ts --outdir electron/dist --target node --format cjs --external electron",
"electron:dev": "bun run electron:compile && electron .",
"electron:pack": "bun run build && bun run electron:compile && electron-builder"
}
}
개발 중에는 터미널 하나에서 dev 서버를 시작하고 (bun run dev) 및 bun run electron:dev 다른 곳에서. 데스크톱 창에서 핫 리로드를 사용할 수 있습니다.
6단계: electron-builder로 설치 패키지 생성
# electron-builder.yml
appId: com.example.myapp
productName: My App
directories:
output: release
files:
- dist/**/*
- electron/dist/**/*
- package.json
mac:
target: [dmg, zip]
category: public.app-category.productivity
hardenedRuntime: true
win:
target: [nsis]
linux:
target: [AppImage, deb]
category: Utility
실행 bun run electron:pack. electron-builder는 실행 중인 OS에 맞춰 빌드합니다. 세 가지를 모두 만들려면 CI에서 macOS, Windows, Linux 러너 매트릭스를 사용하세요.
| OS | 일반적인 타깃 | 서명 |
|---|---|---|
| macOS가 | .dmg, .zip |
Developer ID 인증서와 notarization이 없으면 Gatekeeper가 앱을 차단합니다 |
| Windows | NSIS .exe, MSI |
Code 서명 인증서로 SmartScreen 경고 방지 |
| Linux | AppImage, .deb, .rpm |
선택 사항 |
Electron 앱은 Chromium과 Node.js를 포함하므로 모바일 빌드보다 용량이 큽니다. 타겟에 따라 설치 파일 크기는 약 80~150MB 정도입니다.
7단계: 새 설치 파일 없이 웹 업데이트 배포하기
Capacitor 앱의 변경 사항은 대부분 웹 변경입니다. 모바일에서는 이를 Capgo Live Update 사용하기로 배포할 수 있습니다. 데스크톱에서는 @capgo/electron-updater 채널, 내장 번들, 다운로드한 번들, 그리고 새 번들이 정상 작동하지 않을 때의 자동 롤백 등 동일한 모델로 같은 역할을 합니다. notifyAppReady().
bun add @capgo/electron-updater
업데이터가 로드할 번들을 선택할 수 있도록 메인 프로세스를 업데이트하세요:
// electron/main.ts (production branch)
import { ElectronUpdater, setupIPCHandlers, setupEventForwarding } from '@capgo/electron-updater';
const updater = new ElectronUpdater({
appId: 'com.example.myapp',
autoUpdate: true,
});
// inside createWindow(), instead of loading dist/index.html directly
const builtinPath = path.join(__dirname, '..', '..', 'dist', 'index.html');
await updater.initialize(win, builtinPath);
setupIPCHandlers(updater);
setupEventForwarding(updater, win);
await win.loadFile(updater.getCurrentBundlePath());
같은 preload 파일에 updater bridge를 추가하세요:
// electron/preload.ts (next to the desktop API)
import { exposeUpdaterAPI } from '@capgo/electron-updater/preload';
exposeUpdaterAPI();
그리고 데스크톱에서만 웹 앱에서 정상적으로 실행되는지 확인하세요:
import { isDesktop } from './desktop';
if (isDesktop()) {
const { requireUpdater } = await import('@capgo/electron-updater/renderer');
await requireUpdater().notifyAppReady();
}
모바일에서 쓰는 것과 동일한 CLI로 새 번들을 업로드하세요:
bun run build
bunx @capgo/cli@latest bundle upload --channel=production
웹 업데이트로는 Electron 자체나 메인 프로세스, preload를 변경할 수 없습니다. 이 경우에는 바이너리 auto-updater가 포함된 새 설치 파일을 배포해야 합니다. 관련 가이드는 Electron 자동 업데이트는 해당 경로를 다루고, electron-updater 플러그인 페이지에서는 및 문서 모든 옵션을 다룹니다.
문제 해결
패키지된 앱에서 빈 창이 표시됩니다. 에셋이 절대 경로를 사용합니다. 설정을 base: './' 하고 다시 빌드하세요. 패키지된 앱에서 DevTools를 열어 404 오류를 확인하세요. file:///assets/....
새로고침 후 라우트에서 빈 페이지가 표시됩니다. 라우터가 디스크에서 /route 를 로드하려고 했습니다. 데스크톱에서는 해시 라우팅으로 전환하세요.
require is not defined 또는 프리로드 오류가 발생합니다. 샌드박스된 프리로드는 런타임에 임의의 패키지를 로드할 수 없습니다. 위의 컴파일 스크립트처럼 단일 파일로 번들링하세요.
플러그인 호출이 “not implemented on web”으로 거부됩니다. 해당 플러그인에 웹 구현이 없습니다. 메인 프로세스에 IPC 핸들러를 추가하고 window.desktop.
macOS에서 앱이 손상되었거나 출처를 알 수 없는 개발자의 앱이라고 표시됩니다. 앱에 서명 및 노타리제이션이 적용되지 않았습니다. electron-builder에서 Developer ID 인증서와 노타리제이션을 구성하세요.
설치 직후 업데이트가 롤백됩니다. notifyAppReady() 타임아웃(기본 10초) 내에 호출되지 않았습니다. 앱 시작 시 초기에 호출하세요.
마무리
데스크톱에 도달하려면 전용 Capacitor 플랫폼 패키지가 필요하지 않습니다. 짧은 Electron main process, 타입이 지정된 API를 노출하는 preload, 상대 경로 자산, 그리고 electron-builder만 있으면 동일한 웹 빌드로 세 가지 운영체제용 설치 파일을 만들 수 있습니다. 추가 @capgo/electron-updater 을 추가하면 데스크톱 앱이 iOS 및 Android 빌드와 동일한 live update 워크플로우를 사용할 수 있습니다. 데스크톱 런타임 선택 중이라면 우리의 Electron vs Tauri 비교.