스토어 함정 #4: .NET Trimming이 WinUI 3 바인딩을 잘라버렸다
2026-05-31
Tags: Windows · Microsoft Store · 스토어 함정
아주 초기에 겪었던 문제입니다. 최근 함정 시리즈를 쓰면서 오래된 거부 메일을 찾았습니다 — 이 기록이 WinUI 3 / .NET 배포 과정을 처음 접하는 개발자들에게 참고가 되길 바랍니다.
무슨 일이 있었나
제 WinUI 3 앱을 Microsoft Store 인증에 제출했습니다. 인증 보고서가 다음과 같이 돌아왔습니다:
Status: Attention needed
10.1.2.10 Functionality
Unusable Feature: "Download to local cache", "Open File" - The product fails to download copied items to local cache and "Open File" button in the product is not usable
잠깐만요 — 로컬에서 테스트했을 때 "로컬 캐시에 다운로드"와 "파일 열기" 모두 정상 작동했습니다. 왜 Microsoft 테스트 머신에서는 안 되는 걸까요?
원인 파악
가장 의심스러운 차이점은: 로컬에서는 Debug 빌드를 사용했지만, 스토어에 제출한 것은 Release 빌드라는 점이었습니다. .csproj 파일을 확인해 보니, Debug와 Release 설정 사이에 결정적인 차이가 있었습니다:
<!-- Debug -->
<PublishTrimmed>False</PublishTrimmed>
<!-- Release -->
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>Release 빌드에 .NET Trimming이 활성화되어 있었습니다.
.NET Trimming이란
.NET Trimming은 .NET SDK에서 제공하는 최적화 기능입니다. 앱을 게시할 때 트리머가 정적 분석을 수행하여 코드에서 직접 참조되지 않는 어셈블리, 타입, 메서드를 찾아 삭제함으로써 앱 크기를 줄입니다.
문제는: 트리머는 정적 참조만 볼 수 있습니다. 코드가 런타임에 리플렉션을 통해 어떤 타입을 호출하는데, 컴파일 시간에 해당 타입에 대한 정적 참조가 없다면, 트리머는 그 타입이 "사용되지 않는다"고 판단하여 삭제해 버립니다.
왜 WinUI 3가 특히 취약한가
WinUI 3와 그 기반 WinRT 상호 운용 계층은 리플렉션과 동적 기능에 크게 의존합니다. 기존 {Binding}, 값 변환기, WinRT 상호 운용 메타데이터 리플렉션은 모두 트리머의 정적 분석으로 추적할 수 없는 런타임 호출을 생성합니다. 트리머가 바인딩에 필요한 데이터 모델, 명령, 변환기 등의 타입을 모두 "미사용"으로 간주하고 삭제하면, 런타임에 바인딩이 대상을 찾지 못하고 버튼, 텍스트 업데이트 등의 UI 기능이 조용히 작동을 멈춥니다 — 에러도 없고, 크래시도 없고, 그냥 작동하지 않을 뿐입니다.
해결 방법
가장 직접적인 방법: .csproj에서 트리밍을 비활성화합니다:
- <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
+ <PublishTrimmed>False</PublishTrimmed>다시 패키징하여 제출하니 인증을 통과했습니다.
패키지 크기를 줄이기 위해 트리밍이 꼭 필요하다면, 다음 방법으로 호환성을 유지할 수도 있습니다:
- 필요한 타입에
[DynamicDependency]또는[DynamicallyAccessedMembers]특성을 추가하여 트리머에게 보존하도록 지시 .csproj에<TrimmerRootAssembly>를 설정하여 전체 어셈블리 보존
하지만 대부분의 WinUI 3 앱에서는 트리밍을 그냥 끄는 것이 가장 마음 편한 선택입니다.
요약
- .NET Trimming은 "미사용"이라고 판단한 코드를 삭제하지만, 리플렉션 호출을 인식하지 못합니다
- WinUI 3의 데이터 바인딩은 리플렉션에 의존하므로, 잘못 잘려나가기 쉽습니다
- WinUI 3 앱이 스토어 인증에서 "기능 사용 불가"로 실패하지만 로컬에서는 정상 작동한다면, 가장 먼저 확인해야 할 것이 트리밍입니다
- Debug 빌드는 기본적으로 트리밍을 적용하지 않지만, Release 빌드는 트리밍을 활성화할 수 있습니다 — 스토어 제출 전 반드시 Release/Publish 빌드를 테스트하세요
- 곧 스토어 제출 전 테스트 팁에 대한 후속 글을 올릴 예정입니다 — 기대해 주세요
이 글은 스토어 함정 시리즈의 일부입니다.