- Design 14: WiX MSI + Burn with a C# BA host and WebView2 web UI, default screen catalog, installer-ui.json and the WebView2 fallback. - Design 16: Smart App Control checks and what must be signed. - Manual: beginner guide to Azure Artifact Signing. - Link the decision from designs 09 and 10 and record it. Refs #15 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
19 KiB
EXE 설치 UI: WebView2 BA와 기본 화면
결정일: 2026-11-09. EXE 설치 파일(setup.exe)의 화면을 사용자가 자유롭게 꾸밀 수 있게 하는 구조와 기본으로 제공할 화면 목록을 정한다. 이 문서는 설계이며 BA·웹 UI 구현, Burn 연결, Windows 설치 시험은 수행하지 않았다. MSIX는 Windows가 설치 UI를 관리하므로 이 문서의 대상이 아니다.
09 설치 과정과 UI 편집의 단계·데이터 계약과 10 설치 마법사 재설계의 모드별 흐름·실패 처리 원칙은 유지한다. 아래 결정이 두 문서와 다르면 이 문서를 따른다.
결정
| 항목 | 결정 |
|---|---|
| 설치 패키지 | WiX로 만든 MSI를 Burn 번들(setup.exe)로 감싼다. WiX 버전과 이용 조건(OSMF)은 채택 전에 확정한다. |
| 설치 UI | 사용자 정의 Burn BA를 사용한다. BA 호스트는 C#(.NET Framework 4.8)로 만들고 화면은 WebView2가 표시하는 HTML/CSS/TypeScript 웹 UI로 만든다. |
| 제품별 차이 | BA 호스트와 웹 UI는 공통으로 한 번 빌드·서명한다. 제품마다 다른 화면 설정은 installer-ui.json과 이미지·약관 자산으로만 전달한다. |
| 기본 화면 | 아래 기본 화면 카탈로그를 표준으로 제공한다. 제작자는 문구·자산·표시 여부·일부 순서를 편집한다. |
| WebView2가 없을 때 | 같은 BA 안의 단순한 네이티브 기본 화면(WPF)으로 설치를 진행한다. 인터넷이 되면 WebView2 웹 부트스트래퍼를 BA 선행 패키지로 먼저 설치할 수 있다. 고정 버전 런타임 포함은 기본값이 아니다. |
| 무인 설치 | /quiet·/passive에서는 WebView2를 만들지 않고 같은 검증과 기본값으로 설치한다. |
| 제외 | Rust/Tauri로 BA를 직접 구현하는 방식(09의 "Rust/Tauri 사용자 정의 설치 UI")과 WixStdBA 테마 편집을 첫 구현으로 삼는 순서는 채택하지 않는다. WiX가 C++·.NET BA API만 공식 제공하기 때문이다. |
구조
setup.exe (Burn 번들, 서명)
├─ Burn 엔진 ........ 탐지 → 계획 → 적용, UAC 권한 상승, 롤백, 프로그램 추가/제거 등록
├─ BA 호스트 (C#, .NET Framework 4.8)
│ ├─ WebView2 ── 웹 UI 표시
│ ├─ 기본 네이티브 화면 ── WebView2를 쓸 수 없을 때
│ └─ 브리지 ── 웹 UI ↔ Burn 메시지 중계, 입력 재검증
├─ 웹 UI (Vite 빌드 결과: HTML/CSS/JS)
├─ installer-ui.json + 로고·이미지·약관
├─ 선행 프로그램 패키지
└─ 제품 MSI
WiX v5부터 Burn은 BA를 별도 실행 파일로 띄운다. BA 호스트는 WiX가 제공하는 .NET BA 라이브러리로 Burn과 통신한다. 실제 파일·레지스트리 변경은 Burn과 MSI가 수행하며 BA나 웹 UI가 msiexec를 따로 실행하지 않는다.
Installer Studio도 WebView2(Tauri)를 쓰므로 같은 웹 UI에 가짜 브리지를 연결해 제작 화면에서 미리보기를 제공한다. 미리보기는 시스템을 변경하지 않으며, 최종 판정은 실제 번들의 VM 시험으로 한다.
실행 흐름
- Burn이 BA 호스트를 실행한다. 대화형 모드면 호스트가 창을 만들고 WebView2 사용 가능 여부를 확인한다.
- 웹 UI는 번들 안의 파일만 가상 호스트(
https://installer.local/)로 읽는다. 외부 주소 이동, 새 창, 다운로드는 차단한다. - Burn 탐지 결과(설치 여부, 설치된 버전, 기능 상태, 선행 프로그램 필요 여부)를 웹 UI에 보내고, 웹 UI는 신규 설치·업그레이드·유지 관리 중 맞는 흐름을 연다.
- 사용자가
review에서 시작을 누르면 웹 UI가apply메시지를 보낸다. 호스트는 경로 규칙, 허용 기능, 약관 동의를 다시 검사한 뒤 Burn 변수를 설정하고 계획·적용을 요청한다. - Burn의 진행·오류·재부팅·취소 이벤트를 웹 UI에 전달한다. 적용이 시작된 뒤에는 선택값과 계획을 바꾸지 않는다.
- 완료 화면의 "앱 실행"은 원래 사용자 권한으로 실행한다.
브리지 메시지
웹 UI는 아래 형식의 메시지만 보낼 수 있다. 호스트는 알 수 없는 type, 형식이 틀린 값, 현재 상태에서 허용되지 않는 요청을 거부한다.
| 방향 | type |
내용 |
|---|---|---|
| 호스트 → UI | init |
installer-ui.json, 선택 언어, 실행 모드(대화형) |
| 호스트 → UI | detected |
설치 상태(absent/older/same/newer), 설치된 버전·경로, 기능 상태, 필요한 선행 프로그램, 차단 사유 |
| 호스트 → UI | validation |
입력 검증 결과(필드별 오류 코드와 문구) |
| 호스트 → UI | progress |
전체 진행률, 현재 패키지·작업 이름 |
| 호스트 → UI | filesInUse |
사용 중인 프로그램 목록 |
| 호스트 → UI | result |
succeeded/failed/cancelled, 재부팅 필요 여부, 오류 코드, 로그 경로 |
| UI → 호스트 | validate |
설치 폴더, 기능 선택 등 입력 검증 요청 |
| UI → 호스트 | browseFolder |
폴더 선택 대화상자 요청. 결과 경로도 호스트가 검증 |
| UI → 호스트 | apply |
동작(install/modify/repair/uninstall), 설치 폴더, 기능, 추가 작업, 약관 동의 |
| UI → 호스트 | cancel |
취소 요청. 처리 결과는 result로 확인 |
| UI → 호스트 | retryFilesInUse / closeApplications |
사용 중인 프로그램 다시 확인. 강제 종료는 기본 동작이 아니다. |
| UI → 호스트 | launchApp, openLink, restart, close |
완료 후 작업. openLink는 installer-ui.json에 등록된 주소만 허용 |
기본 화면 카탈로그
모든 화면은 공통 틀(상단 로고·제품명·단계 표시, 본문, 하단 이전/다음·실행/취소 버튼)을 쓴다. 언어는 한국어·영어를 기본으로 하고 시스템 언어로 자동 선택한다.
신규 설치 흐름
| 순서 | 화면 ID | 내용 | 숨김 |
|---|---|---|---|
| 1 | welcome |
제품명·버전·로고, 안내 문구. 이전 버전이 있으면 업그레이드 안내 | 가능 |
| 2 | license |
약관 전문과 동의. 동의해야 다음으로 진행 | 약관이 없을 때만 |
| 3 | installFolder |
기본 설치 폴더, 찾아보기, 필요·사용 가능 공간 | 가능. 숨기면 기본 경로 |
| 4 | features |
기능 트리와 용량. 필수 기능은 해제 불가 | 가능. 숨기면 기본 선택 |
| 5 | tasks |
바탕화면 바로가기, 파일 연결 등 선택 작업 | 가능. 숨기면 기본값 |
| 6 | review |
경로, 기능, 선행 프로그램, 필요한 권한 요약과 시작 버튼 | 불가 |
| 7 | progress |
현재 작업, 전체 진행률, 취소 | 불가 |
유지 관리 흐름
이미 설치된 PC에서 setup.exe를 실행하거나 프로그램 추가/제거에서 변경을 선택할 때 사용한다.
| 화면 ID | 내용 | 다음 화면 |
|---|---|---|
maintenance |
수정, 복구, 제거 선택 | 수정: features → review, 복구·제거: review 또는 uninstallConfirm |
uninstallConfirm |
제거할 항목과 남는 항목(사용자 도면·설정·백업) 안내, 확인 | progress |
공통 상태 화면
조건에 따라 표시하며 숨길 수 없다.
| 화면 ID | 조건 | 내용 |
|---|---|---|
blocked |
시작 전 검사 실패 | 더 높은 버전 설치됨, 지원하지 않는 OS, 디스크 부족 등 원인과 조치 |
filesInUse |
설치할 파일을 쓰는 프로그램이 있음 | 저장 후 종료 안내, 다시 확인 |
finish |
성공 | 결과 요약, 앱 실행 선택, 링크 |
rebootRequired |
성공했지만 재부팅 필요 | 이유, 지금/나중에 선택. 기본은 나중에 |
failure |
오류 | 실패 단계, 기존 제품 상태, 로그 위치, 다음 조치 |
cancelled |
사용자 취소 | 취소 시점과 엔진이 확인한 실제 상태 |
편집 규칙
- 화면을 숨겨도 그 단계의 작업은 기본값으로 실행한다. 숨김은 표시 여부만 바꾼다.
license는 약관이 지정되어 있으면 숨길 수 없다. 숨김을 동의로 처리하지 않는다. 무인 설치의 동의 전달은 별도 인자로 정한다.- 순서 변경은
welcome~tasks사이의 선택 화면에만 허용한다.review이후와 상태 화면은 고정한다. - 표시하는 입력은 실제 설치 정의와 연결되어야 한다. 예를 들어
tasks의 바로가기 항목은 MSI 바로가기 정의가 있어야 하며, 연결이 없으면 빌드 오류로 처리한다.
| 대상 | 편집 가능 항목 |
|---|---|
| 모든 화면 | 제목, 설명, 다국어 문구, 로고, 강조 색, 밝기 테마 |
welcome |
대표 이미지, 안내 문구 |
license |
약관 파일(RTF/TXT/HTML), 동의 문구 |
installFolder |
기본 경로(프로젝트 formats.exe.installRoot), 경로 변경 허용 여부 |
features |
기능 이름·설명·기본 선택·필수 여부 |
tasks |
항목 표시, 기본 선택 상태 |
finish |
앱 실행 표시와 기본 선택, 링크 목록 |
installer-ui.json
빌드 시 프로젝트 설정에서 생성해 번들에 넣는 파일이다. 제작기에서는 버전 메타파일의 installerUi로 편집하며(제작기 설치화면 편집), 이 절은 번들에 넣는 생성 결과의 형식을 정한다. 자산 경로는 프로젝트 폴더 기준의 안전한 상대 경로만 허용한다.
{
"schemaVersion": 1,
"template": { "id": "standard-wizard", "version": "1.0.0" },
"theme": {
"logo": "assets/logo.png",
"welcomeImage": "assets/welcome.png",
"accentColor": "#7257C5",
"colorScheme": "dark"
},
"locales": { "default": "ko-KR", "available": ["ko-KR", "en-US"] },
"strings": {
"ko-KR": { "welcome.title": "EG-BIM 설치를 시작합니다" },
"en-US": { "welcome.title": "Welcome to EG-BIM Setup" }
},
"pages": [
{ "id": "welcome", "visible": true },
{ "id": "license", "visible": true, "file": { "ko-KR": "assets/license.ko.rtf", "en-US": "assets/license.en.rtf" } },
{ "id": "installFolder", "visible": true, "allowChange": true },
{ "id": "features", "visible": false },
{ "id": "tasks", "visible": true },
{ "id": "review" },
{ "id": "progress" }
],
"finish": {
"launchApp": { "visible": true, "defaultChecked": true },
"links": [{ "label": "release.notes", "url": "https://example.com/egbim/release-notes" }]
}
}
- 알 수 없는 필드, 카탈로그에 없는 화면 ID, 숨길 수 없는 화면의
visible: false, 허용 범위 밖의 순서는 빌드 전에 거부한다. - 상태 화면은
pages에 적지 않는다. 문구만strings로 바꾼다. url은https만 허용하고openLink요청은 이 목록과 비교한다.- 빌드 스냅샷에는
installer-ui.json과 모든 자산의 SHA-256을 기록한다. 서명한setup.exe의 화면을 외부 파일로 바꾸지 않는다.
제작기 설치화면 편집 (2026-11-09)
설치 구성 › 설치화면 탭에서 버전마다 설치 단계·화면 문구·모양·스크립트를 편집한다. 값은 버전 메타파일(.isver)의 installerUi에 저장하고, 버전을 추가하면 함께 복사하며, 잠긴 버전은 읽기 전용이다. 이 필드가 없는 기존 버전 파일은 아래 표준 마법사로 연다. 빌드 시 이 값에서 installer-ui.json과 MSI 사용자 지정 동작을 만든다(EXE 빌드와 함께 후속 구현).
| 항목 | 규칙 |
|---|---|
| 표준 마법사 | welcome, installFolder, review, progress, finish 다음에 maintenance, uninstallConfirm, failure, cancelled. 이 9개는 지울 수 없다. |
| 단계 추가 | license(한 번만, 약관 파일 필수), info(안내 화면: 제목·설명), input(입력 화면). 추가한 단계는 review 바로 앞에 들어가며 삭제할 수 있다. |
| 순서 | welcome·license·installFolder·info·input만 review 앞에서 순서를 바꾼다. review·progress·finish와 유지 관리·결과 화면은 고정이다(InvalidPageOrder). |
| 숨김 | welcome·installFolder·info·input만 숨긴다. 숨겨도 그 단계는 기본값으로 진행한다(PageCannotBeHidden). |
| 문구 | 제목·설명은 서식 문자열이며 [ProductName] 같은 속성문자를 쓴다. 정의되지 않은 속성은 UnknownProperty. |
| 입력 화면 | 항목마다 저장할 사용자 속성(프로젝트 설정 › 속성문자), 이름, 형식(text·checkbox·choice), 기본값, 선택 목록, 필수 여부를 둔다. 같은 속성을 두 항목이 쓰지 않는다(DuplicateInputProperty). 입력값은 레지스트리·바로가기·스크립트 인수에서 [속성]으로 쓴다. |
| 모양 | 밝기(dark/light), 강조 색(#RRGGBB), 로고(프로젝트 폴더 기준 PNG·JPG·SVG). 약관 파일은 RTF·TXT·HTML. 파일이 없으면 MissingAsset. |
| 설치 폴더 | fixedFolder를 켜면 기본 설치 폴더를 바꿀 수 없다. |
스크립트
| 항목 | 규칙 |
|---|---|
| 종류 | powershell(.ps1), cmd(.cmd·.bat), exe(.exe). 확장자가 다르면 ScriptKindMismatch. 화면에 스크립트 본문을 입력하지 않는다. |
| 파일 | 프로젝트 소스(alias) 폴더 안의 / 경로. 설치 파일과 별도로 번들에 포함하므로 설치되지 않는 파일도 쓸 수 있다. 없으면 MissingScriptFile. 빌드 스냅샷에 SHA-256을 기록한다. |
| 실행 시점 | beforeFiles(설치: 파일 복사 전), afterFiles(설치: 파일 복사 후), beforeUninstall(제거: 파일 삭제 전)은 MSI 지연 사용자 지정 동작으로 설치 엔진 권한(관리자)으로 실행한다. afterFinish(완료 후)는 BA가 완료 화면 뒤에 로그인 사용자 권한으로 실행한다. |
| 인수 | 한 줄에 하나씩 배열로 전달하고 명령 문자열로 합치지 않는다. PowerShell은 powershell.exe -NoProfile -ExecutionPolicy Bypass -File <파일> <인수...>, CMD는 cmd.exe /d /c <파일> <인수...> 형태로 실행하며 인수는 따옴표 규칙에 맞게 하나씩 이스케이프한다. 각 인수는 서식 문자열이다. |
| 실패 처리 | fail은 0이 아닌 종료 코드·제한 시간 초과에서 설치를 중단하고 설치 엔진의 변경을 되돌린다. 스크립트가 직접 바꾼 내용은 되돌리지 않는다. ignore는 로그에 남기고 계속한다. afterFinish는 ignore만 허용한다(ScriptFailureAfterFinish). |
| 제한 시간 | 1~3600초(기본 600). 시간이 지나면 프로세스 트리를 종료한다. |
| 로그 | 스크립트 이름, 시점, 종료 코드, 소요 시간을 설치 로그에 남긴다. 출력에 비밀 값을 쓰지 않도록 안내한다. |
패치 버전도 같은 스크립트를 가질 수 있으며 패치 적용 시 실행된다. 스크립트 실행은 Windows VM 설치·제거·패치 시험 전에는 검증된 것으로 표시하지 않는다.
WebView2 가용성
Windows 11과 대부분의 Windows 10에는 WebView2 Evergreen 런타임이 있지만 LTSC, Server, 업데이트가 막힌 오프라인 PC에는 없을 수 있다.
| 상황 | 동작 |
|---|---|
| WebView2 있음 | 웹 UI 표시 |
| 없음 + 인터넷 사용 가능 | 번들 설정이 허용하면 WebView2 웹 부트스트래퍼를 BA 선행 패키지로 설치한 뒤 웹 UI 표시. 실패하면 기본 네이티브 화면 |
| 없음 + 오프라인 | 기본 네이티브 화면으로 같은 흐름 진행 |
| 무인 설치 | WebView2를 사용하지 않음 |
기본 네이티브 화면은 같은 카탈로그의 필수 화면(welcome, license, installFolder, review, progress, 상태 화면)과 같은 검증을 제공하며 디자인 편집은 문구·로고로 제한한다. WebView2 사용자 데이터 폴더는 실행마다 임시 폴더에 만들고 종료 시 삭제한다.
보안
- 웹 UI는 번들 안 정적 파일만 사용한다. 원격 스크립트, 외부 CDN,
eval을 쓰지 않고 CSP는default-src 'self'로 둔다. - 릴리스 빌드는 개발자 도구, 상황 메뉴, 확대·축소 단축키를 끈다.
- 웹 UI는 파일·레지스트리·프로세스에 직접 접근하지 않는다. 모든 요청은 정해진 메시지로 호스트를 거치고 호스트가 다시 검증한다.
- BA와 웹 UI는 일반 사용자 권한으로 실행한다. 권한 상승은 Burn이 관리하며 UAC 화면은 꾸미지 않는다.
- 로그와 메시지에 토큰·비밀번호를 넣지 않는다.
1단계 범위와 검증 실험
첫 구현은 신규 설치·업그레이드·제거와 실패·취소를 확인할 수 있는 화면 10개로 한다.
welcome, license, installFolder, review, progress, finish, failure, cancelled, maintenance, uninstallConfirm
features, tasks, filesInUse, rebootRequired, blocked는 MSI 기능·바로가기·파일 연결을 프로젝트 모델에 넣는 다음 단계에서 추가한다.
본 구현 전에 백로그 BL-08의 검증 실험을 VM에서 수행한다. MSI 1개와 선행 프로그램 1개를 담은 Burn 번들에 C# 호스트와 웹 UI를 붙이고 아래를 확인한다.
- 신규 설치, 같은 UpgradeCode의 상위 버전 업그레이드, 제거, 복구
- 설치 폴더 변경과 잘못된 경로 거부
- 진행률 표시, 적용 중 취소와 롤백 결과 표시
- 실패 화면과 로그 위치
/quiet무인 설치에서 WebView2 미사용- WebView2가 없는 PC에서 기본 네이티브 화면으로 설치 완료
- 고해상도(DPI), 키보드 이동, 스크린 리더 읽기
실험 결과는 src/tests/verification-YYYY-MM-DD-<slug>.json에 실행·미실행 항목을 모두 기록한다. 실험 전에는 이 구조를 검증된 것으로 표시하지 않는다.
미결정 사항
- WiX 버전(v6 또는 v7)과 OSMF 이용 조건 수용 여부
- 지원 대상 PC 범위(Windows 10 LTSC·Server·오프라인 PC 포함 여부)와 WebView2 웹 부트스트래퍼 사용 허용 여부
- 약관 파일 형식과 무인 설치의 동의 전달 방식
- 기본 지원 언어 목록
- 기본 네이티브 화면을 WPF로 할지, 같은 호스트의 최소 Win32 화면으로 할지
근거
- FireGiant — Out-of-process bootstrapper applications: WiX v5부터 BA를 별도 실행 파일로 실행, C++·.NET BA API 제공
- FireGiant — WixStdBA: 표준 BA 테마의 지원 범위
- Microsoft Learn — Distribute your app and the WebView2 Runtime: Evergreen·고정 버전 런타임 배포 방식
- Microsoft Learn — Overview of WebView2 features and APIs: 가상 호스트 매핑, 웹 메시지 통신
- Inno Setup — WizardStyle, Revenera — Defining InstallShield Prerequisites: 기본 화면 구성 참고