Windows 네이티브 앱 개발이 엉망인 이유
(domenic.me)- Windows 네이티브 앱 개발 생태계는 수십 년간 이어진 프레임워크 단절과 반복적 재설계로 인해 2025년 현재도 실질적인 개발 생산성을 제공하지 못하는 상태임
- Win32부터 MFC, WinForms, WPF, WinRT, UWP를 거쳐 WinUI 3까지 7단계의 UI 프레임워크 변천에도 불구하고, 최신 API만으로는 기본적인 기능조차 구현이 불가능한 경우가 다수 존재
- 트레이 아이콘, 글로벌 단축키 인터셉트 등 일상적인 기능들이 여전히 P/Invoke를 통한 Win32 호출에 의존해야 하며, 이는 최신 프레임워크 도입의 의미를 퇴색시킴
- .NET AOT 컴파일 방식으로 단순한 유틸리티 앱을 빌드해도 9MiB가 넘는 바이너리가 생성되고, MSIX 배포를 위한 코드 서명 인증서는 연간 $200–300에 달하는 현실적 장벽 존재
- Microsoft 자체의 주요 앱들(VS Code, Outlook, 시작 메뉴)이 웹 기술로 구현되어 있다는 사실은, Windows 네이티브 개발이 Microsoft 내부에서도 더 이상 우선순위가 아님을 방증함
배경: 무엇을 만들었나
-
Windows 전용 유틸리티 앱 ‘Display Blackout’ 개발 과정에서 현재 네이티브 앱 개발 환경의 문제점이 드러남
- 이 앱은 다중 모니터 환경에서 게임 중 좌우 화면을 검은색 오버레이로 끄는 기능을 제공
- 필요한 기능은 디스플레이 열거, 경계 계산, 글로벌 단축키 처리, 트레이 아이콘 표시, 시작 시 자동 실행, 설정 저장 등
- 기존 AutoHotkey 스크립트나 Microsoft Store 앱이 존재하지만, 학습 목적과 UI 개선을 위해 직접 개발을 시도
- 결과적으로 Windows 네이티브 앱 개발 환경이 극도로 복잡하고 비효율적임을 확인
Windows 프로그래밍의 역사
- 초기에는 C 기반 Win32 API가 유일한 선택지였으며, 이 API는 오늘날에도 여전히 유효함
- C++ 기반 MFC가 클래스·템플릿 등 객체지향 추상화를 Win32 위에 추가
- .NET 1.0(2002) 도입으로 C# 언어와 JIT 바이트코드 VM, 자동 메모리 관리 등이 등장했으며 Windows Forms는 Win32 창·컨트롤 API의 래퍼
- .NET 3.0(2006)의 WPF는 XAML 마크업 언어를 도입하고 GPU 기반 컨트롤 렌더링으로 Win32 의존을 탈피한 첫 시도
- Windows 8(2012)의 WinRT는 샌드박스 앱 모델을 도입, 데스크톱·태블릿·폰 통합을 목표로 했으나 XAML 구조가 WPF와 미묘하게 달라 혼란 초래
-
Windows 10(2015)의 UWP는 샌드박스 제한을 일부 완화했으나 WPF 수준의 데스크톱 권한에는 미치지 못했고, 일부 OS 기능(푸시 알림, 라이브 타일, Microsoft Store 배포)은 WinRT/UWP 전용으로 제한됨
- Chrome, Microsoft Office 같은 기존 앱들이 WinRT/UWP 브릿지 앱을 IPC로 연결하는 어색한 아키텍처를 채택하게 된 원인
- Windows 11(2021)의 Windows App SDK는 WinRT/UWP 전용 기능을 표준 C++ 및 .NET 앱 모두에 개방하고, WinUI 3라는 새로운 XAML 기반 컨트롤 라이브러리를 포함
-
UI 프레임워크 진화 요약:
Win32 C APIs → MFC → WinForms → WPF → WinRT XAML → UWP XAML → WinUI 3
개발 방식 선택의 딜레마
- WinUI 3 앱 개발에는 세 가지 경로가 존재:
- C++: 린(lean)한 바이너리, Win32 C API와의 쉬운 인터롭 — 그러나 메모리 안전성 부재
- C#/XAML + 프레임워크 의존 배포: 최신 Windows 11에도 .NET 4.8.1만 사전 설치되어 있어, 첫 설치 시 .NET 라이브러리 다운로드 다이얼로그가 뜨는 열악한 사용자 경험 발생
- C#/XAML + .NET AOT: 전체 .NET 런타임(VM, GC, 표준 라이브러리)을 바이너리에 포함 — 단순한 앱도 9MiB 이상의 바이너리 생성
- Rust 바인딩 유지 시도(windows-app-rs)는 보관 처리(archived) 됨
- 배포 방식도 선택의 고통:
- MSIX 형식은 코드 서명 인증서가 필요하며 미국 외 거주자 기준 연간 $200–300 소요
- 비서명 사이드로드는 관리자 터미널에서만 사용 가능한 난해한 PowerShell 명령 필요
- Microsoft Store 등록은 "독창적이고 지속적인 가치" 미제공을 이유로 거절됨
최신 SDK에서도 불가능한 기능들
| 기능 | Windows App SDK 지원 여부 |
|---|---|
| 디스플레이 열거 | 부분 가능 (foreach 루프 불가, 변경 감지는 P/Invoke 필요) |
| 비활성화 블랙 윈도우 배치 | 부분 가능 (non-activating은 P/Invoke 필요) |
| 전역 키보드 단축키 인터셉트 | 불가 — P/Invoke 필요 |
| 시작 시 자동 실행 | 가능 (시스템 설정 연동 API 제공) |
| 설정 영구 저장 | 가능 |
| 트레이 아이콘 + 메뉴 | 불가 — P/Invoke 필요, 메뉴 스타일 비표준화 |
- 트레이 아이콘 메뉴 스타일은 앱마다 제각각으로, OS 전반에 걸쳐 일관된 표준 없음
- WPF에서 WinUI 3로 오는 과정에서 창 크기 자동 조정 같은 기본 기능조차 사라진 상태
C#과 Win32 인터롭의 구조적 한계
- 모던 P/Invoke 도구인 CsWin32는 구조체 내부의 문자열을 올바르게 래핑하지 못하는 버그 존재
- CsWin32 문서는 Win32 API의 기본 파라미터 타입인
[optional, out]을 C#이 관용적으로 표현할 방법이 없어, 동일 메서드를 두 가지 버전으로 생성한다고 명시 -
WPF 출시(2006)로부터 20년이 지난 현재도 UI 바인딩용 클래스 작성의 보일러플레이트는 거의 개선되지 않음
- 모든 프로퍼티를 getter/setter 쌍으로 변환, 동일값 가드, 이벤트 발생 호출 등의 반복 코드가 여전히 필요
- JavaScript의 데코레이터·프록시에 해당하는 언어 수준의 해결책이 20년간 C#에 추가되지 않음
- CsWin32는 체인지로그 내용이 빈약하고 1.0 미만 버전에 머물러 있어 수년 내 프로젝트 포기 가능성이 있어 보임
결론: 왜 Electron이 답인가
- 현재 Microsoft는 네이티브 앱 개발을 우선하지 않음
- 관련 이슈 트래커는 버그와 리포트가 가득하지만 Microsoft 엔지니어의 응답이 거의 없는 상태
- Windows App SDK의 변경 로그는 머신러닝 API 추가 중심
- VS Code, Outlook, 시작 메뉴 등 Microsoft 자사 주요 앱들이 웹 기술로 구현되어 있다는 사실이 이를 방증
- 커뮤니티는 Avalonia, Uno Platform 같은 서드파티 UI 프레임워크로 이동 중
- 이들은 WPF 철학을 계승하고 크로스플랫폼 지원을 강화
- 현재로서는 Electron이나 Tauri 같은 웹 기반 프레임워크가 더 현실적 선택
- TypeScript/React/CSS 조합이 C#/XAML보다 생산적이며
- Win32 API 접근도 가능
-
Tauri는시스템 WebView를 활용해Chromium 번들 불필요
- WebView2 런타임은 4주마다 업데이트, 반면 시스템 .NET은 4.8.1에 고정
- Microsoft가 Windows App SDK 개선과 배포 체계 단순화를 추진한다면 회복 가능성이 있으나
- 현재로서는 대부분의 개발자가 기대하지 않음
- 결론적으로 “웹 스택을 선택하겠다”는 판단으로 마무리
- Microsoft의 최근 Windows 품질 집중 발표에는 OS 전반에 WinUI 3를 더 많이 사용하겠다는 내용이 포함되어 있으나, 실질적 개선으로 이어질지는 불투명
Hacker News 의견들
-
나도 다른 사람들처럼 Win32를 고수하는 게 맞다고 생각함
오랜 기간 Win32로 개발해온 입장에서, 필요한 기능은 8KB 이하의 독립 실행 파일로 충분히 구현 가능함
디스플레이 열거, 창 생성, 단축키 후킹, 시작 프로그램 등록, 설정 저장, 트레이 아이콘 표시 등 모두 몇백 바이트 수준의 API 호출로 가능함
하지만 2026년에 새 프로젝트를 메모리 안전하지 않은 언어(C++) 로 작성하는 건 시대착오적임
다만, 신뢰되지 않은 입력이 거의 없는 앱이라면 굳이 선전(Propaganda)에 휘둘릴 필요는 없음- 메모리 안전성과
_UNICODE문제를 피하고 싶다면 .NET Framework로 절반의 시간에 같은 걸 만들 수 있음 - 예전에는 Delphi나 MFC 같은 얇은 레이어가 이런 문제를 해결했지만 지금은 유행이 지나고 대체제가 없음
“NoFramework” 운동이 다시 나와서 RAD 시대로 돌아가야 한다고 생각함 - Win32는 여전히 풍부한 문서와 도구가 있고, 앞으로도 오래 살아남을 것임
하지만 새 프로젝트에서 C++을 선택하는 건 신중해야 함
메모리 안전 언어에서도 Win32를 쓸 수 있음 — 예를 들어 windows-rs - 일반 사용자 눈에 Win32 앱을 예쁘게 보이게 하려면 어떻게 해야 하는지 궁금함
- Winamp 개발자들은 어떻게 그렇게 훌륭한 Win32 앱을 만들었을까 궁금함
당시엔 Borland Delphi가 가장 인기 있는 도구였음
- 메모리 안전성과
-
기존 WinRT, UAP, UWP 앱이 아니라면 WinUI 3.0이나 WinAppSDK는 피해야 함
Win32, MFC, WinForms, WPF 같은 검증된 기술을 계속 쓰는 게 낫고,
Microsoft 외부 생태계라면 Qt, VCL, Firemonkey, Avalonia, Uno, ImGUI 등을 고려할 만함
WinUI 3.0은 너무 엉망이라 Microsoft조차 커뮤니티에 오픈소스로 넘기려는 중임 -
임베디드 개발자인데, 장치와 통신하는 Win32 GUI 프로그램을 만드는 게 여전히 쉬움
XP 시절 코드가 Windows 11에서도 그대로 동작했고, VC6 프로젝트를 Visual Studio 2022로 열어도 문제없이 빌드됨
이런 하위 호환성은 다른 플랫폼에서 보기 힘듦- 나는 차라리 투박한 Win32 코드를 계속 쓰는 게 낫다고 느낌
Apple의 Cocoa는 구조는 “우아”하지만 실제로는 복잡하고 문서도 불친절함 - 요즘 개발자들은 매주 바뀌는 생태계에 익숙해서, 오래된 게 “유지보수 안 되는 것”이라 생각하는 경향이 있음
- 다만 고해상도 DPI나 입력 처리 문제 등은 여전히 까다로움
- 32비트에서 64비트로 완전 이전하는 게 가장 큰 도전이었음
- 문자열 정의 방식이 너무 많아서 혼란스러움
- 나는 차라리 투박한 Win32 코드를 계속 쓰는 게 낫다고 느낌
-
순수 Win32 API는 여전히 실용적인 선택임
C++로 MFC 비슷한 래퍼를 직접 만들면 2~3주 안에 완성 가능하고, 이후엔 완전한 제어권을 가질 수 있음
Microsoft의 강력한 하위 호환성 덕분에 Win32 기반 앱은 장기적으로 안정적임
10년 넘게 업데이트한 예시를 여기에서 볼 수 있음- 다크 모드 지원이 가장 큰 문제임
시스템 색상과 컨트롤이 하드코딩되어 있어서 어두운 테마와 충돌함
메뉴나 메시지 박스, 파일 대화상자 등 일부 UI만 다크 모드를 지원해 일관성이 깨짐 - 이 접근법은 Windows 11 스타일 UI를 만들지 못함
관련 논의는 이 글 참고 - 예전에 WTL 3.0을 썼는데, MFC보다 훨씬 가볍고 Win32 기능에 모두 접근 가능했음
현재는 오픈소스로 유지되고 있고 버전 10까지 나와 있음 - MFC 스타일 래퍼의 API 설계를 잘 하면, AI가 구현을 대신 작성해줄 수도 있음
- 차라리 C++ Builder나 Delphi를 쓰는 게 낫지 않냐는 의견도 있음
- 다크 모드 지원이 가장 큰 문제임
-
Windows 앱을 여러 개 만들어본 경험상,
(1) Win32 API는 오래됐지만 매우 안정적이고,
(2) Microsoft 소유 UI 툴킷은 피해야 함 — 결국 Win32 레벨의 제어가 필요해짐- 다만 WPF와 WinForms는 예외적으로 안정적임
지난 20년간 Microsoft가 만든 UI 기술 대부분이 WPF의 변형이었음
WPF를 계속 발전시켰다면 지금쯤 UI 개발의 표준이 되었을 것임
- 다만 WPF와 WinForms는 예외적으로 안정적임
-
사용자 입장에서는 네이티브 앱이 빠르고 좋지만, 개발은 정말 복잡한 혼돈임
Outlook과 Teams 같은 앱이 점점 나빠지는 이유도 이 때문임- 아이러니하게도 Outlook과 Teams가 웹앱 기반(Electron) 이라서 그런 문제를 겪음
포커스, 키보드 내비게이션 등에서 네이티브 감각을 흉내내기 어려움 - 나도 개인 도구를 만들 때 TypeScript + Bun + Electrobun 조합을 씀
Electrobun 프로젝트 참고 - Microsoft조차 Electron으로 앱을 만들고 있으니, 다른 개발자들에게 뭘 기대하겠음
- 아이러니하게도 Outlook과 Teams가 웹앱 기반(Electron) 이라서 그런 문제를 겪음
-
“C++로 새 프로젝트를 만드는 건 범죄”라는 말엔 동의하지 않음
GUI는 안전성보다 성능과 제어가 중요하고, C++은 여전히 검증된 언어임
Qt는 2026년에도 최고의 GUI 프레임워크 중 하나로, 메모리 안전 기능을 내장하고 있음- 하지만 Qt는 일부 Linux 배포판에서만 진정한 네이티브로 동작함
- Qt 앱은 런타임이 필요하므로 완전한 네이티브라 보긴 어려움
- 물론 관리형 환경에 비하면 불편하지만, 임베디드 환경에서는 여전히 유용함
-
왜 최신 .NET 런타임이 Windows 11에 기본 포함되지 않는지 의문임
이는 Microsoft가 일관된 사용자 경험을 포기한 또 다른 사례로 보임- .NET 버전이 너무 많고 완전한 하위 호환이 아니기 때문에,
Windows Update로 모두 배포하는 건 비현실적임
대신 AOT 컴파일된 독립 실행 파일로 배포할 수 있음 (약 9MiB)
Electron보다 훨씬 작고 효율적임 - 예전엔 Windows Update로 배포했지만, 업데이트가 앱을 깨뜨리거나 너무 커서 비효율적이었음
지금은 DLL을 함께 패키징하는 게 더 나음 - .NET 5 이후 보안 모델과 네임스페이스가 크게 바뀌어
OS에 내장하기 어려워졌음
빠른 릴리스 주기를 유지하려고 OS 업데이트와 분리한 것임 - 현재는 레거시 .NET Framework가 OS에 포함되고,
.NET Core는 별도로 설치해야 하는 구조임
- .NET 버전이 너무 많고 완전한 하위 호환이 아니기 때문에,
-
오랜만에 Tauri로 Windows와 Mac용 앱을 만들어봤는데,
개발과 빌드는 쉬웠지만 코드 서명 문제로 동료들이 설치를 못 했음
Mac에서는 터미널 명령으로 해결했지만, Windows는 Smart App Control을 꺼야 했고
이 기능은 재설치 없이는 다시 켤 수 없음
보안 목적은 이해하지만, 설치 과정이 이렇게 어려울 줄은 몰랐음 -
“왜 Electron을 쓰지 않느냐”는 질문의 답은 간단함 —
Electron 앱은 사용자 경험이 나쁨
개발은 쉽지만, 성능과 품질을 희생함
좋은 제품을 만들고 싶다면 어렵더라도 네이티브로 가야 함
개인적으로는 C#이 TypeScript보다 훨씬 낫다고 생각함