Electron Basic by Mark

2woongjae@gmail.com

1. Electron

Electron

  • Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS.

맞습니다. Electron 은 Framework 입니다.

Framework VS Library

  • 프로그램의 라이프 사이클
    • 컨트롤 할것이냐?
    • 컨트롤 당할 것이냐?
      • Electron 의 app 모듈에서 어플리케이션 시작 및 종료에 관한 이벤트를 받아 프로그래머가 작성한 코드를 실행합니다.
  • 라이브러리는 앱을 풍성하게 하기 위한 도움 요소입니다.
    • Electron + React
    • Electron + jQuery
  • 제어하는 부분이 다르다면 프레임워크를 한가지만 써야 하는것은 아닙니다.
    • Electron + Angular

Electron 은

Web Technologies 을 통해

Native Desktop App

을 만드는 Framework 입니다.

Web Technologies 란 ?

  • HTML, CSS, JavaScript 로 하는 모든 것 입니다.
  • 어떻게 하면 일렉트론을 잘 만들수 있어요?
    • 일렉트론 프레임워크에 대한 이해 (구조 및 API)
    • HTML, CSS, JavaScript  를 잘하면 됩니다.
      • 근데 이 세가지는 끝이 없다 할 정도 입니다.
      • HTML, CSS 는 크로미움에 맞는 스펙을 잘 알면 됩니다.
      • 하지만 JavaScript 는?
        • JavaScript 문법
        • ES6, ES7 문법
        • DOM, Node.js
        • jQuery / React / Angular / Vue ...
        • TypeScript ...
      • 계속해서 웹기술을 더 깊이 있게 배우는 것이 중요

일렉트론 프레임워크의 이해

node.js & chrome

  • 일렉트론은 이 두가지를 이용합니다.
  • Node.js 와 OS 의 이벤트 루프를 합쳐 ' Native UI ' 를 제어합니다.
    • Native UI ?
    • 어떤 노드를 쓰나요?
      • 노드 버전은 일렉트론 버전과 함께 올라갑니다.
      • 사용할 수 있는 자바스크립트 문법이나 노드 api 가 달라집니다.
  • 크롬 브라우저에 들어가는 크로미움을 개조한 라이브러리를 랜더링 할때 사용합니다.
    • 그냥 크롬 브라우저가 뜨는구나 하시면 됩니다.
      • 그래서 크롬의 버전 체크가 중요합니다.
        • 제공하는 ES 스펙
        • 크롬의 api
    • 이 크롬 브라우저에서 Node.js 도 사용할 수 있습니다. (우왓!!!)

Electron 전체 구조

Electron API Demos

Main Process & Renderer Process (1)

Main Process & Renderer Process (2)

Main Process & Renderer Process (3)

  • Main Process - Only One
    • 그냥 노드 서버라고 생각하면 편하다
  • Renderer Process - 만들기 나름
    • 크롬 브라우저라고 생각하면 편하다.
    • 크롬에서 노드를 사용할 수 있다니 !
      • 브라우저 샌드박스가 아니다.
      • 하지만 샌드박스로 만들수 있다.
    • BrowserWindow 객체를 생성하면 프로세스가 생긴다.
  • GPU Process - Default
    • 사용하지 않을 수 있다.

ipc & remote

  • Main 과 Renderer 프로세스는 각각 별도의 프로세스이다.
    • 데이타를 주고 받으려면 별도의 방식을 사용해야 한다.
  • 방식 1. IPC - Inter Process Communication
    • 메인의 ipcMain 모듈과 랜더러의 ipcRenderer 모듈을 이용
    • 이벤트를 반인딩하고, 메세지와 데이터를 샌드하는 방식
  • 방식 2. remote
    • 랜더러 프로세스의 remote 모듈을 이용
    • 메인의 몇가지 모듈을 제어 가능하다.
    • 라이프사이클로 인해 사용에 주의한다.
      • 주로 단발성 액션에 사용

Electron 실행 중 사용 가능한 라이브러리

Discuss. 왜 메인으로 보내야 할까요 ?

2. Creating Desktop App

데스크탑 어플리케이션 개발의 개발 프로세스

  • step1. 기획 및 디자인
  • step2. 개발
    • 일렉트론 프레임워크 이해
    • 웹기술
  • step3. 빌드 및 패키징
    • ' Electron ' 과 ' Electron Userland ' 가 서포트
    • ' Electron Korea ' 서포트
  • step4. 배포 및 운영
    • ' Electron ' 과 ' Electron Userland ' 가 서포트
    • ' Electron Korea ' 서포트

Electron UserLand - 3rd party community

그래서 구체적으로 무엇을 도와주는가

  • electron-packager
    • 각 플랫폼에 맞는 실행 파일로 만들어줌
  • electron-builder
    • 각 플랫폼에 맞는 실행 및 설치 파일로 만들어줌
    • 자동 업데이트 파일 생성
    • 어플리케이션 코드 서명
  • electron-release-server
  • electron-forge (HOT !)

Electron Korea - facebook & slack

3. Electron 을 사용한 앱의 실행 과정

step1. 각각의 환경에 맞는 prebuilt 된 실행파일 준비

step2. 이 prebuilt 된 실행파일의 실행 방법

  • 1. 단독 실행 => 실행 환경(OS)에 따라 미리 정해진 폴더를 실행
    • 더블 클릭 하거나
    • 커맨드라인에서 인자 없이 실행
  • 2. 인자로 특정 폴더를 지정하여 실행 (개발 모드에서 실행하는 방식)
    • 커맨드라인에서 실행파일 뒤에 폴더를 인자로 하여 실행

실행 환경(OS)에 따라 미리 정해진 폴더

// On macOS
electron/Electron.app/Contents/Resources/app/
├── package.json
├── main.js
└── index.html

// On Windows and Linux
electron/resources/app
├── package.json
├── main.js
└── index.html

step3. 실행파일이 실행하는 폴더

  • ${Project-Folder}/ <= 실행할때 지정된 폴더
    • package.json
      • main 이 지정하는 js 파일을 엔트리 포인트로 사용
  • 그래서 이 실행파일에서 사용하는 node.js 의 버전과 크로미움의 버전이 중요
    • 컴퓨터의 node.js 가 6.x.x 버전이지만,
    • electron 1.7.8 의 node.js 가 7.9.0 이기 때문에
    • async - await 가 사용 가능합니다.
  • 이 프로젝트 폴더가 ' 개발해야할 영역 ' 입니다.

step4. 실행파일의 이름과 아이콘 변경

  • 리브랜딩 이라고 합니다.
  • 일렉트론 프레임워크의 소스코드를 받아서 빌드할때 처리할 수 있지만, 권장하지 않습니다.
  • 미리 빌드된 실행파일로도 이름과 아이콘을 변경할 수 있습니다.
    • 그래서 대부분 미리 빌드된 실행파일을 다운받고,
    • 자신의 프로젝트 소스 폴더만 정해진 자리에 넣은 뒤,
    • 아이콘과 이름만 변경하는 방식을 사용합니다.

4. Development Environment

Project Setting

Electron 을 개발하려면 ?

electron 의 최신 버전 알아보기

Electron 버전의 의미

  • Electron - 1.7.8
    • Node - 7.9.0
    • Chromium - 58.0.3029.110
    • V8 - 5.8.283.38
  • ​실행 환경과 개발 환경
    • 개발 환경에서의 일렉트론 실행 절차 알아보기
    • 개발 환경의 Node 버전과 실행 환경인 Electron 의 Node 버전
  • 그래서 개발 환경의 Node 버전은 어떤 것으로 ?

NVM 설치 (선택)

Node 프로젝트 만들기

  • mkdir <PROJECT_NAME>
  • cd <PROJECT_NAME>
  • npm init -y

electron 설치 및 설정

  • npm i electron -D
    • D 인 이유 : electron-builder
  • electron 의 메인 진입점 경로를 package.json 의 main 프로퍼티와 맞춘다.

package.json

{
  "name": "electron",
  "version": "1.0.0",
  "description": "프로젝트 설정",
  "main": "index.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ts-korea/electron-with-typescript.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/ts-korea/electron-with-typescript/issues"
  },
  "homepage": "https://github.com/ts-korea/electron-with-typescript#readme",
  "devDependencies": {
    "electron": "^1.7.5",
    "tslint": "^5.7.0",
    "typescript": "^2.4.2"
  }
}

5. Electron Quick Start

git clone https://github.com/electron/electron-quick-start.git

Marks:electron-basic-online mark$ git clone https://github.com/electron/electron-quick-start.git
Cloning into 'electron-quick-start'...
remote: Counting objects: 255, done.
remote: Total 255 (delta 0), reused 0 (delta 0), pack-reused 255
Receiving objects: 100% (255/255), 42.55 KiB | 0 bytes/s, done.
Resolving deltas: 100% (117/117), done.
Marks:electron-basic-online mark$ 

cd electron-quick-start/

Marks:electron-basic-online mark$ cd electron-quick-start/
Marks:electron-quick-start mark$ 

npm i

Marks:electron-quick-start mark$ npm i

> electron@1.6.6 postinstall /Users/mark/Project/electron-basic-online/electron-quick-start/node_modules/electron
> node install.js

electron-quick-start@1.0.0 /Users/mark/Project/electron-basic-online/electron-quick-start
└─┬ electron@1.6.6 
  ├─┬ electron-download@3.3.0 
  │ ├─┬ debug@2.6.6 
  │ │ └── ms@0.7.3 
  │ ├─┬ fs-extra@0.30.0 
  │ │ ├── graceful-fs@4.1.11 
  │ │ ├─┬ jsonfile@2.4.0 
  │ │ │ └── graceful-fs@4.1.11  deduped
  │ │ ├─┬ klaw@1.3.1 
  │ │ │ └── graceful-fs@4.1.11  deduped
  │ │ ├── path-is-absolute@1.0.1 
  │ │ └─┬ rimraf@2.6.1 
  │ │   └─┬ glob@7.1.1 
  │ │     ├── fs.realpath@1.0.0 
  │ │     ├─┬ inflight@1.0.6 
  │ │     │ ├── once@1.4.0  deduped
  │ │     │ └── wrappy@1.0.2 
  │ │     ├── inherits@2.0.3  deduped
  │ │     ├─┬ minimatch@3.0.3 
  │ │     │ └─┬ brace-expansion@1.1.7 
  │ │     │   ├── balanced-match@0.4.2 
  │ │     │   └── concat-map@0.0.1 
  │ │     ├─┬ once@1.4.0 
  │ │     │ └── wrappy@1.0.2  deduped
  │ │     └── path-is-absolute@1.0.1  deduped
  │ ├── home-path@1.0.5 
  │ ├── minimist@1.2.0 
  │ ├─┬ nugget@2.0.1 
  │ │ ├── debug@2.6.6  deduped
  │ │ ├── minimist@1.2.0  deduped
  │ │ ├─┬ pretty-bytes@1.0.4 
  │ │ │ ├── get-stdin@4.0.1 
  │ │ │ └─┬ meow@3.7.0 
  │ │ │   ├─┬ camelcase-keys@2.1.0 
  │ │ │   │ ├── camelcase@2.1.1 
  │ │ │   │ └── map-obj@1.0.1  deduped
  │ │ │   ├── decamelize@1.2.0 
  │ │ │   ├─┬ loud-rejection@1.6.0 
  │ │ │   │ ├─┬ currently-unhandled@0.4.1 
  │ │ │   │ │ └── array-find-index@1.0.2 
  │ │ │   │ └── signal-exit@3.0.2 
  │ │ │   ├── map-obj@1.0.1 
  │ │ │   ├── minimist@1.2.0  deduped
  │ │ │   ├─┬ normalize-package-data@2.3.8 
  │ │ │   │ ├── hosted-git-info@2.4.2 
  │ │ │   │ ├─┬ is-builtin-module@1.0.0 
  │ │ │   │ │ └── builtin-modules@1.1.1 
  │ │ │   │ ├── semver@5.3.0  deduped
  │ │ │   │ └─┬ validate-npm-package-license@3.0.1 
  │ │ │   │   ├─┬ spdx-correct@1.0.2 
  │ │ │   │   │ └── spdx-license-ids@1.2.2 
  │ │ │   │   └── spdx-expression-parse@1.0.4 
  │ │ │   ├── object-assign@4.1.1 
  │ │ │   ├─┬ read-pkg-up@1.0.1 
  │ │ │   │ ├─┬ find-up@1.1.2 
  │ │ │   │ │ ├── path-exists@2.1.0  deduped
  │ │ │   │ │ └── pinkie-promise@2.0.1  deduped
  │ │ │   │ └─┬ read-pkg@1.1.0 
  │ │ │   │   ├─┬ load-json-file@1.1.0 
  │ │ │   │   │ ├── graceful-fs@4.1.11  deduped
  │ │ │   │   │ ├─┬ parse-json@2.2.0 
  │ │ │   │   │ │ └─┬ error-ex@1.3.1 
  │ │ │   │   │ │   └── is-arrayish@0.2.1 
  │ │ │   │   │ ├── pify@2.3.0 
  │ │ │   │   │ ├── pinkie-promise@2.0.1  deduped
  │ │ │   │   │ └─┬ strip-bom@2.0.0 
  │ │ │   │   │   └── is-utf8@0.2.1 
  │ │ │   │   ├── normalize-package-data@2.3.8  deduped
  │ │ │   │   └─┬ path-type@1.1.0 
  │ │ │   │     ├── graceful-fs@4.1.11  deduped
  │ │ │   │     ├── pify@2.3.0  deduped
  │ │ │   │     └── pinkie-promise@2.0.1  deduped
  │ │ │   ├─┬ redent@1.0.0 
  │ │ │   │ ├─┬ indent-string@2.1.0 
  │ │ │   │ │ └─┬ repeating@2.0.1 
  │ │ │   │ │   └─┬ is-finite@1.0.2 
  │ │ │   │ │     └── number-is-nan@1.0.1  deduped
  │ │ │   │ └─┬ strip-indent@1.0.1 
  │ │ │   │   └── get-stdin@4.0.1  deduped
  │ │ │   └── trim-newlines@1.0.0 
  │ │ ├─┬ progress-stream@1.2.0 
  │ │ │ ├── speedometer@0.1.4 
  │ │ │ └─┬ through2@0.2.3 
  │ │ │   ├─┬ readable-stream@1.1.14 
  │ │ │   │ ├── core-util-is@1.0.2  deduped
  │ │ │   │ ├── inherits@2.0.3  deduped
  │ │ │   │ ├── isarray@0.0.1 
  │ │ │   │ └── string_decoder@0.10.31 
  │ │ │   └─┬ xtend@2.1.2 
  │ │ │     └── object-keys@0.4.0 
  │ │ ├─┬ request@2.81.0 
  │ │ │ ├── aws-sign2@0.6.0 
  │ │ │ ├── aws4@1.6.0 
  │ │ │ ├── caseless@0.12.0 
  │ │ │ ├─┬ combined-stream@1.0.5 
  │ │ │ │ └── delayed-stream@1.0.0 
  │ │ │ ├── extend@3.0.1 
  │ │ │ ├── forever-agent@0.6.1 
  │ │ │ ├─┬ form-data@2.1.4 
  │ │ │ │ ├── asynckit@0.4.0 
  │ │ │ │ ├── combined-stream@1.0.5  deduped
  │ │ │ │ └── mime-types@2.1.15  deduped
  │ │ │ ├─┬ har-validator@4.2.1 
  │ │ │ │ ├─┬ ajv@4.11.8 
  │ │ │ │ │ ├── co@4.6.0 
  │ │ │ │ │ └─┬ json-stable-stringify@1.0.1 
  │ │ │ │ │   └── jsonify@0.0.0 
  │ │ │ │ └── har-schema@1.0.5 
  │ │ │ ├─┬ hawk@3.1.3 
  │ │ │ │ ├─┬ boom@2.10.1 
  │ │ │ │ │ └── hoek@2.16.3  deduped
  │ │ │ │ ├─┬ cryptiles@2.0.5 
  │ │ │ │ │ └── boom@2.10.1  deduped
  │ │ │ │ ├── hoek@2.16.3 
  │ │ │ │ └─┬ sntp@1.0.9 
  │ │ │ │   └── hoek@2.16.3  deduped
  │ │ │ ├─┬ http-signature@1.1.1 
  │ │ │ │ ├── assert-plus@0.2.0 
  │ │ │ │ ├─┬ jsprim@1.4.0 
  │ │ │ │ │ ├── assert-plus@1.0.0 
  │ │ │ │ │ ├── extsprintf@1.0.2 
  │ │ │ │ │ ├── json-schema@0.2.3 
  │ │ │ │ │ └─┬ verror@1.3.6 
  │ │ │ │ │   └── extsprintf@1.0.2  deduped
  │ │ │ │ └─┬ sshpk@1.13.0 
  │ │ │ │   ├── asn1@0.2.3 
  │ │ │ │   ├── assert-plus@1.0.0 
  │ │ │ │   ├─┬ bcrypt-pbkdf@1.0.1 
  │ │ │ │   │ └── tweetnacl@0.14.5  deduped
  │ │ │ │   ├─┬ dashdash@1.14.1 
  │ │ │ │   │ └── assert-plus@1.0.0 
  │ │ │ │   ├─┬ ecc-jsbn@0.1.1 
  │ │ │ │   │ └── jsbn@0.1.1  deduped
  │ │ │ │   ├─┬ getpass@0.1.7 
  │ │ │ │   │ └── assert-plus@1.0.0 
  │ │ │ │   ├─┬ jodid25519@1.0.2 
  │ │ │ │   │ └── jsbn@0.1.1  deduped
  │ │ │ │   ├── jsbn@0.1.1 
  │ │ │ │   └── tweetnacl@0.14.5 
  │ │ │ ├── is-typedarray@1.0.0 
  │ │ │ ├── isstream@0.1.2 
  │ │ │ ├── json-stringify-safe@5.0.1 
  │ │ │ ├─┬ mime-types@2.1.15 
  │ │ │ │ └── mime-db@1.27.0 
  │ │ │ ├── oauth-sign@0.8.2 
  │ │ │ ├── performance-now@0.2.0 
  │ │ │ ├── qs@6.4.0 
  │ │ │ ├── safe-buffer@5.0.1 
  │ │ │ ├── stringstream@0.0.5 
  │ │ │ ├─┬ tough-cookie@2.3.2 
  │ │ │ │ └── punycode@1.4.1 
  │ │ │ ├─┬ tunnel-agent@0.6.0 
  │ │ │ │ └── safe-buffer@5.0.1  deduped
  │ │ │ └── uuid@3.0.1 
  │ │ ├─┬ single-line-log@1.1.2 
  │ │ │ └─┬ string-width@1.0.2 
  │ │ │   ├── code-point-at@1.1.0 
  │ │ │   ├─┬ is-fullwidth-code-point@1.0.0 
  │ │ │   │ └── number-is-nan@1.0.1 
  │ │ │   └─┬ strip-ansi@3.0.1 
  │ │ │     └── ansi-regex@2.1.1 
  │ │ └── throttleit@0.0.2 
  │ ├─┬ path-exists@2.1.0 
  │ │ └─┬ pinkie-promise@2.0.1 
  │ │   └── pinkie@2.0.4 
  │ ├─┬ rc@1.2.1 
  │ │ ├── deep-extend@0.4.1 
  │ │ ├── ini@1.3.4 
  │ │ ├── minimist@1.2.0  deduped
  │ │ └── strip-json-comments@2.0.1 
  │ ├── semver@5.3.0 
  │ └─┬ sumchecker@1.3.1 
  │   ├── debug@2.6.6  deduped
  │   └── es6-promise@4.1.0 
  └─┬ extract-zip@1.6.5 
    ├─┬ concat-stream@1.6.0 
    │ ├── inherits@2.0.3 
    │ ├─┬ readable-stream@2.2.9 
    │ │ ├── buffer-shims@1.0.0 
    │ │ ├── core-util-is@1.0.2 
    │ │ ├── inherits@2.0.3  deduped
    │ │ ├── isarray@1.0.0 
    │ │ ├── process-nextick-args@1.0.7 
    │ │ ├─┬ string_decoder@1.0.0 
    │ │ │ └── buffer-shims@1.0.0  deduped
    │ │ └── util-deprecate@1.0.2 
    │ └── typedarray@0.0.6 
    ├─┬ debug@2.2.0 
    │ └── ms@0.7.1 
    ├─┬ mkdirp@0.5.0 
    │ └── minimist@0.0.8 
    └─┬ yauzl@2.4.1 
      └─┬ fd-slicer@1.0.1 
        └── pend@1.2.0 

Marks:electron-quick-start mark$ 

npm start

Marks:electron-quick-start mark$ npm start

> electron-quick-start@1.0.0 start /Users/mark/Project/electron-basic-online/electron-quick-start
> electron .

code .

electron-quick-start/package.json

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "~1.6.2"
  }
}

devDependencies - electron

electron's dependencies

{
  "dependencies": {
    "electron-download": "^3.0.1",
    "extract-zip": "^1.0.3"
  }
}

electron's scripts

{
  "scripts": {
    "cache-clean": "rm -rf ~/.electron && rm -rf dist",
    "postinstall": "node install.js",
    "pretest": "npm run cache-clean && npm run postinstall",
    "test": "tape test/*.js && standard"
  }
}

electron's postinstall

#!/usr/bin/env node

// maintainer note - x.y.z-ab version in package.json -> x.y.z
var version = require('./package').version.replace(/-.*/, '')

var fs = require('fs')
var os = require('os')
var path = require('path')
var extract = require('extract-zip')
var download = require('electron-download')

var installedVersion = null
try {
  installedVersion = fs.readFileSync(path.join(__dirname, 'dist', 'version'), 'utf-8').replace(/^v/, '')
} catch (ignored) {
  // do nothing
}

var platformPath = getPlatformPath()

if (installedVersion === version && fs.existsSync(path.join(__dirname, platformPath))) {
  process.exit(0)
}

// downloads if not cached
download({
  cache: process.env.electron_config_cache,
  version: version,
  platform: process.env.npm_config_platform,
  arch: process.env.npm_config_arch,
  strictSSL: process.env.npm_config_strict_ssl === 'true',
  quiet: ['info', 'verbose', 'silly', 'http'].indexOf(process.env.npm_config_loglevel) === -1
}, extractFile)

// unzips and makes path.txt point at the correct executable
function extractFile (err, zipPath) {
  if (err) return onerror(err)
  extract(zipPath, {dir: path.join(__dirname, 'dist')}, function (err) {
    if (err) return onerror(err)
    fs.writeFile(path.join(__dirname, 'path.txt'), platformPath, function (err) {
      if (err) return onerror(err)
    })
  })
}

function onerror (err) {
  throw err
}

function getPlatformPath () {
  var platform = process.env.npm_config_platform || os.platform()

  switch (platform) {
    case 'darwin':
      return 'dist/Electron.app/Contents/MacOS/Electron'
    case 'freebsd':
    case 'linux':
      return 'dist/electron'
    case 'win32':
      return 'dist/electron.exe'
    default:
      throw new Error('Electron builds are not available on platform: ' + platform)
  }
}

ls -al node_modules/electron/dist/

Marks:electron-quick-start mark$ ls -al node_modules/electron/dist/
total 3352
drwxr-xr-x   6 mark  staff      204 May  8 01:55 .
drwxr-xr-x  16 mark  staff      544 May  8 01:55 ..
drwxr-xr-x   3 mark  staff      102 May  8 01:55 Electron.app
-rw-r--r--   1 mark  staff     1060 May  8 01:55 LICENSE
-rw-r--r--   1 mark  staff  1708004 May  8 01:55 LICENSES.chromium.html
-rw-r--r--   1 mark  staff        6 May  8 01:55 version
Marks:electron-quick-start mark$ 

ls -al node_modules/electron/path.txt

dist/Electron.app/Contents/MacOS/Electron

electron-quick-start/package.json

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "~1.6.2"
  }
}

npm start

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

electron .

electron . => node_modules/.bin/electron .

ls -al node_modules/.bin/

Marks:electron-quick-start mark$ ls -al node_modules/.bin/
total 112
drwxr-xr-x   16 mark  staff   544 May  8 01:55 .
drwxr-xr-x  150 mark  staff  5100 May  8 01:55 ..
lrwxr-xr-x    1 mark  staff    18 May  8 01:55 electron -> ../electron/cli.js
lrwxr-xr-x    1 mark  staff    33 May  8 01:55 electron-download -> ../electron-download/build/cli.js
lrwxr-xr-x    1 mark  staff    21 May  8 01:55 extract-zip -> ../extract-zip/cli.js
lrwxr-xr-x    1 mark  staff    20 May  8 01:55 mkdirp -> ../mkdirp/bin/cmd.js
lrwxr-xr-x    1 mark  staff    16 May  8 01:55 nugget -> ../nugget/bin.js
lrwxr-xr-x    1 mark  staff    22 May  8 01:55 pretty-bytes -> ../pretty-bytes/cli.js
lrwxr-xr-x    1 mark  staff    14 May  8 01:55 rc -> ../rc/index.js
lrwxr-xr-x    1 mark  staff    16 May  8 01:55 rimraf -> ../rimraf/bin.js
lrwxr-xr-x    1 mark  staff    20 May  8 01:55 semver -> ../semver/bin/semver
lrwxr-xr-x    1 mark  staff    23 May  8 01:55 sshpk-conv -> ../sshpk/bin/sshpk-conv
lrwxr-xr-x    1 mark  staff    23 May  8 01:55 sshpk-sign -> ../sshpk/bin/sshpk-sign
lrwxr-xr-x    1 mark  staff    25 May  8 01:55 sshpk-verify -> ../sshpk/bin/sshpk-verify
lrwxr-xr-x    1 mark  staff    22 May  8 01:55 strip-indent -> ../strip-indent/cli.js
lrwxr-xr-x    1 mark  staff    16 May  8 01:55 uuid -> ../uuid/bin/uuid
Marks:electron-quick-start mark$ 

node_modules/.bin/electron .

node_modules/.bin/electron . => node_modules/electron/cli.js

node_modules/electron/cli.js .

#!/usr/bin/env node

var electron = require('./')

var proc = require('child_process')

var child = proc.spawn(electron, process.argv.slice(2), {stdio: 'inherit'})
child.on('close', function (code) {
  process.exit(code)
})

node_modules/electron/index.js

var fs = require('fs')
var path = require('path')

var pathFile = path.join(__dirname, 'path.txt')

if (fs.existsSync(pathFile)) {
  module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
} else {
  throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')
}

node_modules/electron/path.txt

dist/Electron.app/Contents/MacOS/Electron

electron-quick-start/package.json

{
  "name": "electron-quick-start",
  "version": "1.0.0",
  "description": "A minimal Electron application",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "repository": "https://github.com/electron/electron-quick-start",
  "keywords": [
    "Electron",
    "quick",
    "start",
    "tutorial",
    "demo"
  ],
  "author": "GitHub",
  "license": "CC0-1.0",
  "devDependencies": {
    "electron": "~1.6.2"
  }
}

main entry

{
  "main": "main.js"
}

main.js

const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600})

  // and load the index.html of the app.
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <script>document.write(process.versions.node)</script>,
    Chromium <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.
  </body>

  <script>
    // You can also require other files to run in this process
    require('./renderer.js')
  </script>
</html>

renderer.js

// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.

어플리케이션의 흐름 요약

// main process 생성
main.js

// main process 생성 완료 이벤트 바인딩
app.on('ready', createWindow);

// 바인딩 된 함수 실행
createWindow();

// renderer process 생성
mainWindow = new BrowserWindow({width: 800, height: 600});

// renderer process 에 로딩할 컨텐츠 삽입
mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
}));

// renderer process 의 script 실행
require('renderer.js');

6. app

  • 랜더러 프로세스가 없는 앱도 데스크탑 앱이다.
  • app 모듈은 개발하는 앱의 라이프사이클을 알고 있다.
  • 그 라이프사이클의 이벤트에 사용자의 함수를 할당한다.
  • 그 이벤트들을 학습하자.

7. BrowserWindow

  • 랜더러 프로세스 생성
    • 어려가지 옵션
  • 생성된 윈도우에 이벤트 설정
    • ready-to-show 이벤트 알아두기
  • 타이틀바 없는 창
    • frame
    • titleBarStyle
  • 창의 부모 자식 관계
    • parent
    • modal

8. Tray

  • 트레이 생성
    • new Tray(이미지 경로);
  • 트레이에 이벤트를 할당 가능
  • 트레이에 메뉴를 적용
    • 트레이.setContextMenu(메뉴);
      • 메뉴 생성
        • = Menu.buildFromTemplate(템플릿);
        • = new Menu();
          • MenuItem 만들어서 추가

9. Application Menu

어플리케이션 메뉴를 만드는 법

  • Menu.setApplicationMenu(메뉴);
  • 메뉴
    • = Menu.buildFromTemplate(템플릿);
    • = new Menu();
      • MenuItem 만들어서 추가
  • 템플릿은 객체

macOS 와 Windows 의 어플리케이션 메뉴 차이

  • macOS
    • 앱 하나에 어플리케이션 메뉴 하나
  • Windows
    • 랜더러 프로세스 별로 메뉴 존재
    • win.setMenu(null | 메뉴) 로 개개 메뉴 변경

10. dialog

native dialog 는 총 5개

  • open dialog
    • 콜백 인자는 string[]
  • save dialog
    • 콜백 인자는 string
  • message dialog
    • 콜백 인자는 버튼 id: number
  • error dialog
    • 노 콜백
  • certificate dialog

고급 옵션 찾아보기

https://electron.atom.io/docs/api/dialog/

11. ipc

inter process communication

  • 랜더러 프로세스와 메인 프로세스의 데이터 전달
  • 메인 프로세스는 ipcMain 모듈 사용
  • 랜더러 프로세스는 ipcRenderer 모듈 사용
  • 동기로 전달하는 방식과 비동기로 전달하는 방식이 있다.
    • 비동기 : 랜더러에서 전달받은 후 메인에서 비동기로 데이터를 처리해서 다시 랜더러로 넘기는 경우
    • 동기 : 랜더러에서 가져갈 데이터가 메인에 바로 있는 경우

12. remote

13. shell, process

Made with Slides.com