본문으로 건너뛰기

빠른 시작

이곳에서는 electron/electron-quick-start와 유사하게 일렉트론에서 간단한 Hello World 앱을 만드는 과정을 안내합니다.

이 자습서를 끝까지 따라하면, 앱은 실행 중인 크로미움, 노드, 일렉트론의 버전에 대한 정보가 포함된 웹 페이지를 표시하는 브라우저 창을 열 것입니다.

준비하기​

일렉트론을 사용하려면 노드를 설치해야 합니다. 가능한 최신 LTS 버전을 사용하는 것이 좋습니다.

팁

여러분의 플랫폼에 맞는 설치 프로그램을 사용하여 노드를 설치하세요. 그렇지 않으면 다른 개발 도구와 호환되지 않는 문제가 발생할 수 있습니다.

노드가 올바르게 설치되었는지 확인하려면 터미널 클라이언트에 다음 명령을 입력하세요.

sh
node -v
npm -v
sh
node -v
npm -v

명령은 노드와 npm의 버전을 출력해야 합니다.

참고

일렉트론은 노드를 이진에 내장하므로, 코드를 실행하는 노드의 버전은 시스템에서 실행 중인 노드의 버전과 관련이 없습니다.

애플리케이션 만들기​

프로젝트의 기초 세우기​

일렉트론 앱은 다른 노드 프로젝트와 동일한 일반적인 구조를 갖습니다. 폴더를 만들고 npm 패키지를 초기화하여 프로젝트를 시작하세요.

sh
mkdir my-electron-app && cd my-electron-app
npm init
sh
mkdir my-electron-app && cd my-electron-app
npm init

대화형 init 명령은 설정에서 일부 필드를 지정하라는 메시지를 표시합니다. 이 자습서의 목적을 위해 따라야 할 몇 가지 규칙이 있습니다.

  • 진입점은 main.js여야 합니다.
  • author와 description에는 아무 값이나 넣을 수 있지만 앱 패키징에 필요합니다.

package.json 파일은 다음과 같아야 합니다.

json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT"
}
json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT"
}

이제 electron 패키지를 앱의 devDependencies에 설치합니다.

sh
npm install --save-dev electron
sh
npm install --save-dev electron
참고

일렉트론을 설치하는 과정에서 문제가 생겼다면 고급 설치 안내서를 참고하세요.

마지막으로 일렉트론 실행을 위한 설정이 필요합니다. package.json 설정의 scripts 필드에 다음과 같이 start 명령을 추가합니다.

json
{
"scripts": {
"start": "electron ."
}
}
json
{
"scripts": {
"start": "electron ."
}
}

이 start 명령을 사용하면 개발 모드에서 앱을 열 수 있습니다.

sh
npm start
sh
npm start
참고

이 스크립트는 일렉트론 프로젝트의 루트 폴더에서 실행되도록 지시합니다. 이 단계의 앱에서는 실행할 앱을 찾을 수 없다는 오류가 바로 발생합니다.

주 프로세스 실행하기​

일렉트론 애플리케이션의 진입점은 main 스크립트입니다. 이 스크립트는 전체 노드 환경에서 실행됩니다. 스크립트는 앱의 수명 주기를 제어하고, 네이티브 인터페이스를 표시하고, 권한 있는 작업을 수행하고, 렌더러 프로세스를 관리하는 주 프로세스(main process)를 제어합니다. (자세한 내용은 나중에 설명함)

실행 중에 일렉트론은 앱의 기초 세우기 단계에서 설정했던 package.json의 main 필드의 값을 이용하여 스크립트를 찾습니다.

main 스크립트를 초기화하려면 프로젝트의 루트 폴더에 main.js라는 빈 파일을 만드세요.

참고

이 시점에서 start 스크립트를 다시 실행하면 앱에서 더 이상 오류가 발생하지 않습니다! 그러나 아직 main.js에 코드를 추가하지 않았기 때문에 아무 작업도 수행하지 않습니다.

웹 페이지 만들기​

앱 창을 만들기 전에 창에 로드할 콘텐츠를 만들어야 합니다. 일렉트론에서 각 창은 로컬 HTML 파일 또는 원격 URL에서 로드할 수 있는 웹 콘텐츠를 표시합니다.

이 자습서에서는 로컬 HTML 파일을 사용합니다. 프로젝트의 루트 폴더에 다음 index.html 파일을 만드세요.

html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium
<span id="chrome-version"></span>, and Electron
<span id="electron-version"></span>.
</body>
</html>
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium
<span id="chrome-version"></span>, and Electron
<span id="electron-version"></span>.
</body>
</html>
참고

이 HTML 문서를 보면 본문의 텍스트에 버전 값이 누락된 것을 확인할 수 있습니다. 나중에 자바스크립트를 사용하여 수동으로 삽입할 것입니다.

브라우저 창에서 웹 페이지 열기​

이제 웹 페이지가 생겼으니 앱 창에 로드해 보겠습니다. 여기에는 두 개의 일렉트론 모듈이 필요합니다.

  • 앱의 이벤트 수명 주기를 제어하는 app 모듈
  • 앱 창을 만들고 관리하는 BrowserWindow 모듈

주 프로세스는 노드를 실행하므로 파일의 맨 위에서 코먼JS 모듈로 가져오는 것이 가능합니다.

js
const { app, BrowserWindow } = require('electron');
js
const { app, BrowserWindow } = require('electron');

그런 다음 index.html을 새로운 BrowserWindow 인스턴스로 로드하는 createWindow() 함수를 추가합니다.

js
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
});
win.loadFile('index.html');
};
js
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
});
win.loadFile('index.html');
};

이제 이 createWindow() 함수를 호출하여 창을 엽니다.

일렉트론에서 브라우저 창은 app 모듈의 ready 이벤트가 발생한 후에만 생성할 수 있습니다. app.whenReady() API를 사용하여 이 이벤트를 기다릴 수 있습니다. whenReady()가 프라미스를 이행한 후 createWindow()를 호출합니다.

js
app.whenReady().then(() => {
createWindow();
});
js
app.whenReady().then(() => {
createWindow();
});
참고

이 시점에서 일렉트론 앱은 웹 페이지를 표시하는 창을 성공적으로 열어야 합니다!

창의 수명 주기 관리하기​

이제 브라우저 창을 열 수 있습니다. 하지만 각 플랫폼에 더 네이티브한 느낌을 주려면 몇 가지 상용구 코드가 추가로 필요합니다. 앱 창은 각 OS에서 다르게 작동하며, 일렉트론은 이러한 규칙을 앱에 구현할 책임을 개발자에게 부여합니다.

일반적으로 전역 process의 platform 프로퍼티를 사용하여 특정 운영 체제용 코드를 실행할 수 있습니다.

모든 창이 닫힐 때 앱 종료하기 (윈도우 및 리눅스)​

윈도우와 리눅스에서 모든 창을 종료하면 일반적으로 앱이 완전히 종료됩니다.

이를 구현하려면, app 모듈의 'window-all-closed' 이벤트를 수신하고, 사용자가 맥OS(darwin)를 사용하지 않는다면 app.quit()를 호출합니다.

js
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
js
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});

열려 있는 창이 없으면 창 열기 (맥OS)​

리눅스와 윈도우 앱은 열려 있는 창이 없을 때 종료됩니다. 반면 맥OS 앱은 일반적으로 창이 열려 있지 않아도 계속 실행되며, 사용 가능한 창이 없을 때 앱을 활성화하면 새 창이 열립니다.

이 기능을 구현하려면, app 모듈의 activate 이벤트를 수신하고, 브라우저 창이 열려 있지 않으면 기존의 createWindow() 메서드를 호출합니다.

ready 이벤트 전에는 창을 만들 수 없으므로, 앱이 초기화된 후에만 activate 이벤트를 수신해야 합니다. 이를 위해서는 기존의 whenReady() 콜백 내에서 이벤트 수신기를 연결하면 됩니다.

js
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
js
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
참고

이 시점에서 창 조작이 완전히 작동해야 합니다!

사전 로드 스크립트를 사용하여 렌더러에서 노드에 접근하기​

이제 마지막으로 할 일은 일렉트론의 버전 번호와 종속성을 웹 페이지에 출력하는 것입니다.

이 정보에 접근하는 것은 노드의 전역 process 객체를 통해 주 프로세스에서 간단하게 수행할 수 있습니다. 그러나 주 프로세서에서 렌더러의 document 컨텍스트에 접근할 수는 없기 때문에, DOM을 편집할 수는 없습니다. 그들은 완전히 다른 프로세스에 있습니다!

참고

일렉트론의 프로세스에 대해 더 자세히 살펴보려면 프로세스 모델 문서를 참고하세요.

이때 렌더러에 사전 로드(preload) 스크립트를 첨부하는 것이 편리합니다. 사전 로드 스크립트는 렌더러 프로세스가 로드되기 전에 실행되며 렌더러 전역(예: window 및 document)과 노드 환경에 모두 접근할 수 있습니다.

다음과 같이 preload.js라는 새 스크립트를 만드세요.

js
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});
js
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});

이 코드는 노드의 process.versions 객체에 접근하고, 기본적인 replaceText 도우미 함수를 실행하여 버전 번호를 HTML 문서에 삽입합니다.

이 스크립트를 렌더러 프로세스에 첨부하려면, 사전 로드 스크립트의 경로를 기존 BrowserWindow 생성자의 webPreferences.preload 옵션에 전달합니다.

js
// 파일 상단에 노드의 path 모듈을 포함합니다.
const path = require('path');
// 기존의 createWindow() 함수를 수정합니다.
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('index.html');
};
// ...
js
// 파일 상단에 노드의 path 모듈을 포함합니다.
const path = require('path');
// 기존의 createWindow() 함수를 수정합니다.
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('index.html');
};
// ...

여기에 사용되는 노드 개념이 두 가지 있습니다.

  • __dirname 문자열은 현재 실행 중인 스크립트의 경로(이 경우 프로젝트의 루트 폴더)를 가리킵니다.
  • path.join API는 여러 경로 조각을 결합하여 모든 플랫폼에서 작동하는 결합된 경로 문자열을 생성합니다.

현재 실행 중인 자바스크립트 파일에 상대적인 경로를 사용하므로, 개발 모드와 패키지 모드 모두에서 상대 경로가 작동합니다.

보너스: 웹 콘텐츠에 기능 추가하기​

이 시점에서 애플리케이션에 더 많은 기능을 추가하는 방법이 궁금할 수 있습니다.

웹 콘텐츠와의 상호 작용을 위해 렌더러 프로세스에 스크립트를 추가하려고 합니다. 렌더러는 보통의 웹 환경에서 실행되기 때문에, index.html 파일의 닫는 </body> 태그 바로 앞에 <script> 태그를 추가하여 원하는 스크립트를 넣을 수 있습니다.

html
<script src="./renderer.js"></script>
html
<script src="./renderer.js"></script>

renderer.js에 포함된 코드는 일반적인 프런트엔드 개발에 사용하는 것과 동일한 자바스크립트 API 및 도구를 사용할 수 있습니다. 예를 들어 웹팩을 사용하여 코드를 묶고 축소하거나, 리액트를 사용하여 사용자 인터페이스를 관리할 수 있습니다.

요약​

위의 단계를 따른 후에는 일렉트론 애플리케이션이 다음과 같이 완전히 작동해야 합니다.

전체 코드는 다음과 같습니다.

main.js
js
// 앱의 수명을 제어하고 기본 브라우저 창을 만드는 모듈입니다.
const { app, BrowserWindow } = require('electron');
const path = require('path');
const createWindow = () => {
// 브라우저 창을 만듭니다.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// 그리고 앱의 'index.html'을 로드합니다.
mainWindow.loadFile('index.html');
// 개발도구를 엽니다.
// mainWindow.webContents.openDevTools()
};
// 이 메서드는 일렉트론이 초기화를 완료하고 브라우저 창을 생성할 준비가 되면 호출됩니다.
// 일부 API는 이 이벤트가 발생한 후에만 사용할 수 있습니다.
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
// 맥OS에서는 독 아이콘을 클릭하고 열려 있는 다른 창이 없을 때
// 앱에서 창을 다시 만드는 것이 일반적입니다.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 맥OS가 아닐 때 모든 창이 닫히면 종료합니다.
// 이 경우에는 사용자가 'Cmd + Q'로 명시적으로 종료할 때까지
// 앱과 해당 메뉴 표시줄이 활성 상태로 유지되는 것이 일반적입니다.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// 이 파일에는 앱의 특정한 주 프로세스 코드의 나머지 부분이 포함될 수 있습니다. 코드를 별도의 파일에 넣고 여기에서 요구(require)할 수도 있습니다.
main.js
js
// 앱의 수명을 제어하고 기본 브라우저 창을 만드는 모듈입니다.
const { app, BrowserWindow } = require('electron');
const path = require('path');
const createWindow = () => {
// 브라우저 창을 만듭니다.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// 그리고 앱의 'index.html'을 로드합니다.
mainWindow.loadFile('index.html');
// 개발도구를 엽니다.
// mainWindow.webContents.openDevTools()
};
// 이 메서드는 일렉트론이 초기화를 완료하고 브라우저 창을 생성할 준비가 되면 호출됩니다.
// 일부 API는 이 이벤트가 발생한 후에만 사용할 수 있습니다.
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
// 맥OS에서는 독 아이콘을 클릭하고 열려 있는 다른 창이 없을 때
// 앱에서 창을 다시 만드는 것이 일반적입니다.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 맥OS가 아닐 때 모든 창이 닫히면 종료합니다.
// 이 경우에는 사용자가 'Cmd + Q'로 명시적으로 종료할 때까지
// 앱과 해당 메뉴 표시줄이 활성 상태로 유지되는 것이 일반적입니다.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// 이 파일에는 앱의 특정한 주 프로세스 코드의 나머지 부분이 포함될 수 있습니다. 코드를 별도의 파일에 넣고 여기에서 요구(require)할 수도 있습니다.
preload.js
js
// 사전 로드 프로세스에서 모든 노드 API를 사용할 수 있습니다.
// 크롬 확장 프로그램과 동일한 샌드박스를 가지고 있습니다.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});
preload.js
js
// 사전 로드 프로세스에서 모든 노드 API를 사용할 수 있습니다.
// 크롬 확장 프로그램과 동일한 샌드박스를 가지고 있습니다.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});
index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium
<span id="chrome-version"></span>, and Electron
<span id="electron-version"></span>.
<!-- 이 프로세스에서 다른 파일을 실행하도록 요구할 수도 있습니다. -->
<script src="./renderer.js"></script>
</body>
</html>
index.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium
<span id="chrome-version"></span>, and Electron
<span id="electron-version"></span>.
<!-- 이 프로세스에서 다른 파일을 실행하도록 요구할 수도 있습니다. -->
<script src="./renderer.js"></script>
</body>
</html>
피들에서 열기

수행한 모든 단계를 요약하면 다음과 같습니다.

  • 노드 앱을 부트스트랩하고 일렉트론을 종속성으로 추가했습니다.
  • 앱을 제어하고 노드 환경에서 실행되는 주 프로세스를 실행하는 main.js 스크립트를 만들었습니다. 이 스크립트에서는 일렉트론의 app과 BrowserWindow 모듈을 사용하여 별도의 프로세스(렌더러)에서 웹 콘텐츠를 표시하는 브라우저 창을 만들었습니다.
  • 렌더러에서 특정 노드 기능에 접근하기 위해 BrowserWindow 생성자에 사전 로드 스크립트를 첨부했습니다.

애플리케이션 패키징과 배포​

새로 만든 앱을 배포하는 가장 빠른 방법은 일렉트론 포지를 사용하는 것입니다.

먼저 일렉트론 포지를 앱의 개발 종속성으로 추가하고 import 명령을 사용하여 포지의 기초 세우기를 설정합니다.

sh
npm install --save-dev @electron-forge/cli
npx electron-forge import
✔ Checking your system
✔ Initializing Git Repository
✔ Writing modified package.json file
✔ Installing dependencies
✔ Writing modified package.json file
✔ Fixing .gitignore
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
Thanks for using "electron-forge"!!!
sh
npm install --save-dev @electron-forge/cli
npx electron-forge import
✔ Checking your system
✔ Initializing Git Repository
✔ Writing modified package.json file
✔ Installing dependencies
✔ Writing modified package.json file
✔ Fixing .gitignore
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
Thanks for using "electron-forge"!!!

그런 다음 포지의 make 명령을 사용하여 배포 파일을 만듭니다.

sh
npm run make
> my-electron-app@1.0.0 make /my-electron-app
> electron-forge make
✔ Checking your system
✔ Resolving Forge Config
We need to package your application before we can make it
✔ Preparing to Package Application for arch: x64
✔ Preparing native dependencies
✔ Packaging Application
Making for the following targets: zip
✔ Making for target: zip - On platform: darwin - For arch: x64
sh
npm run make
> my-electron-app@1.0.0 make /my-electron-app
> electron-forge make
✔ Checking your system
✔ Resolving Forge Config
We need to package your application before we can make it
✔ Preparing to Package Application for arch: x64
✔ Preparing native dependencies
✔ Packaging Application
Making for the following targets: zip
✔ Making for target: zip - On platform: darwin - For arch: x64

일렉트론 포지는 패키지가 위치할 out 폴더를 생성합니다.

// 맥OS의 예시
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
// 맥OS의 예시
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app