onedrive를 사용하는, 업데이트 서버 없이 WPF 앱을 배포하는 방법

 



https://github.com/tobony/template_dotnet_updater_from_onedrive_sharepoint/


OneDrive for Business 기반 자동업데이트 템플릿을 만들며

WPF 앱을 만들다 보면 개발보다 더 오래 붙잡게 되는 일이 있다.
바로 업데이트와 배포다.

기능은 다 만들었다.
내 PC에서는 잘 실행된다.
그런데 실제 사용자는 어떻게 새 버전을 받을까?

처음에는 단순하게 생각할 수 있다.
새로 빌드한 exe 파일을 공유 폴더에 올려두거나, 메일이나 메신저로 전달하면 된다. 사용자가 직접 내려받아 교체하면 끝이라고 생각하기 쉽다.

하지만 앱을 계속 운영하다 보면 이 방식은 금방 한계를 드러낸다.

누군가는 최신 버전을 쓰고, 누군가는 예전 버전을 계속 쓴다.
문제가 생기면 먼저 “지금 어떤 버전을 쓰고 있나요?”부터 확인해야 한다.
파일을 교체하다가 꼬이면 복구도 번거롭다.

작은 앱이라도 운영이 시작되면 배포는 더 이상 작은 문제가 아니다.




MS 365 환경에서 생긴 현실적인 고민

내가 고민했던 환경은 Microsoft 365를 이미 사용하고 있는 조직이었다.

OneDrive for Business와 SharePoint는 이미 쓰고 있었다.
파일 공유도 가능했다.
그렇다면 별도의 업데이트 서버를 새로 만들지 않고, 이 환경을 활용해 WPF 앱을 배포할 수 있지 않을까?

물론 WPF 앱 배포에는 여러 방법이 있다.

ClickOnce를 쓸 수도 있고, MSIX를 고려할 수도 있다.
Squirrel.Windows 같은 도구를 사용할 수도 있다.
아예 별도의 업데이트 서버를 만들거나 GitHub Releases를 활용하는 방법도 있다.

하지만 내가 원한 것은 조금 달랐다.

  • 별도 서버를 운영하고 싶지는 않았다.
  • 이미 있는 MS 365 환경을 활용하고 싶었다.
  • 사용자가 복잡한 설치 과정을 거치지 않기를 바랐다.
  • 작은 팀이나 사내 도구에 맞는 단순한 구조가 필요했다.
  • 새 버전을 올리고 update.json만 바꾸면 업데이트가 되기를 원했다.

그래서 만든 것이 OneDrive for Business를 활용한 WPF 자동업데이트 템플릿이다.



핵심 아이디어

구조는 단순하다.

OneDrive for Business에 업데이트 파일과 update.json을 올려둔다.
WPF 앱은 실행될 때 update.json을 확인한다.
서버에 올라간 버전이 현재 앱보다 높으면 업데이트를 안내하고, 사용자가 확인하면 새 파일을 내려받는다.

전체 흐름은 다음과 같다.

WPF 앱 실행
→ update.json 다운로드
→ 현재 버전과 서버 버전 비교
→ 업데이트 필요 여부 확인
→ 업데이트 다이얼로그 표시
→ 새 파일 다운로드
→ SHA256 해시 검증
→ 기존 파일 백업
→ 새 파일로 교체
→ 앱 재시작

말하자면 OneDrive for Business를 간단한 업데이트 저장소처럼 사용하는 방식이다.
거창한 배포 시스템은 아니지만, 작은 팀이나 사내 앱에는 충분히 현실적인 출발점이 될 수 있다.




이 템플릿이 해결하려는 문제

이 템플릿은 단순히 “파일을 내려받는 코드”가 아니다.
WPF 앱을 실제로 배포할 때 자주 마주치는 문제를 줄이기 위해 만들었다.

1. 사용자가 직접 업데이트를 챙기지 않아도 된다

앱이 시작될 때 자동으로 새 버전을 확인한다.
사용자가 매번 공유 폴더에 들어가 새 파일이 있는지 확인할 필요가 없다.

원하면 버튼을 눌러 수동으로 업데이트를 확인할 수도 있다.
자동 확인이 실패했거나, 사용자가 직접 확인하고 싶을 때를 위한 기능이다.


2. 다운로드 진행 상황을 볼 수 있다

업데이트 파일을 내려받는 동안 진행률을 표시한다.

작은 기능처럼 보이지만 사용자 입장에서는 중요하다.
진행률이 없으면 앱이 멈춘 것처럼 느껴질 수 있다.
특히 zip 파일처럼 크기가 있는 파일을 받을 때는 진행 상황을 보여주는 것만으로도 사용자의 불안이 줄어든다.


3. SHA256 해시로 파일을 검증한다

업데이트 파일을 내려받았다고 해서 곧바로 실행하거나 교체하는 것은 위험할 수 있다.
파일이 손상되었을 수도 있고, 의도한 파일이 아닐 수도 있다.

그래서 SHA256 해시 검증을 넣었다.

update.json에 해시값을 함께 적어두고, 앱이 다운로드한 파일의 해시와 비교한다.
값이 다르면 업데이트를 중단한다.

{
  "version": "1.1.0",
  "fileName": "MyApp.exe",
  "shareId": "OneDrive_파일의_shareId",
  "hash": "sha256:a1b2c3d4e5f6...",
  "mandatory": false,
  "changelog": "변경 내용을 여기에 작성"
}

물론 내부 테스트용이라면 해시 검증을 생략할 수도 있다.
하지만 실제 배포에서는 가능하면 사용하는 편이 좋다.


4. 강제 업데이트와 선택 업데이트를 나눌 수 있다

모든 업데이트가 같은 중요도를 갖지는 않는다.

단순한 UI 개선이나 작은 기능 추가라면 사용자가 나중에 업데이트해도 된다.
하지만 중요한 오류 수정이나 보안 관련 수정이라면 반드시 업데이트해야 할 수 있다.

그래서 mandatory 값을 통해 강제 업데이트와 선택 업데이트를 구분했다.

{
  "mandatory": true
}

mandatorytrue이면 사용자가 업데이트를 건너뛸 수 없다.
반대로 false이면 사용자가 나중에 업데이트하도록 둘 수 있다.


5. single exe와 zip 배포를 모두 지원한다

WPF 앱을 배포할 때 상황은 두 가지로 나뉠 수 있다.

단일 실행 파일 하나만 교체하면 되는 경우가 있고,
exe와 함께 여러 파일을 같이 배포해야 하는 경우도 있다.

그래서 이 템플릿은 두 가지 방식을 모두 지원하도록 만들었다.

  • MyApp.exe 단일 파일 배포
  • MyApp_1.2.0.zip 압축 파일 배포

zip 배포를 사용할 경우, 압축 파일 안에 exe와 필요한 파일을 함께 넣으면 된다.
업데이트 시 zip을 풀어 앱 디렉터리에 덮어쓰는 방식으로 동작한다.

예를 들어 zip 배포용 update.json은 다음처럼 작성할 수 있다.

{
  "version": "1.2.0",
  "fileName": "MyApp_1.2.0.zip",
  "shareId": "zip파일의_shareId",
  "hash": "sha256:zip파일의_해시값",
  "mandatory": false,
  "changelog": "zip 배포 테스트"
}

6. 업데이트 실패 시 롤백할 수 있다

업데이트에서 가장 부담스러운 순간은 파일 교체다.
새 파일로 바꾸는 도중 문제가 생기면 앱이 실행되지 않을 수도 있다.

그래서 기존 실행 파일을 .bak 파일로 백업한 뒤 새 파일로 교체하도록 했다.
문제가 생겼을 때 기존 파일로 되돌릴 수 있는 여지를 남기는 것이다.

자동업데이트에서 롤백은 선택 기능처럼 보이지만, 실제 운영에서는 꽤 중요한 안전장치다.


프로젝트 구조

템플릿의 기본 구조는 다음과 같다.

MyApp/
├── MyApp.csproj           ← 프로젝트 설정, 버전, publish 설정
├── App.xaml / App.xaml.cs
├── MainWindow.xaml/.cs    ← 메인 화면, 버전 표시, 업데이트 버튼
├── UpdateDialog.xaml/.cs  ← 업데이트 다이얼로그, 진행률, 변경 내용
├── UpdateInfo.cs          ← update.json 모델
└── UpdateService.cs       ← 업데이트 핵심 로직

핵심은 UpdateService.cs다.
이 파일에서 업데이트 정보 다운로드, 버전 비교, 파일 다운로드, 해시 검증, 파일 교체 흐름을 처리한다.

앱 화면은 단순하게 구성했다.
현재 버전을 보여주고, 사용자가 직접 업데이트 확인 버튼을 누를 수 있도록 했다.
업데이트가 필요하면 다이얼로그를 띄워 변경 내용과 진행률을 보여준다.


update.json의 역할

이 구조에서 가장 중요한 파일은 update.json이다.

앱은 이 파일을 읽고 다음 정보를 확인한다.

  • 새 버전 번호
  • 다운로드할 파일명
  • OneDrive 공유 파일의 shareId
  • SHA256 해시값
  • 강제 업데이트 여부
  • 변경 내용

예시는 다음과 같다.

{
  "version": "1.1.0",
  "fileName": "MyApp.exe",
  "shareId": "여기에_exe파일의_shareId",
  "hash": "sha256:여기에_해시값",
  "mandatory": false,
  "changelog": "변경 내용을 여기에 작성"
}

각 필드의 의미는 이렇다.

필드 설명
version 새 버전 번호. 현재 앱보다 높아야 업데이트가 감지된다.
fileName 다운로드할 파일명. exe 또는 zip 파일을 사용할 수 있다.
shareId OneDrive 공유 링크에서 추출한 파일 ID다.
hash sha256:해시값 형식으로 입력한다. 비워두면 검증을 생략할 수 있다.
mandatory true이면 강제 업데이트가 된다.
changelog 업데이트 다이얼로그에 표시할 변경 내용이다.

이 파일만 잘 관리하면 새 버전을 배포하는 과정이 단순해진다.


배포 흐름

새 버전을 배포하는 흐름은 대략 다음과 같다.

1. MyApp.csproj에서 버전 번호를 올린다.
2. dotnet publish -c Release로 빌드한다.
3. 생성된 exe 또는 zip 파일을 OneDrive for Business에 업로드한다.
4. 업로드한 파일의 공유 링크를 만든다.
5. 공유 링크에서 shareId를 추출한다.
6. SHA256 해시값을 만든다.
7. update.json의 version, shareId, hash, changelog를 수정한다.
8. update.json을 OneDrive에 다시 업로드한다.

이후 사용자가 앱을 실행하면 앱이 update.json을 확인한다.
새 버전이 있으면 업데이트 다이얼로그가 뜨고, 사용자가 확인하면 다운로드와 교체가 진행된다.


OneDrive 공유 링크에서 shareId 추출하기

OneDrive 공유 링크는 대략 이런 형태다.

https://tenant-my.sharepoint.com/:u:/g/personal/user_tenant_onmicrosoft_com/IQBo2mgEO12oTpxVCCBwF3jcAa50fA9CiomljfHfsnHWMHg?e=JeAjun

여기서 /g/personal/.../ 뒤에 있는 문자열이 shareId다.

IQBo2mgEO12oTpxVCCBwF3jcAa50fA9CiomljfHfsnHWMHg

?e= 이후의 값은 제외한다.

shareIdupdate.json에 넣으면 앱이 해당 파일을 다운로드할 수 있다.


SHA256 해시값 만들기

PowerShell에서는 다음 명령으로 SHA256 해시를 만들 수 있다.

(Get-FileHash .\MyApp.exe -Algorithm SHA256).Hash.ToLower()

생성된 값을 update.json에 다음처럼 넣는다.

"hash": "sha256:a1b2c3d4e5f6..."

해시 검증을 생략하고 싶다면 빈 문자열로 둘 수 있다.

"hash": ""

하지만 가능하면 해시값을 넣는 것을 권장한다.
업데이트 파일이 올바르게 내려받아졌는지 확인하는 최소한의 안전장치이기 때문이다.


MS 365 환경에서 주의할 점

OneDrive for Business를 업데이트 저장소처럼 사용하는 방식은 편리하다.
하지만 몇 가지 주의할 점이 있다.

1. 공유 링크 권한을 확인해야 한다

앱에서 파일을 다운로드하려면 OneDrive 공유 링크가 앱에서 접근 가능한 상태여야 한다.

원문 기준으로는 공유 설정을 **“링크가 있는 모든 사용자”**로 해야 한다.
조직 내부 전용 링크로 설정하면 인증이 필요해지고, 앱에서 다운로드할 때 403 오류가 날 수 있다.

다만 회사에 따라 “링크가 있는 모든 사용자” 옵션이 막혀 있을 수 있다.
이 경우에는 조직의 보안 정책을 먼저 확인해야 한다.


2. 조직 보안 정책의 영향을 받는다

MS 365 환경은 조직마다 설정이 다르다.

어떤 조직은 외부 공유가 자유롭고,
어떤 조직은 외부 공유가 제한되어 있다.
또 어떤 조직은 특정 파일 형식의 다운로드를 막을 수도 있다.

따라서 이 방식은 모든 회사 환경에서 그대로 동작한다고 단정할 수 없다.
사내 정책이 엄격하다면 SharePoint 권한 설정이나 Microsoft Graph API를 활용하는 방식도 검토해야 한다.


3. 파일을 삭제하고 다시 올리면 shareId가 바뀔 수 있다

update.json이나 배포 파일을 삭제한 뒤 다시 올리면 공유 ID가 바뀔 수 있다.
그러면 기존 앱이 바라보는 링크가 더 이상 유효하지 않을 수 있다.

가능하면 기존 파일을 삭제하지 말고 덮어쓰는 방식으로 관리하는 것이 안전하다.
특히 update.json은 공유 ID가 바뀌지 않도록 주의해야 한다.


4. 실행 파일 배포에는 보안 고려가 필요하다

이 방식은 결국 exe 또는 zip 파일을 다운로드해 교체하는 구조다.
따라서 해시 검증은 중요하다.

가능하다면 코드 서명도 함께 고려하는 것이 좋다.
특히 조직 외부에 배포하거나 여러 사용자가 쓰는 앱이라면 실행 파일의 신뢰성을 확보하는 과정이 필요하다.


5. 실행 중인 exe는 바로 덮어쓸 수 없다

Windows에서는 실행 중인 exe 파일을 그대로 덮어쓰기 어렵다.
그래서 업데이트 과정에서는 앱을 종료한 뒤 파일을 교체해야 한다.

이 템플릿은 배치 스크립트를 생성해 기존 exe를 백업하고, 새 파일로 교체한 뒤 앱을 다시 실행하는 흐름을 사용한다.

WPF 자동업데이트를 직접 구현할 때 가장 놓치기 쉬운 부분이 바로 이 지점이다.
파일을 다운로드하는 것보다, 실행 중인 앱을 안전하게 교체하는 것이 더 까다롭다.


이 방식의 장점

이 템플릿의 가장 큰 장점은 단순함이다.

별도의 업데이트 서버를 만들 필요가 없다.
이미 Microsoft 365를 사용하고 있다면 OneDrive for Business를 활용할 수 있다.
새 버전 파일과 update.json만 관리하면 기본적인 자동업데이트 흐름을 만들 수 있다.

정리하면 이런 장점이 있다.

  • 별도 업데이트 서버가 필요 없다.
  • 작은 팀이나 개인 프로젝트에 적용하기 쉽다.
  • 사내 도구 배포에 잘 맞는다.
  • 앱 시작 시 자동으로 새 버전을 확인할 수 있다.
  • 강제 업데이트와 선택 업데이트를 구분할 수 있다.
  • single exe와 zip 배포를 모두 지원한다.
  • 해시 검증과 롤백으로 최소한의 안전장치를 갖출 수 있다.

이 방식의 한계

물론 이 방식이 모든 상황에 맞는 것은 아니다.

대규모 상용 배포 시스템을 대체하기 위한 구조는 아니다.
OneDrive 공유 정책에 영향을 받고, 조직의 보안 설정에 따라 다운로드가 막힐 수도 있다.

또한 이 템플릿이 코드 서명이나 설치 관리자, 권한 관리까지 모두 해결해주는 것은 아니다.
정교한 배포 관리가 필요하다면 MSIX, ClickOnce, 자체 업데이트 서버, Microsoft Graph API 기반 구조 등을 함께 검토해야 한다.

이 방식은 어디까지나 작은 팀이나 사내 도구를 위한 현실적인 자동업데이트 출발점에 가깝다.


이런 사람에게 잘 맞는다

이 템플릿은 다음과 같은 상황에 잘 맞는다.

  • WPF 앱을 만들었지만 배포 방식이 고민인 사람
  • 별도 업데이트 서버를 운영하고 싶지 않은 사람
  • Microsoft 365 환경을 이미 사용하고 있는 조직
  • 사내 도구를 빠르게 배포하고 싶은 작은 팀
  • 수동으로 exe 파일을 전달하는 방식에서 벗어나고 싶은 개발자
  • 자동업데이트 구조를 처음부터 직접 만들기 부담스러운 사람

반대로 수천 명 이상의 사용자에게 배포하거나, 정교한 권한 제어와 설치 정책이 필요한 환경이라면 더 전문적인 배포 방식을 검토하는 편이 좋다.


마치며

WPF 앱의 자동업데이트를 위해 반드시 별도 서버부터 준비할 필요는 없다.
이미 Microsoft 365를 쓰고 있다면, OneDrive for Business와 update.json만으로도 작은 팀에 충분한 배포 흐름을 만들 수 있다.

물론 완벽한 방식은 아니다.
조직의 공유 정책과 보안 설정을 확인해야 하고, 실행 파일 배포에 따른 안전장치도 필요하다.

하지만 매번 exe 파일을 직접 전달하고, 사용자가 알아서 교체하기를 기다리는 방식보다는 훨씬 낫다.
무엇보다 “작동하는 업데이트 흐름”을 빠르게 만들 수 있다는 점에서 의미가 있다.

이 템플릿은 그런 고민에서 출발했다.
복잡한 인프라보다 먼저 필요한 것은, 실제로 동작하는 단순한 배포 구조일 때가 많다.

WPF 앱에 자동업데이트를 붙이고 싶지만 어디서부터 시작해야 할지 막막했다면, 이 프로젝트가 하나의 출발점이 될 수 있을 것이다.





댓글