기여하기

이 문서 편집

문서를 번역하거나 컴퓨터에서 전체 프로젝트를 여세요.

작업을 제출하기 전까지는 아무것도 게시되지 않습니다.

자동 업데이트되는 Wails 앱

`wails3 init`부터 서명된 릴리스 검증과 헬퍼 모드 교체까지, GitHub Releases에서 스스로 업데이트하는 Wails v3 애플리케이션을 빌드합니다.

이 튜토리얼에서는 새 Wails v3 애플리케이션에 앱 내 업데이터를 추가합니다. 완료하면 앱에서 다음 작업을 수행할 수 있습니다.

  • 요청 시 GitHub Releases를 확인합니다(선택적으로 타이머를 사용할 수도 있습니다).
  • 현재 실행 중인 OS와 아키텍처에 맞는 에셋을 다운로드합니다.
  • 다운로드한 바이트를 기준으로 SHA-256 다이제스트를 검증합니다(선택적으로 Ed25519 서명도 검증할 수 있습니다).
  • 프레임워크의 기본 업데이트 창에 릴리스 노트를 표시합니다.
  • 별도의 헬퍼 실행 파일을 배포하지 않고 실행 중인 바이너리를 교체한 후 다시 실행합니다.

무료이고 별도의 인프라가 필요하지 않으므로 업데이트 소스로 GitHub Releases를 사용하겠습니다. 동일한 패턴을 keygen.sh와 Sparkle AppCast에도 적용할 수 있습니다. 완료한 후 업데이터 가이드를 참조하세요.


1

새 Wails 앱으로 시작하기

vanilla 템플릿으로 새 프로젝트의 기본 구조를 생성합니다.

bash
wails3 init -n updater-tutorial -t vanilla
cd updater-tutorial

이제 main.go, frontend/ 및 Taskfile.yml이 포함된 디렉터리가 있어야 합니다. 빌드되고 실행되는지 확인합니다.

bash
wails3 task dev

빈 Wails 창이 열려야 합니다. 앱을 종료하고 계속 진행하세요.

2

업데이터 import 추가하기

main.go을 열고 import에 다음 두 업데이터 패키지를 추가합니다.

main.go
go
package main

import (
    _ "embed"

    "github.com/wailsapp/wails/v3/pkg/application"
    "github.com/wailsapp/wails/v3/pkg/updater"
    "github.com/wailsapp/wails/v3/pkg/updater/providers/github"
)

이 패키지들은 Updater 자체와 GitHub Releases 공급자를 가져옵니다.

3

Updater 구성하기

app.Updater은 이미 모든 *application.App에 연결되어 있으므로 Init만 호출하면 됩니다.

main.go
go
const currentVersion = "1.0.0"

gh, err := github.New(github.Config{
    Repository:    "yourorg/your-repo",   // ← change this
    ChecksumAsset: "SHA256SUMS",          // sibling file with sha256 digests
})
if err != nil {
    log.Fatalf("github.New: %v", err)
}

if err := app.Updater.Init(updater.Config{
    CurrentVersion: currentVersion,
    Providers:      []updater.Provider{gh},
}); err != nil {
    log.Fatalf("Updater.Init: %v", err)
}

이 코드를 application.New 뒤, app.Run() 앞에 배치합니다.

4

업데이트를 실행하는 메뉴 항목 추가하기

같은 main.go에 “업데이트 확인…” 메뉴 항목을 추가합니다.

main.go
go
menu := app.Menu.New()
app.Menu.SetApplicationMenu(menu)
appMenu := menu.AddSubmenu("App")
appMenu.Add("Check for Updates…").OnClick(func(*application.Context) {
    go func() {
        if err := app.Updater.CheckAndInstall(context.Background()); err != nil {
            app.Logger.Error("update", "error", err)
        }
    }()
})

CheckAndInstall은 프레임워크의 업데이트 창을 열고 Check을 실행한 다음, 릴리스가 발견되면 DownloadAndInstall을 자동으로 실행합니다. 새로운 릴리스가 없으면 창은 “최신 버전” 상태로 열린 채 유지되며, 사용자가 닫기 버튼으로 닫습니다.

5

릴리스가 없는 상태에서 한 번 실행하기

bash
wails3 task dev

앱 → 업데이트 확인…을 클릭합니다. 업데이트 창이 잠시 열리고 GitHub API를 호출한 뒤 1.0.0보다 새로운 릴리스가 없음을 확인하고, 녹색 ✓와 함께 최신 버전 상태로 전환되어야 합니다.

여기서 오류가 발생한다면 일반적으로 다음 중 하나가 원인입니다.

증상 해결 방법
404 Not Found Repository 필드가 잘못되었습니다. 반드시 owner/repo이어야 합니다.
403 rate-limited github.Config에 Token: "ghp_…"을 추가합니다(public_repo 범위가 있는 PAT를 사용하세요).
네트워크 오류 실행 중인 앱에서 api.github.com에 연결할 수 있는지 확인합니다.
6

테스트 릴리스 게시하기

main.go의 currentVersion을 1.0.0로 올립니다(또는 그대로 둡니다). 릴리스에 첨부할 바이너리를 얻도록 한 플랫폼용으로 빌드합니다.

bash
wails3 task build:darwin
# produces bin/updater-tutorial.app
# zip it for the release asset:
cd bin && zip -r updater-tutorial-darwin-arm64.zip updater-tutorial.app && cd ..

바이너리와 같은 위치에 SHA256SUMS 파일을 생성합니다.

bash
cd bin
shasum -a 256 updater-tutorial-* > SHA256SUMS
cat SHA256SUMS

다음과 같은 줄이 하나 이상 표시되어야 합니다.

text
abc123…  updater-tutorial-darwin-arm64.zip

이제 GitHub 저장소에 이를 v2.0.0로 게시합니다.

bash
gh release create v2.0.0 \
    --title "v2.0.0" \
    --notes "First update for the self-update tutorial.

- **Bold** Markdown renders in the update window
- \`Code spans\` too
- Lists work
- GFM tables work" \
    bin/SHA256SUMS bin/updater-tutorial-*
7

앱을 실행하고 업데이트 확인하기

currentVersion이 여전히 1.0.0인 상태에서 앱을 다시 실행합니다.

bash
wails3 task dev

앱 → 업데이트 확인…을 클릭합니다. 이번에는 다음과 같은 화면이 표시되어야 합니다.

업데이트 준비 완료 상태의 기본 업데이터 창으로, 버전 배지, Markdown으로 렌더링된 릴리스 노트 및 기본 작업 버튼인 다시 시작 및 적용이 표시되어 있습니다.

  • 대표 아이콘이 파란색 ↓(“업데이트 사용 가능”)에서 녹색 ✓(“업데이트 준비 완료”)로 바뀝니다.
  • 부제에 v1.0.0 → v2.0.0 · <size>이 표시됩니다.
  • 릴리스 노트 패널은 굵은 글꼴, 인라인 코드 및 표가 포함된 Markdown을 렌더링합니다.
  • 다운로드 중에 진행률 표시줄이 채워집니다(바이너리가 작으므로 금방 완료됩니다).

Updater는 새 바이너리를 임시 디렉터리에 준비합니다. 업데이트를 완료하려면 다음과 같이 합니다.

  • 다시 시작 및 적용을 클릭합니다.
  • 앱이 종료되고 헬퍼가 바이너리를 교체한 다음 새 바이너리가 다시 실행됩니다.
  • 다시 실행된 앱에는 currentVersion = "1.0.0"이 표시되지만(하드코딩했기 때문입니다), 디스크의 바이트는 v2.0.0 빌드와 일치합니다.

실제 앱에서는 새 바이너리가 현재 버전이 v2.0.0임을 인식하고 이후 확인 시 업데이트가 없다고 판단하도록, 빌드할 때 -ldflags을 통해 currentVersion을 설정합니다.

8

`currentVersion`을 빌드에 연결하기

상수를 빌드 시점 변수로 교체합니다.

main.go
go
var (
    currentVersion = "dev" // overridden by -ldflags at release time
)

그런 다음 빌드 명령에서 다음과 같이 지정합니다.

bash
wails3 task build:darwin -- -ldflags "-X main.currentVersion=2.0.0"

또는 git describe --tags에서 값을 가져오도록 Taskfile.yml에 -ldflags을 추가합니다.

9

암호학적 서명 추가하기(프로덕션 환경에 권장)

SHA256SUMS 방식은 무결성(바이트가 GitHub에 저장된 내용과 일치하는지)은 검증하지만, 진위성(해당 바이트가 탈취된 메인테이너 계정이 아니라 릴리스 파이프라인에서 생성되었는지)은 검증하지 않습니다. 변조를 방지하려면 Ed25519 키로 각 릴리스에 서명하세요.

bash
# One-time: generate the keypair
ssh-keygen -t ed25519 -f updater-key -N "" -C "wails-updater"
#   updater-key      — keep secret (build server, HSM, password manager)
#   updater-key.pub  — bundle in your app

각 릴리스에서 모든 자산의 SHA-256 다이제스트를 비공개 키로 서명하세요. 다음과 같은 간단한 Go 도우미를 사용할 수 있습니다.

cmd/sign-release/main.go
go
package main

import (
    "crypto/ed25519"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "io"
    "os"
)

func main() {
    priv, _ := os.ReadFile("updater-key")
    key := ed25519.PrivateKey(priv) // raw 64-byte private key

    f, _ := os.Open(os.Args[1])
    defer f.Close()
    h := sha256.New()
    _, _ = io.Copy(h, f)
    sig := ed25519.Sign(key, h.Sum(nil))
    fmt.Println(base64.StdEncoding.EncodeToString(sig))
}

기본 GitHub 공급자는 현재 별도의 서명 파일을 가져오지 않습니다. 이를 수행하는 사용자 지정 공급자를 작성하거나, 모든 아티팩트를 서버 측에서 서명하고 API를 통해 다이제스트와 서명을 모두 제공하는 keygen.sh로 전환할 수 있습니다.

공개 키를 앱에 포함하세요.

main.go
go
//go:embed updater-key.pub
var updaterPublicKey []byte

app.Updater.Init(updater.Config{
    CurrentVersion: currentVersion,
    PublicKey:      updaterPublicKey,
    Providers:      []updater.Provider{gh},
})

PublicKey을 설정하면 Signature을 포함해 배포되는 모든 릴리스가 이 키를 사용한 검증을 통과해야 합니다. 릴리스 소스는 자체 키로 바꿔치기할 수 없습니다. 이것이 바로 빌드 시 대역 외 방식으로 키를 고정하는 이유입니다.

10

창 사용자 지정

기본 창은 일반적인 사용 사례를 지원합니다. 더 세밀하게 제어해야 한다면 다음 세 가지 확장 방법 중 원하는 사용자 지정 수준에 따라 하나를 선택하세요.

go
app.Updater.Init(updater.Config{
    // …
    Window: &updater.BuiltinWindow{
        CSS: `:root { --accent: #ff6f00; --radius: 16px; }`,
    },
})

전체 변수 목록은 CSS 변수를 통한 테마 설정 섹션을 참조하세요.

11

백그라운드에서 자동 검사 실행

메뉴 클릭 대신 또는 메뉴 클릭과 함께 타이머로 검사하려면 다음과 같이 설정하세요.

go
app.Updater.Init(updater.Config{
    CurrentVersion: currentVersion,
    Providers:      []updater.Provider{gh},
    PublicKey:      updaterPublicKey,
    CheckInterval:  6 * time.Hour,
})

타이머가 실행될 때마다 수동으로 클릭했을 때와 동일한 CheckAndInstall 흐름이 실행됩니다. 실제로 업데이트가 발견될 때까지 주기적 검사를 표시하지 않으려면 Window: updater.WindowNone을 설정하세요. 그런 다음 표시할 UX를 직접 결정할 수 있도록 EventUpdateAvailable을 구독하세요.

완료되었습니다

이제 Wails 앱에서 다음 기능을 사용할 수 있습니다.

  • 요청 시 그리고 타이머에 따라 GitHub Releases에서 업데이트를 확인합니다.
  • 세련된 기본 창에서 릴리스 정보를 Markdown으로 렌더링합니다.
  • 게시한 SHA-256 다이제스트를 기준으로 다운로드를 검증합니다.
  • 선택적으로 빌드 시 포함한 공개 키를 기준으로 Ed25519 서명을 검증합니다.
  • 실행 중인 바이너리를 제자리에서 교체하고 자동으로 다시 실행합니다.

다음 단계

  • 업데이터 가이드에서 전체 API 레퍼런스, 모든 이벤트와 구성 옵션, 도우미 모드의 교체 메커니즘을 확인할 수 있습니다.
  • 복제하여 사용할 수 있는 완전한 동작 예제는 v3/examples/updater에서 확인하세요.
  • 테스트 대상 저장소 wailsapp/updater-demo에서 권장 릴리스 자산 구조를 확인할 수 있습니다.

프로덕션 환경에서 주의할 사항

  • macOS에서 코드 서명 — Gatekeeper는 교체되는 바이너리에 서명과 공증을 요구합니다. 릴리스용 ZIP 파일로 압축하기 전에 .app 번들에 서명하세요. 업데이터는 바이트를 그대로 보존하며 아무것도 다시 서명하지 않습니다.
  • Windows의 바이러스 백신 — 인터넷에서 다운로드한 서명되지 않은 .exe 파일은 SmartScreen 경고를 유발할 수 있습니다. Authenticode 인증서로 바이너리에 서명하세요. 그러지 않으면 제한이 엄격한 컴퓨터의 사용자가 앱을 허용 목록에 추가해야 할 수 있습니다.
  • 원자적 릴리스 — SHA256SUMS과 바이너리를 별도 커밋으로 나누지 말고 함께 게시하세요. 업데이터는 바이너리와 별도로 사이드카를 가져오므로, 둘이 일치하지 않으면 다이제스트 검사가 실패 시 차단 방식으로 종료됩니다.
  • 건너뛴 버전 — 기본 창의 “이 버전 건너뛰기” 버튼은 건너뛰기 설정을 로컬에 기록합니다. 중요한 보안 업데이트를 배포할 때는 이전 릴리스를 무시한 사용자의 환경에서 자동으로 건너뛰지 않도록 새 버전 번호를 지정하세요.
페이지 편집

마지막 업데이트: