들어가기 전에

리액트 지식 정리 #7, CRA 사용하여 Electron 앱 만들기

기술 스택

Electron은 JavaScript, HTML, and CSS와 같은 웹 기술로 네이티브 애플리케이션을 만들기 위한 프레임워크 입니다. 이번에는 리액트를 사용할 겁니다.

React는 유저 인터페이스를 빌드하기 위한 자바스크립트 라이브러리입니다. 그리고 더욱 많은 기능이 있습니다.

리액트 초기 세팅을 더 쉽게 하기 위하여 Create React App을 이용할 것입니다. Create React App(CRA)는 굉장합니다. 많은 시간을 아낄 수 있고 귀찮은 환경설정을 안 해도 됩니다.

Create React App은 페이스북의 개발자에 의해 빌드된 툴입니다. 리액트 앱을 만들 때, 좋은 시작점을 제공합니다. create-react-app이라는 명령어 하나만 입력하면 간단하게 리액트 프로젝트를 시작할 수 있습니다.

Rescripts는 우리가 CRA 세팅을 eject 없이 할 수 있도록 도와줍니다.

CRA를 Ejecting하는 것은 정말 피하고 싶은 일 중 하나입니다. 왜냐하면 CRA의 최신 개선점들을 더이상 이용할 수 없기 때문입니다.

Electron Builder는 배포를 위한 데스크탑 앱을 패키징하는데 이용됩니다.

개발 세팅

Create React App을 이용하여 새로운 앱을 만들어봅시다.

npx create-react-app my-app
cd my-app

Electron을 추가합시다

yarn add electron electron-builder --dev

우리가 필요한 개발 도구들도 추가해줍시다.

yarn add wait-on concurrently --dev
yarn add electron-is-dev

public/electron.js 새로운 파일을 만들어주시고 아래 내용을 입력해주세요.

const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

const path = require('path');
const isDev = require('electron-is-dev');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({width: 900, height: 680});
  mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`);
  if (isDev) {
    // Open the DevTools.
    //BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
    mainWindow.webContents.openDevTools();
  }
  mainWindow.on('closed', () => mainWindow = null);
}

app.on('ready', createWindow);

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

app.on('activate', () => {
  if (mainWindow === null) {
    createWindow();
  }
});

다음 명령어를 package.json의 scripts 태그에 추가해주세요.

"electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\""

위 스크립트는 Electron 앱을 실행하기 전, CRA가 React app을 localhost:3000에서 동작시킬 때까지 기다립니다.

다음 main 태그를 package.json에 추가해주세요

"main": "public/electron.js",

모두 다 마쳤다면 package.json의 형태는 아마 다음과 같을 겁니다.

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "electron-is-dev": "^1.0.1",
    "react": "^16.8.3",
    "react-dom": "^16.8.3",
    "react-scripts": "2.1.5"
  },
  "main": "public/electron.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\""
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "devDependencies": {
    "concurrently": "^4.1.0",
    "electron": "^4.0.6",
    "electron-builder": "^20.38.5",
    "wait-on": "^3.2.0"
  }
}

이 과정을 모두 마쳤다면 앱을 개발 모드에서 실행시킬 수 있습니다.

역자 주: 저는 똑같이 했는데 안돼서, "electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"" 부분을 "electron-dev": "concurrently \"SET BROWSER=none&&yarn start\" \"wait-on http://localhost:3000 && electron .\""로 바꾸었습니다. 참조하세요.

yarn electron-dev

다음과 같은 화면을 보게될 것입니다.

electron-boilerplate.png

이 이미지가 보인다면, 축하드립니다! 이제 앱을 개발할 준비가 됐습니다! 예이!

만일 fs와 같은 모듈에 접근할 필요가 있는데 섣불리 기존에 하던 방식으로 하시면, Module not found 에러를 겪게 되실 겁니다. 여기를 보세요.

이 문제를 해결하기 위해서는 웹팩 타겟으로 electron-renderer를 사용할 필요가 있습니다. 하지만 우리는 CRA를 eject하긴 싫으니까 [Rescripts]를 사용하면 됩니다.

먼저, Rescripts를 설치합시다.

yarn add @rescripts/cli @rescripts/rescript-env --dev

기존 package.json 파일의 scripts 태그는 아래와 같은 형태로 생겼을 겁니다.

"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",

이걸 다음과 같이 바꿔주세요.

"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",

그리고 .rescriptsrc.js라는 새로운 파일을 추가하고 다음 내용을 파일에 저장하세요.

마지막으로, .webpack.config.js라는 새로운 파일을 추가하고 다음과 같은 내용을 넣어주시면 됩니다.

// define child rescript
module.exports = config => {
  config.target = 'electron-renderer';
  return config;
}

이제 fs 모듈을 사용할 수 있습니다.

패키지 세팅

훌륭합니다! 이제 우리 앱을 패키징할 준비가 됐습니다.

먼저, Electron Builder와 Typescript를 깔아줍시다.

yarn add electron-builder typescript --dev

CRA는 기본 값으로 절대경로를 사용하는 index.html을 빌드합니다. 일렉트론에서 로딩할 때는 이 빌드는 실패할 겁니다. 이것을 수정하기 위해 환경설정 옵션이 필요합니다.

package.json 내에서 homepage 프로퍼티를 세팅합시다.

"homepage": "./",

다음으로, 빌드를 패키징할 electron-pack 명령어를 추가합시다. 다음 내용들을 package.json 안의 sciprts 태그에 추가해주세요.

"postinstall": "electron-builder install-app-deps",
"preelectron-pack": "yarn build",
"electron-pack": "build -mw"

"postinstall": "electron-builder install-app-deps"는 native dependency를 electron 버전과 맞춰줍니다.
"preelectron-pack": "yarn build"는 CRA를 빌드할 것입니다.
"electron-pack": "build-mw"는 맥과 윈도우를 위한 앱 패키징입니다.

이 명령어를 실행하기 전에 일단 Electron Builder 환경설정이 필요합니다.
다음 내용을 package.json에 추가해주세요.

"author": {
  "name": "Your Name",
  "email": "your.email@domain.com",
  "url": "https://your-website.com"
},
"build": {
  "appId": "com.my-website.my-app",
  "productName": "MyApp",
  "copyright": "Copyright © 2019 ${author}",
  "mac": {
    "category": "public.app-category.utilities"
  },
  "files": [
    "build/**/*",
    "node_modules/**/*"
  ],
  "directories": {
    "buildResources": "assets"
  }
}

Electron Builder 옵션은 여기에서 확인 가능합니다.

만일 앱 아이콘이 들어갈 assets라 불리는 디렉토리를 추가하길 원하면, 여기를 참조하세요.

지금의 package.json파일은 다음과 같은 모습일 겁니다.

{
  "name": "my-app",
  "description": "Electron + Create React App + Electron Builder",
  "version": "0.1.0",
  "private": true,
  "author": {
    "name": "Your Name",
    "email": "your.email@domain.com",
    "url": "https://your-website.com"
  },
  "build": {
    "appId": "com.my-website.my-app",
    "productName": "MyApp",
    "copyright": "Copyright © 2019 ${author}",
    "mac": {
      "category": "public.app-category.utilities"
    },
    "files": [
      "build/**/*",
      "node_modules/**/*"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },
  "dependencies": {
    "electron-is-dev": "^1.0.1",
    "react": "^16.8.3",
    "react-dom": "^16.8.3",
    "react-scripts": "2.1.5"
  },
  "homepage": "./",
  "main": "public/electron.js",
  "scripts": {
    "start": "rescripts start",
    "build": "rescripts build",
    "test": "rescripts test",
    "eject": "react-scripts eject",
    "electron-dev": "concurrently \"BROWSER=none yarn start\" \"wait-on http://localhost:3000 && electron .\"",
    "postinstall": "electron-builder install-app-deps",
    "preelectron-pack": "yarn build",
    "electron-pack": "build -mw"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "devDependencies": {
    "@rescripts/cli": "^0.0.10",
    "@rescripts/rescript-env": "^0.0.5",
    "concurrently": "^4.1.0",
    "electron": "^4.0.6",
    "electron-builder": "^20.38.5",
    "typescript": "^3.3.3333",
    "wait-on": "^3.2.0"
  }
}

이제 우린 앱을 패키징할 준비가 됐습니다. 다음의 명령어를 입력해봅시다.

yarn electron-pack 

패키징된 artifacts를 dist 디렉토리에서 보실 수 있을 겁니다.
이게 끝입니다. 이젠 앱을 코딩하고 대단한 거를 만들어보세요.

샘플 앱도 구경해보세요.
이 블로그가 도움이 되었길 바랍니다.