# EXE 버전과 누적 패치 결정일: 2026-11-09. EXE 프로젝트의 버전을 **메이저 버전**과 **패치 버전**으로 나누고, 패치 버전마다 전체 설치 파일과 누적 패치 파일을 함께 만든다. 이 문서는 설계다. 버전 종류·ProductCode 상속·패치 제약 검사는 모델과 화면에 구현했지만 MSI·MSP 생성, Burn 번들, 실제 설치·패치 시험은 EXE 빌드와 함께 후속으로 구현한다. 버전 메타파일의 위치와 형식은 [프로젝트 모델](02-project-model.md), 설치 화면과 Burn 구조는 [14 EXE 설치 UI](14-exe-installer-ui.md)를 따른다. ## 결정 | 항목 | 결정 | | --- | --- | | 버전 종류 | 버전마다 `updateType`을 둔다. `major`는 새 ProductCode를 받고 이전 제품을 제거한 뒤 설치한다(메이저 업그레이드). `patch`는 아래쪽 메이저 버전의 ProductCode를 그대로 쓴다(마이너 업그레이드). 기존 버전 파일에 값이 없으면 `major`다. | | 라인 | 메이저 버전과, 그보다 높고 다음 메이저 버전보다 낮은 패치 버전들을 한 **라인**으로 본다. 예: 1.0.0(메이저, A)·1.0.1·1.0.2·1.0.3(패치, A) / 1.1.0(메이저, B)·1.1.1(패치, B). | | 패치 기준 | **누적 패치**. 1.0.3 패치 파일 하나가 같은 라인의 이전 버전 모두(1.0.0·1.0.1·1.0.2)를 1.0.3으로 올린다. 이전 패치를 대체(supersede)하므로 사용자는 최신 패치 하나만 설치한다. | | 산출물 | 메이저 버전: 전체 설치 파일 `setup.exe`. 패치 버전: 전체 설치 파일 `setup.exe`(새 PC에서 바로 그 버전 설치)와 패치 파일(누적 MSP와 이를 감싼 패치 EXE) 두 개. | | 설치 경로 | 같은 라인의 이전 버전이 설치된 PC는 **패치 파일로만** 업데이트한다. 전체 설치 파일은 그 경우 설치하지 않고 패치 파일을 안내한다. | | 기준 보존 | 패치가 올릴 이전 버전은 잠가야 한다. 잠긴 버전의 빌드 결과(MSI, `.wixpdb`, 실제 파일)는 패치 생성의 기준으로 보존하며 지우지 않는다. | ## 설치 동작 `setup.exe`와 패치 EXE는 Burn이 설치된 제품(UpgradeCode·ProductCode·버전)을 탐지한 뒤 다음처럼 동작한다. 1.0.3이 패치 버전인 경우다. | PC 상태 | 1.0.3 전체 설치 파일 | 1.0.3 패치 파일 | | --- | --- | --- | | 설치 없음 | 1.0.3을 새로 설치 | 적용 대상 없음, 전체 설치 파일 안내 | | 1.0.0·1.0.1·1.0.2 (같은 라인) | 설치하지 않고 패치 파일 안내 | 1.0.3으로 업데이트 | | 1.0.3 | 유지·복구 화면 | 이미 적용됨 | | 0.9.x (이전 라인, 다른 ProductCode) | 메이저 업그레이드: 0.9.x 제거 후 1.0.3 설치 | 적용 대상 없음, 전체 설치 파일 안내 | | 1.1.0 이상 (더 높은 버전) | 낮은 버전 설치 거부 | 적용 대상 없음 | 1.0.3 전체 설치 파일로 새로 설치한 PC도 같은 라인이다. 이후의 1.0.4 누적 패치는 1.0.3도 기준에 포함하므로 그대로 적용된다. 따라서 패치 버전의 전체 설치 파일도 출시하면 잠그고 기준으로 보존한다. ## 누적 패치 생성 - 패치 MSP는 같은 라인의 이전 버전마다 하나씩 기준(WiX `PatchBaseline`)을 두고, 각 기준의 빌드 결과에서 이번 버전으로 가는 변환을 한 파일에 담는다. - 같은 라인의 패치는 하나의 패치 계열(`PatchFamily`)로 묶고 순서 값을 올려, 새 패치가 이전 패치를 대체한다. - 기준의 빌드 결과는 잠근 시점의 버전 파일 해시, 공유 설정 해시, 파일 목록·해시와 함께 `releases/`에 둔다. 패치 빌드는 이 해시를 다시 확인하고, 다르면 `BaselineChanged`로 거부한다. 공유 설정(아키텍처·설치 폴더·설치 범위·UpgradeCode·속성)을 출시 후 바꾸면 그 라인의 패치를 만들 수 없다. - MSI 구성 요소 GUID는 설치 경로에서 결정되도록 만들어 같은 경로의 파일이 버전 사이에 같은 구성 요소로 유지되게 한다. ## 패치 제약 Windows Installer 패치는 이전 버전이 설치한 구성 요소를 지우거나 옮길 수 없다. 입력 검사(`validate_version_line`)는 버전 파일 수준에서 다음을 확인하며, 규칙은 그대로인데 소스 폴더에서 파일이 사라진 경우는 패치 빌드에서 기준 파일 목록과 비교해 거부한다. | 코드 | 조건 | | --- | --- | | `MissingMajorVersion` | 패치 버전보다 낮은 메이저 버전이 없다. | | `PatchProductCodeMismatch` | 패치의 ProductCode가 라인 메이저 버전과 다르다. | | `BaselineNotLocked` | 패치가 올릴 이전 버전 중 잠기지 않은 버전이 있다. | | `PatchRemovesFiles` | 이전 버전의 파일 규칙(ID 기준)을 지웠다. | | `PatchMovesFiles` | 같은 파일 규칙의 소스 또는 설치 위치를 바꿨다. | | `PatchRemovesRegistry` | 이전 버전의 레지스트리 값을 지웠거나 루트·뷰·키 경로·값 이름을 바꿨다. 형식·데이터 변경은 허용한다. | | `PatchRemovesShortcut` | 이전 버전의 바로가기를 지웠거나 위치·폴더·이름을 바꿨다. 대상·인수 변경은 허용한다. | | `ProductCodeReused` | 메이저 버전이 다른 메이저 버전의 ProductCode를 쓴다. | 파일·레지스트리·바로가기 추가, 포함·제외 패턴과 데이터 변경은 허용한다. 지우거나 옮겨야 하면 새 메이저 버전을 만든다. ## 제작기 화면 - 설치 구성 › 버전 목록에 **종류** 열(메이저 / 패치 · 라인 메이저 버전)을 둔다. - 버전 목록 오른쪽 위 **버전 추가** 팝업에서 새 버전 번호, **종류**(번호를 입력할 때마다 자동 선택: 아래쪽 메이저 버전과 Major·Minor가 같으면 패치, 아니면 메이저. 직접 바꿀 수 있지만 아래에 메이저 버전이 없는 최초 버전은 패치 선택지가 비활성화된다), 복사할 버전을 고르고 **만들기**로 추가한다. 패치는 라인 메이저 버전의 ProductCode를 받고, 낮은 메이저 버전이 없으면 `MissingMajorVersion`으로 추가하지 않는다. 첫 버전은 항상 메이저다. - 버전 › 일반 탭은 종류를 표시하고, 패치 버전의 ProductCode는 읽기 전용으로 보여 준다(새 GUID 버튼 없음). - 입력 검사는 선택한 버전에 대해 위 패치 제약을 함께 검사하고 해당 탭으로 이동한다. ## 구현 범위 | 항목 | 상태 | | --- | --- | | `ExeVersion.updateType`, `line_major`, `patch_baselines`, `validate_version_line` | 구현 | | 버전 목록 종류 열, 종류 선택 추가, ProductCode 상속, 일반 탭 표시 | 구현 | | 기준 빌드 결과 보존·해시 기록, `BaselineChanged` | 후속 (EXE 빌드) | | MSI·누적 MSP·Burn 전체 설치/패치 EXE 생성, 설치 상태별 안내 | 후속 (EXE 빌드) | | 같은 라인 PC에서 전체 설치 파일 거부, 패치 대체 순서 | 후속 (BA 구현) | | Windows VM 설치·패치·제거 시험 | 미실행 | ## 근거 - [Windows Installer: Patching and Upgrades](https://learn.microsoft.com/en-us/windows/win32/msi/patching-and-upgrades) - [Windows Installer: Changing the Product Code](https://learn.microsoft.com/en-us/windows/win32/msi/changing-the-product-code) - [Windows Installer: Minor Upgrades](https://learn.microsoft.com/en-us/windows/win32/msi/minor-upgrades) - [Windows Installer: Sequencing Patches](https://learn.microsoft.com/en-us/windows/win32/msi/sequencing-patches) - [Windows Installer: MsiPatchSequence Table](https://learn.microsoft.com/en-us/windows/win32/msi/msipatchsequence-table) - [WiX: Building patches](https://docs.firegiant.com/wix/tools/patches/) - [WiX: PatchBaseline](https://docs.firegiant.com/wix/schema/wxs/patchbaseline/)