프로젝트 구조 설계
TypeScript와 Electron을 함께 사용한다면 컴파일 산출물과 원천 소스를 명확히 격리하는 디렉토리 레이아웃이 필수적입니다. 다음과 같은 계층적 구조를 기반으로 빈 파일을 생성합니다.
my-electron-app/
├── dist/ // tsc 컴파일된 번들 및 JS 파일 위치
├── src/ // TypeScript 원천 코드 루트
│ ├── main.ts // 메인 프로세스 진입점
│ ├── preload.ts // 보안 브릿지 역할 스크립트
│ └── renderer.ts // UI 렌더링 및 이벤트 처리
├── index.html // 프레젠테이션 레이어 템플릿
├── package.json // 패키지 메타데이터 및 실행 스크립트
└── tsconfig.json // 타입스크립트 컴파일 옵션 정의
설정 파일 구성
1. package.json 관리
애플리케이션의 진입점을 컴파일 출력 경로로 지정하고, 일괄 빌드 및 런칭 명령어를 정의합니다.
{
"name": "ts-electron-starter",
"version": "1.0.0",
"description": "TypeScript + Electron 통합 샘플",
"main": "./dist/main.js",
"scripts": {
"build": "tsc",
"start": "npm run build && electron ./dist/main.js"
},
"devDependencies": {}
}
2. 의존성 설치
네트워크 가속용 미러 변수를 설정한 후, 필요한 패키지를 개발 환경 의존성으로 추가합니다.
# Windows PowerShell 또는 CMD 환경 변수 설정
$env:ELECTRON_MIRROR = "https://npmmirror.com/mirrors/electron/"
# npm 또는 yarn을 통한 설치
npm install --save-dev electron typescript @types/node
3. tsconfig.json 최적화
CommonJS 모듈 체계와 ES6 대상 타겟팅을 명시하며, 엄격한 타입 검사를 활성화합니다.
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
4. VS Code 디버깅 환경 (launch.json)
메인 프로세스와 렌더러 프로세스를 동시에 추종할 수 있도록 복합 구성 세트를 설정합니다.
{
"version": "0.2.0",
"compounds": [
{
"name": "Full App Debugger",
"configurations": ["Main Process", "Renderer Debug"],
"stopOnEntry": false
}
],
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Main Process",
"program": "${workspaceFolder}/node_modules/.bin/electron.cmd",
"args": [".", "--remote-debugging-port=9229"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
},
{
"type": "chrome",
"request": "attach",
"name": "Renderer Debug",
"port": 9229,
"webRoot": "${workspaceFolder}"
}
]
}
디버거 실행 패널에서 Full App Debugger 프로필을 선택하여 시작합니다.
핵심 로직 구현
1. main.ts (메인 프로세스)
애플리케이션 생명주기를 제어하고 브라우저 창을 관리합니다. IPC 채널을 통해 렌더러에게 데이터를 동기식으로 제공합니다.
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
let activeWindow: BrowserWindow | null = null;
function createMainWindow(): void {
activeWindow = new BrowserWindow({
width: 1024,
height: 768,
webPreferences: {
preload: path.join(__dirname, 'preload-bundle.js'),
contextIsolation: true,
nodeIntegration: false
}
});
activeWindow.loadFile(path.join(__dirname, '..', 'index.html'));
activeWindow.on('closed', () => { activeWindow = null; });
}
app.whenReady().then(createMainWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createMainWindow();
});
// Renderer 요청 처리 인터셉터
ipcMain.handle('query-system-metadata', async () => {
return {
appTitle: app.getName(),
versionNumber: app.getVersion(),
platformInfo: process.platform
};
});
2. preload.ts (보안 브리지)
contextIsolation이 활성화된 현대적 Electron 환경에서 렌더러 프로세스에게 안전한 API 레이어를 노출합니다.
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('DesktopBridge', {
fetchSystemData: (): Promise<{ appTitle: string; versionNumber: string; platformInfo: string }> =>
ipcRenderer.invoke('query-system-metadata')
});
3. renderer.ts (UI 프로세스)
DOM 조작을 통해 비동기적으로 수신된 데이터를 화면에 렌더링합니다. 확장된 전역 타입을 사용하여 정적 검증을 수행합니다.
// 전역 확장형 타입 정의
interface WindowExtension extends Window {
DesktopBridge: {
fetchSystemData(): Promise<{ appTitle: string; versionNumber: string; platformInfo: string }>;
};
}
// 초기화 흐름
async function initUI(): Promise<void> {
try {
const data = await (window as WindowExtension).DesktopBridge.fetchSystemData();
const targetNode = document.getElementById('data-output');
if (targetNode) {
targetNode.innerHTML = `
<p><strong>애플리케이션:</strong> ${data.appTitle}</p>
<p><strong>빌드 버전:</strong> ${data.versionNumber}</p>
<p><strong>실행 환경:</strong> ${data.platformInfo}</p>
`;
}
} catch (err) {
console.error('브릿지 데이터 전송 실패:', err);
}
}
document.addEventListener('DOMContentLoaded', initUI);
4. index.html (렌더링 템플릿)
CSP(Content Security Policy) 전략을 적용하여 외부 코드 주입을 차단하고 로컬 번들을 연결합니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" />
<title>TS+Electron Demo</title>
</head>
<body>
<header><h1>데스크톱 환경 테스트</h1></header>
<main id="data-output">시스템 정보 로딩 중...</main>
<script src="./dist/renderer.js"></script>
</body>
</html>
컴파일 및 실행
모든 원천 코드가 완성되었으면 다음 단계로 진행하여 실행 가능한 상태까지 전환합니다.
# 타입스크립트 문법 검사 및 JavaScript 변환
npm run build
# 변환된 결과를 기반으로 Electron 애플리케이션 실행
npm start
명령어 실행이 완료되면 지정된 해상도의 윈도우가 생성되며, DOM에 바인딩된 시스템 메타데이터가 즉시 반영됩니다.