Skip to main content
← 블로그

Tauri 삽질기 2탄 — DMG 하나 만드는 게 이렇게 어려울 줄이야

VauDium ·

빌드는 됐는데 DMG가 안 만들어지고, 만들어졌는데 아이콘이 사라지고, 열었더니 damaged. Tauri 데스크톱 앱 배포까지의 여정.

Tauri 삽질기 2탄 — DMG 하나 만드는 게 이렇게 어려울 줄이야

Fecit 데스크톱 앱이 돌아갑니다. Tauri v2로 감싼 웹 앱이 로컬에서는 잘 됩니다. 이제 사용자에게 전달하면 됩니다.

DMG 파일 하나 만들면 끝이라고 생각했습니다.

끝이 아니었습니다.

cargo가 없습니다

서버에서 빌드 스크립트를 돌렸습니다. 첫 번째 에러.

No such file or directory: cargo

Tauri는 Rust 기반입니다. 서버에 Rust가 없었습니다. rustup으로 설치하고 다시 실행했습니다.

이건 쉬웠습니다. 진짜 문제는 그 다음이었습니다.

DMG가 안 만들어진다

Tauri 빌드가 끝났습니다. .app도 만들어졌습니다. 그런데 DMG 패키징에서 멈춥니다.

failed to run bundle_dmg.sh

원인을 찾는 데 시간이 걸렸습니다. bundle_dmg.sh를 직접 실행해봤더니 create-dmg가 없다고 합니다. brew install create-dmg. 다시 실행.

이번엔 다른 에러.

AppleEvent timed out. (-1712)

Tauri의 DMG 번들러는 내부적으로 AppleScript를 써서 Finder에 아이콘을 예쁘게 배치합니다. 문제는 서버에 GUI 세션이 없다는 겁니다. SSH로 접속한 Mac에서 Finder를 원격 조작하려니 타임아웃이 나는 겁니다.

—skip-jenkins

--skip-jenkins 옵션을 발견했습니다. AppleScript를 건너뛰고 DMG를 만듭니다.

DMG가 만들어졌습니다. 열어봤습니다.

Applications 폴더 아이콘이 나타났다가 사라집니다.

AppleScript가 아이콘 배치를 담당하는데, 그걸 건너뛰니까 Finder가 뭘 보여줘야 할지 모르는 겁니다. DMG는 만들어졌지만 사용자 경험이 이상합니다.

결국 hdiutil

여러 방법을 시도했습니다. create-dmg의 옵션을 바꿔보고, staging 폴더를 만들어서 넘겨보고, || true로 에러를 무시해보고.

결국 가장 단순한 방법이 답이었습니다.

macOS에 기본 내장된 hdiutil로 직접 DMG를 만드는 겁니다. 임시 폴더에 앱과 Applications 심볼릭 링크를 넣고, hdiutil create로 압축 DMG를 한 번에 생성합니다. AppleScript도 없고, create-dmg도 필요 없습니다.

아이콘 배치가 기본 Finder 스타일이라 화려하진 않지만, Applications 폴더가 안정적으로 보이고 드래그 앤 드롭으로 설치할 수 있습니다.

그런데 damaged?

DMG를 열려고 하니까 “damaged” 경고가 뜹니다.

이건 DMG 문제가 아닙니다. macOS Gatekeeper가 서명되지 않은 앱을 차단하는 겁니다. Apple Developer 인증서로 코드 서명과 공증(notarize)을 해야 합니다. 이건 다음 과제로 남겨뒀습니다.

ARM은요?

서버가 Intel Mac입니다. 기본 빌드는 x64만 나옵니다. Apple Silicon 사용자를 위해 ARM 빌드도 필요합니다.

Rust의 크로스 컴파일이 여기서 빛났습니다. rustup target add aarch64-apple-darwin 한 줄이면 ARM 타겟이 추가되고, --target aarch64-apple-darwin으로 빌드하면 Intel Mac에서 ARM 바이너리가 나옵니다.

하나의 빌드 스크립트에서 x64와 aarch64 두 개의 DMG가 만들어집니다.

다운로드 페이지

DMG를 만들었으면 사용자가 받을 수 있어야 합니다. 랜딩 페이지에 Desktop 다운로드 버튼을 추가하고, /downloads/ 경로에 다운로드 페이지를 만들었습니다.

처음에는 빌드 스크립트에서 인라인 HTML을 생성했는데, 디자인을 바꿀 때마다 스크립트를 수정해야 해서 불편했습니다. 정적 HTML 파일로 분리하고, 빌드 스크립트는 downloads.json 매니페스트만 생성하도록 바꿨습니다. 페이지가 매니페스트를 fetch해서 카드형 UI로 렌더링합니다.

스크립트 분리

처음에는 하나의 deploy-desktop.sh에 웹 빌드, Tauri 빌드, DMG 생성, 다운로드 페이지 생성이 전부 들어있었습니다. Tauri 빌드가 실패하면 뒤의 웹 배포까지 같이 멈춥니다.

deploy-desktop.sh는 웹 앱 빌드와 배포만, build-tauri.sh는 Tauri 네이티브 빌드 + DMG 생성 + 다운로드 파일 배치로 분리했습니다. 웹 앱 업데이트와 네이티브 빌드를 독립적으로 돌릴 수 있게 됐습니다.

돌아보면

DMG 하나 만드는 데 이렇게 많은 삽질이 필요할 줄 몰랐습니다. cargo 설치부터 AppleScript 타임아웃, Gatekeeper 차단, ARM 크로스 컴파일까지. 웹 앱을 Tauri로 감싸는 건 쉬웠는데, “사용자에게 전달하는 것”은 전혀 다른 문제였습니다.

아직 코드 서명과 공증이 남아있습니다. 하지만 빌드 파이프라인은 자리를 잡았고, 버전이 올라갈 때마다 스크립트 하나로 x64 + ARM DMG가 나오고, 다운로드 페이지에 자동으로 반영됩니다.

다음은 Developer ID Application 인증서를 설치하고, 서명된 DMG를 만드는 일입니다. “damaged” 경고 없이 깔끔하게 열리는 그날까지.