Skip to main content

애플리케이션 업데이트

Electron 애플리케이션에 자동 업데이트를 제공하는 방법은 여러 가지가 있다. 가장 간단하고 공식적으로 지원되는 방법은 내장된 Squirrel 프레임워크와 Electron의 autoUpdater 모듈을 활용하는 것이다.

클라우드 객체 스토리지 사용하기 (서버리스)

간단한 서버리스 업데이트 흐름을 구현하려면, Electron의 autoUpdater 모듈을 사용해 최신 릴리스 메타데이터가 포함된 정적 스토리지 URL을 지정할 수 있다. 이 URL을 통해 업데이트가 있는지 확인할 수 있다.

새로운 릴리스가 출시되면, 해당 릴리스와 함께 메타데이터를 클라우드 스토리지에 게시해야 한다. macOS와 Windows의 메타데이터 형식은 서로 다르다.

릴리스 메타데이터 게시

Electron Forge를 사용하면 ZIP Maker(macOS)의 macUpdateManifestBaseUrl과 Squirrel.Windows Maker(Windows)의 remoteReleases를 통해 정적 파일 저장소 업데이트를 설정할 수 있다. 자세한 예제는 Forge의 S3에서 자동 업데이트 가이드를 참고한다.

수동 게시

macOS에서는 Squirrel.Mac이 다음 JSON 형식의 releases.json 파일을 읽어 업데이트를 받을 수 있다:

releases.json
{
"currentRelease": "1.2.3",
"releases": [
{
"version": "1.2.1",
"updateTo": {
"version": "1.2.1",
"pub_date": "2023-09-18T12:29:53+01:00",
"notes": "Theses are some release notes innit",
"name": "1.2.1",
"url": "https://mycompany.example.com/myapp/releases/myrelease"
}
},
{
"version": "1.2.3",
"updateTo": {
"version": "1.2.3",
"pub_date": "2024-09-18T12:29:53+01:00",
"notes": "Theses are some more release notes innit",
"name": "1.2.3",
"url": "https://mycompany.example.com/myapp/releases/myrelease3"
}
}
]
}

Windows에서는 Squirrel.Windows가 빌드 과정에서 생성된 RELEASES 파일을 읽어 업데이트를 받는다. 이 파일은 업데이트할 .nupkg 델타 패키지를 상세히 기술한다.

RELEASES
B0892F3C7AC91D72A6271FF36905FEF8FE993520 electron-fiddle-0.36.3-full.nupkg 103298365

이러한 파일들은 앱의 플랫폼과 아키텍처를 고려한 폴더 구조 하에 릴리스와 같은 디렉터리에 위치해야 한다.

예시:

my-app-updates/
├─ darwin/
│ ├─ x64/
│ │ ├─ my-app-1.0.0-darwin-x64.zip
│ │ ├─ my-app-1.1.0-darwin-x64.zip
│ │ ├─ RELEASES.json
│ ├─ arm64/
│ │ ├─ my-app-1.0.0-darwin-arm64.zip
│ │ ├─ my-app-1.1.0-darwin-arm64.zip
│ │ ├─ RELEASES.json
├─ win32/
│ ├─ x64/
│ │ ├─ my-app-1.0.0-win32-x64.exe
│ │ ├─ my-app-1.0.0-win32-x64.nupkg
│ │ ├─ my-app-1.1.0-win32-x64.exe
│ │ ├─ my-app-1.1.0-win32-x64.nupkg
│ │ ├─ RELEASES

릴리스 메타데이터 읽기

메타데이터를 사용하는 가장 쉬운 방법은 update-electron-app을 설치하는 것이다. 이 Node.js 모듈은 autoUpdater를 설정하고 사용자에게 네이티브 다이얼로그를 표시한다.

정적 스토리지 업데이트를 위해 updateSource.baseUrl 매개변수를 릴리스 메타데이터 파일이 포함된 디렉토리로 지정한다.

main.js
const { updateElectronApp, UpdateSourceType } = require('update-electron-app')

updateElectronApp({
updateSource: {
type: UpdateSourceType.StaticStorage,
baseUrl: `https://my-bucket.s3.amazonaws.com/my-app-updates/${process.platform}/${process.arch}`
}
})

update.electronjs.org 사용하기

Electron 팀은 update.electronjs.org를 유지 관리하며, 이는 Electron 앱이 자동 업데이트를 할 수 있도록 지원하는 무료 오픈소스 웹서비스다. 이 서비스는 다음과 같은 조건을 충족하는 Electron 앱을 위해 설계되었다:

  • 앱이 macOS 또는 Windows에서 실행된다.
  • 앱이 공개된 GitHub 저장소를 가지고 있다.
  • 빌드가 GitHub Releases에 게시된다.
  • 빌드가 코드 서명되어 있다. (macOS 전용)

이 서비스를 사용하는 가장 쉬운 방법은 update-electron-app을 설치하는 것이다. 이는 update.electronjs.org와 함께 사용하도록 미리 구성된 Node.js 모듈이다.

선호하는 Node.js 패키지 관리자를 사용해 모듈을 설치한다:

npm install update-electron-app

그런 다음, 앱의 메인 프로세스 파일에서 업데이터를 호출한다:

main.js
require('update-electron-app')()

기본적으로 이 모듈은 앱 시작 시점과 이후 10분마다 업데이트를 확인한다. 업데이트가 발견되면 백그라운드에서 자동으로 다운로드된다. 다운로드가 완료되면, 사용자가 앱을 다시 시작할 수 있도록 다이얼로그가 표시된다.

구성을 커스터마이징해야 한다면, update-electron-app에 옵션을 전달하거나 업데이트 서비스를 직접 사용할 수 있다.

다른 업데이트 서비스 활용하기

개인용 Electron 애플리케이션을 개발하거나, GitHub Releases에 릴리스를 게시하지 않는 경우, 직접 업데이트 서버를 운영해야 할 수도 있다.

Step 1: 업데이트 서버 배포

필요에 따라 다음 옵션 중 하나를 선택할 수 있다:

  • Hazel – 개인 또는 오픈소스 앱을 위한 업데이트 서버로, Vercel에 무료로 배포할 수 있다. GitHub Releases에서 업데이트를 가져오며, GitHub의 CDN 기능을 활용한다.
  • NutsGitHub Releases를 사용하지만, 앱 업데이트를 디스크에 캐시하고 비공개 저장소를 지원한다.
  • electron-release-server – 릴리스를 관리할 수 있는 대시보드를 제공하며, GitHub에서 릴리스가 시작될 필요가 없다.
  • Nucleus – Atlassian이 유지 관리하는 Electron 앱을 위한 완전한 업데이트 서버다. 여러 애플리케이션과 채널을 지원하며, 서버 비용을 최소화하기 위해 정적 파일 저장소를 사용한다.

업데이트 서버를 배포한 후, Electron의 autoUpdater 모듈을 사용해 앱 코드를 수정하여 업데이트를 받고 적용할 수 있다.

Step 2: 앱에서 업데이트 받기

먼저 메인 프로세스 코드에서 필요한 모듈을 불러온다. 다음 코드는 서버 소프트웨어에 따라 다를 수 있지만, Hazel을 사용할 때는 설명한 대로 동작한다.

실행 환경 확인!

아래 코드가 개발 환경이 아닌 패키징된 앱에서만 실행되도록 주의한다. app.isPackaged API를 사용해 환경을 확인할 수 있다.

main.js
const { app, autoUpdater, dialog } = require('electron')

다음으로, 업데이트 서버 피드의 URL을 구성하고 autoUpdater에 알려준다:

main.js
const server = 'https://your-deployment-url.com'
const url = `${server}/update/${process.platform}/${app.getVersion()}`

autoUpdater.setFeedURL({ url })

마지막으로 업데이트를 확인한다. 아래 예제는 매분마다 업데이트를 확인한다:

main.js
setInterval(() => {
autoUpdater.checkForUpdates()
}, 60000)

앱이 패키징되면, 새로운 GitHub Release가 출시될 때마다 업데이트를 받게 된다.

Step 3: 업데이트 알림 설정

애플리케이션의 기본 업데이트 메커니즘을 구성했다면, 사용자가 업데이트를 알 수 있도록 설정해야 한다. 이는 autoUpdater API 이벤트를 사용해 구현할 수 있다:

main.js
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
const dialogOpts = {
type: 'info',
buttons: ['Restart', 'Later'],
title: 'Application Update',
message: process.platform === 'win32' ? releaseNotes : releaseName,
detail:
'A new version has been downloaded. Restart the application to apply the updates.'
}

dialog.showMessageBox(dialogOpts).then((returnValue) => {
if (returnValue.response === 0) autoUpdater.quitAndInstall()
})
})

또한 에러가 발생할 경우 이를 적절히 처리해야 한다. 다음은 에러를 stderr에 기록하는 예제다:

main.js
autoUpdater.on('error', (message) => {
console.error('There was a problem updating the application')
console.error(message)
})
수동 업데이트 처리

autoUpdate가 수행하는 요청은 직접 제어할 수 없기 때문에, 업데이트 서버가 인증 뒤에 있는 경우와 같이 처리하기 어려운 상황이 발생할 수 있다. url 필드는 file:// 프로토콜을 지원하므로, 로컬 디렉토리에서 업데이트를 로드하는 방식으로 서버 통신 과정을 우회할 수 있다. 이 예제에서 어떻게 구현할 수 있는지 확인할 수 있다.

서버 스펙 업데이트

고급 배포 요구사항이 있다면, Squirrel과 호환되는 자체 업데이트 서버를 구축할 수 있다. 예를 들어, 백분율 기반 롤아웃, 별도의 릴리스 채널을 통한 앱 배포, 인증 체크 뒤에 업데이트 서버를 배치하는 등의 작업이 가능하다.

Squirrel.Windows와 Squirrel.Mac 클라이언트는 서로 다른 응답 형식을 요구하지만, process.platform 값을 기반으로 서로 다른 엔드포인트에 요청을 보내 단일 서버를 사용할 수 있다.

main.js
const { app, autoUpdater } = require('electron')

const server = 'https://your-deployment-url.com'
// 예: 윈도우와 앱 버전 1.2.3의 경우
// https://your-deployment-url.com/update/win32/1.2.3
const url = `${server}/update/${process.platform}/${app.getVersion()}`

autoUpdater.setFeedURL({ url })

윈도우

Squirrel.Windows 클라이언트는 업데이트 서버가 엔드포인트의 /RELEASES 하위 경로에서 최신 빌드의 RELEASES 아티팩트를 반환할 것을 기대한다.

예를 들어, 피드 URL이 https://your-deployment-url.com/update/win32/1.2.3라면, https://your-deployment-url.com/update/win32/1.2.3/RELEASES 엔드포인트는 제공하려는 버전의 RELEASES 아티팩트 내용을 반환해야 한다.

https://your-deployment-url.com/update/win32/1.2.3/RELEASES
B0892F3C7AC91D72A6271FF36905FEF8FE993520 https://your-static.storage/your-app-1.2.3-full.nupkg 103298365

Squirrel.Windows는 현재 앱이 RELEASES에 반환된 버전으로 업데이트해야 하는지 비교 검사를 수행한다. 따라서 업데이트가 없더라도 반드시 응답을 반환해야 한다.

macOS

업데이트가 있을 때, Squirrel.Mac 클라이언트는 피드 URL의 엔드포인트에서 JSON 응답을 기대한다. 이 객체는 필수적인 url 속성을 포함해야 하며, 이 속성은 앱 업데이트의 ZIP 아카이브를 가리킨다. 객체의 다른 모든 속성은 선택 사항이다.

https://your-deployment-url.com/update/darwin/0.31.0
{
"url": "https://your-static.storage/your-app-1.2.3-darwin.zip",
"name": "1.2.3",
"notes": "Theses are some release notes innit",
"pub_date": "2024-09-18T12:29:53+01:00"
}

업데이트가 없을 경우, 서버는 204 No Content HTTP 응답을 반환해야 한다.