Windows에서 어디까지 싱글 바이너리로 만들 수 있는가 - 1 EXE로 가능한 범위, Windows 의존이 남는 곳, 배포 전 판단표

· · Windows, 배포, 싱글 바이너리, .NET, C++, WebView2, WinUI

이 글은 다음 투고에서 시작되었습니다.

Windows에서 「가능하면 1 파일로 배포하고 싶다」는 꽤 평범한 요구입니다. 사내 도구, 장치 연계 도구, 감시 단말, 오프라인 환경, 인스톨러를 가능한 한 피하고 싶은 현장에서는 싱글 바이너리화는 매우 매력적입니다.

다만 이 이야기는 처음에 정리하지 않으면 대체로 도중에 어긋나게 됩니다. Windows에서 말하는 「싱글 바이너리로 하고 싶다」에는 실은 다음 4가지가 섞이기 쉽기 때문입니다.

  • 배포물을 1개로 하고 싶다
  • .NET이나 Visual C++의 런타임을 사전 설치 불필요로 하고 싶다
  • 인스톨러나 관리자 권한 없이 두기만 하면 동작하게 하고 싶다
  • 대상 Windows의 차이에 의존하고 싶지 않다

이 4가지는 같지 않습니다. 실무적으로는 이렇게 생각하는 것이 가장 어긋나지 않습니다.

배포물을 1 EXE로 집약하는 것은 꽤 할 수 있다.
하지만 대상 Windows에의 의존을 제로로 하는 것은 불가능하다.

이 글에서는 그 경계선을 Windows 앱의 실무용으로 정리합니다.

1. 먼저 결론

먼저 결론만 정리하면 이렇습니다.

  • 일반적인 데스크톱 EXE라면 꽤 높은 곳까지 single binary화할 수 있습니다
  • 다만 1 EXE로 할 수 있는 것대상 Windows에 의존하지 않는 것은 별개입니다
  • Shell 확장, Windows 서비스, 드라이버, WebView2, WinUI 3의 일부는 파일 수보다 OS에 무엇을 등록하고 무엇을 전제로 하는가가 본론이 되기 쉽습니다
  • 실무에서 가장 중요한 것은 single binary화하고 싶은가, 인스톨러 불필요로 하고 싶은가, OS 의존을 줄이고 싶은가를 나누어 정하는 것입니다

바꿔 말하면 Windows에서는 다음 선긋기가 꽤 실무적입니다.

  • 배포물을 1개로 집약: 꽤 가능
  • 추가 런타임을 끌어안다: 꽤 가능
  • xcopy 배포로 집약: 앱 종별에 따름
  • 대상 Windows 쪽의 의존을 없앤다: 불가능

2. 「싱글 바이너리」는 4단계로 나누어 생각한다

2.1 레벨 A: 배포물이 1개

가장 표면적인 것은 이것입니다.

  • 메일로 1개 보낼 수 있다
  • USB에 1개 두면 된다
  • 전개처에 app.exe만 둔다

이것은 겉모습의 배포 단위 이야기입니다. 실제로는 기동 시에 임시 전개하고 있어도, OS 측의 DLL에 의존하고 있어도, 이 조건만이라면 충족할 수 있습니다.

2.2 레벨 B: 언어 런타임의 사전 설치가 불필요

다음은 대상 머신에 미리 .NET 런타임이나 VC++ 재배포 가능 패키지를 넣지 않아도 동작하는 상태입니다.

  • C/C++의 정적 링크
  • .NET의 self-contained
  • .NET의 single-file
  • .NET Native AOT

이 레벨이 되면 「단독으로 가져갈 수 있다」는 느낌이 꽤 강해집니다.

2.3 레벨 C: 인스톨이나 등록이 불필요

여기서부터 갑자기 어려워집니다.

단순한 EXE라면 두기만 해도 동작할 수 있습니다. 하지만 다음 같은 것은 별개입니다.

  • Shell 확장
  • Windows 서비스
  • 커스텀 URL 스키마나 파일 관련 짓기
  • 드라이버
  • Explorer나 Office 등 다른 프로세스에 로드되는 컴포넌트

이 영역은 파일을 두기만 해서는 끝나지 않습니다. OS 측의 등록이나 호스트 측과의 연결이 필요합니다.

2.4 레벨 D: 대상 Windows에 의존하지 않는다

이것은 Windows에서는 불가능합니다.

Windows 앱은 최종적으로 Windows의 API, 로더, 보안 모델, 디바이스 스택 위에서 동작하기 때문입니다. single binary화할 수 있는 것은 앱 쪽의 책임 범위까지입니다. OS 자체까지 가지고 가는 것은 아닙니다.

3. 꽤 1 EXE로 하기 쉬운 영역

Windows에서도 다음 같은 앱은 비교적 1 EXE로 집약하기 쉽습니다.

  • 단독 기동되는 데스크톱 도구
  • EXE 자신이 UI와 처리를 가지는 업무 앱
  • 통신, 파일 처리, 로그 수집, 감시, 장치 제어 같은 도구
  • Explorer나 Office의 호스트 통합을 필요로 하지 않는 것
  • Web 런타임을 전제로 하지 않는 UI

이 타입이라면 앱 본체에 포함하기 쉬운 것이 많습니다.

  • 자체 코드
  • 리소스
  • 매니페스트
  • 기정 설정
  • 템플릿 데이터
  • 일부의 서드파티 라이브러리
  • 언어 런타임 본체

더욱이 DLL을 완전히 EXE 안에 매립하지 않더라도, EXE의 옆에 DLL을 두는 app-local 배포는 Windows에서는 평범하게 유력합니다. 실무에서는,

  • app.exe 1개
  • 또는 app.exe + 인접 DLL 몇 개
  • 다만 인스톨러 불필요, 관리자 권한 불필요, xcopy로 배포 가능

이 형태가 억지로 1 EXE로 압축하는 것보다 유지보수하기 쉬운 경우가 꽤 있습니다.

4. 1 EXE여도 사라지지 않는 Windows 의존

「EXE 1개라면 대상 Windows에 의존하지 않는다」고 생각해 버리면 여기서 사고가 납니다. 실제로는 1 EXE여도 다음 의존은 남습니다.

4.1 OS 버전 의존

Windows API에는 각각 최저 서포트 OS가 있습니다. x64 / Arm64의 차이도 있습니다. 즉 단일 EXE로 해도,

  • Windows 10까지 동작하는가
  • Windows 11 전제인가
  • Windows Server에서도 동작시키는가
  • x86 / x64 / Arm64 중 어느 것을 대상으로 하는가

는 먼저 고정할 필요가 있습니다.

4.2 시스템 DLL 의존

이쪽이 1 EXE로 한 셈이라도 실행 시에는 당연히 OS 제공의 컴포넌트를 쓰고 있습니다.

  • kernel32.dll
  • user32.dll
  • advapi32.dll
  • COM 기반
  • 서비스 제어 기반

이쪽은 Windows 측의 책임 범위입니다.

4.3 보안 모델 의존

  • UAC
  • 파일 ACL
  • 서비스 제어 매니저
  • 레지스트리
  • 드라이버 서명 정책

이런 것들은 앱이 단독으로 끌어안을 수 없습니다.

4.4 호스트나 런타임 의존

단독 기동 EXE가 아니라 어떤 호스트에 올리는 설계라면 의존은 단번에 늘어납니다.

  • WebView2를 쓴다: WebView2 Runtime이 필요
  • WinUI 3 / Windows App SDK를 쓴다: 배포 모드의 정리가 필요
  • Shell 확장을 만든다: Explorer 측으로의 등록이 필요

즉, UI나 통합의 선택이 그대로 배포의 어려움이 된다는 경우가 많습니다.

5. 기술별로 보는 현실적인 타협점

5.1 네이티브 C/C++

네이티브 C/C++는 single binary화의 자유도가 높은 쪽입니다. 정적 링크를 선택할 여지가 있고, 단독 기동 EXE라면 꽤 집약하기 쉽습니다.

다만 전부를 1 파일에 밀어 넣는 것보다,

  • UCRT나 VC++ 런타임을 어떻게 할 것인가
  • 서드파티 DLL을 app-local에 둘 것인가
  • 대상 CPU / OS를 어디까지 좁힐 것인가

쪽이 실무상 중요합니다.

5.2 .NET

.NET은 single-file, self-contained, Native AOT가 있으므로 겉모습의 배포 단위는 꽤 작게 할 수 있습니다.

다만 구별은 필요합니다.

  • framework-dependent: 대상 환경의 .NET에 의존
  • self-contained: .NET 런타임을 끌어안는다
  • single-file: 배포물을 하나로 집약
  • Native AOT: 또한 기동 시 의존을 줄이지만 기능 제약도 있다

「single-file이니까 OS 의존이 줄어든다」는 것은 아닙니다. 줄어드는 것은 주로 앱 배포물의 집합입니다.

5.3 WebView2

WebView2를 채택하면 single binary의 어려움은 꽤 바뀝니다. 여기서의 본론은 EXE의 수가 아니라 WebView2 Runtime을 어떻게 다룰 것인가입니다.

올바른 질문은 「1 EXE로 할 수 있는가」보다 다음입니다.

  • Runtime을 기존 환경 전제로 할 것인가
  • Evergreen을 쓸 것인가
  • Fixed Version을 동봉할 것인가
  • 오프라인 배포에서 어디까지 책임을 질 것인가

5.4 WinUI 3 / Windows App SDK

WinUI 3도 채택한 시점에서 배포 요건이 바뀝니다. UI 기술의 선택이 그대로 배포 방식의 선택이 됩니다.

single binary를 최우선으로 한다면 먼저 UI 기술의 전제를 다시 보는 편이 빠른 경우가 자주 있습니다.

6. 본질적으로 「등록·의존」이 필요한 영역

6.1 Shell 확장

Explorer에 로드되는 Shell 확장은 단순한 「두기만 하면 되는 EXE」와는 다른 것입니다. 여기는 파일 수보다 Explorer에 어떻게 등록할 것인가가 본론입니다.

6.2 Windows 서비스

서비스 본체의 exe 자체는 1 파일로 할 수 있어도, 배포는 별개 문제입니다.

  • SCM에의 등록
  • 권한
  • 기동 계정
  • 복구 설정

을 생각할 필요가 있습니다. 즉 서비스는 「1 EXE로 한다」보다 「어떻게 설치할 것인가」를 파고드는 영역입니다.

6.3 드라이버

드라이버는 더욱 명확합니다. INF, 서명, 설치 절차까지 포함해 성립하므로 single binary의 씨름판에 처음부터 오르기 어렵습니다.

7. 실무에서의 판단표

대략 판단한다면 다음 표가 쓰기 쉽습니다.

만들고 싶은 것 1 EXE 현실도 먼저 생각해야 할 것
단독 기동의 Win32 / C++ 도구 높음 정적 링크, 대상 OS / arch
단독 기동의 WinForms / WPF 도구 높음 self-contained, single-file, Native AOT의 적성
WinUI 3 / Windows App SDK 앱 배포 모드, 추가 의존
WebView2 기반의 데스크톱 UI 낮음에서 중 Runtime의 배포 방식
Explorer 우클릭 확장이나 프리뷰 낮음 COM / 레지스트리 등록
Windows 서비스 SCM 등록, 권한, 갱신 절차
드라이버 동봉 앱 낮음 INF, 서명, 설치

이 표에서 가장 중요한 것은 「바이너리의 수」와 「배포의 책임 범위」는 별개라는 것을 알 수 있다는 점입니다.

8. 배포 설계에서 먼저 정해야 할 것

single binary화를 성공시키고 싶다면 구현 전에 다음을 정하는 편이 잘 됩니다.

8.1 무엇을 1개로 하고 싶은가를 정한다

  • 배포물을 1개로 하고 싶은가
  • 런타임 사전 설치를 없애고 싶은가
  • 인스톨러 불필요로 하고 싶은가
  • 오프라인 갱신을 간단하게 하고 싶은가

이 답에 따라 고르는 기술이 바뀝니다.

8.2 최저 서포트 Windows와 arch를 먼저 고정한다

single-file도 Native AOT도 기본적으로 OS / architecture specific입니다. 여기를 애매하게 둔 채 「어쨌든 1 파일로」라고 진행하면 마지막에 API 부족이나 runtime 불일치로 막힙니다.

8.3 「동봉할 것」과 「Windows에 맡길 것」을 명문화한다

실무에서는 이 표를 써 두는 것만으로도 꽤 사고가 줄어듭니다.

  • 앱에 동봉할 것
    • 본체 exe
    • 자체 DLL
    • 설정 템플릿
    • self-contained runtime
  • Windows에 맡길 것
    • 시스템 DLL
    • OS API
    • SCM / 레지스트리 / Explorer
    • 드라이버 기반
  • 별도로 전제하는 것
    • WebView2 Runtime
    • VC++ Redistributable
    • Office / Excel
    • 전용 드라이버

8.4 single binary를 우선한다면 호스트 통합을 줄인다

이것은 꽤 효과가 있습니다.

  • Shell 확장을 그만두고 일반 EXE로 한다
  • 서비스화하지 말고 태스크 스케줄러나 명시 기동으로 끝낸다
  • WebView2가 아니라 네이티브 UI를 쓴다
  • COM은 자기 프로세스 내에서 닫는다

요컨대 OS에 「로드시키는」, 「등록하는」 설계를 줄이는 만큼 single binary에 가까워집니다.

9. 정리

Windows에서의 single binary화는 꽤 할 수 있는 범위까지 가능합니다. 다만 올바른 이해는 다음 한 문장에 다합니다.

앱을 1 EXE로 할 수 있다.
하지만 그 앱이 의존하는 Windows까지 1 EXE로 할 수는 없다.

특히 기억해 두고 싶은 것은 다음 5점입니다.

  • 단독 기동의 일반적인 EXE라면 꽤 1 파일 배포로 집약할 수 있다
  • C/C++의 정적 링크, .NET single-file, Native AOT는 유력
  • 다만 OS 버전, arch, 시스템 DLL, 보안 모델에의 의존은 사라지지 않는다
  • Shell 확장, 서비스, 드라이버, WebView2, WinUI 3의 일부는 OS 등록이나 추가 런타임의 이야기가 본체가 된다
  • single binary의 성패는 「무엇을 1개로 하고 싶은가」를 먼저 구분하는 것으로 정해진다

만약 single binary를 강하게 우선한다면 기술 선정 시점에서 OS와의 결합도를 낮추는 방향으로 설계하는 편이 훨씬 성공하기 쉽습니다.

10. 참고 자료

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기