Xamarin.Forms 공식 지원은 2024년 5월에 이미 종료됐습니다. 그런데 2026년 현재도 생각보다 많은 엔터프라이즈 프로젝트가 여전히 마이그레이션을 끝내지 못하고 있어요. 솔직히 말하면, 제가 지난 1년 동안 상담했던 팀의 절반 이상이 이 상황이었습니다.
다행히(?) Microsoft는 2025년 11월 .NET 10과 함께 .NET MAUI 10을 릴리스했고, 이전 .NET 9 대비 안정성·성능 모두에서 가장 큰 도약을 이뤘습니다. 이 가이드는 실제 프로덕션 앱을 Xamarin.Forms에서 .NET MAUI 10으로 옮기면서 마주치는 모든 단계, 함정, 해결책을 다룹니다. 현장에서 정말 겪었던 이야기 위주로요.
왜 지금 마이그레이션해야 하는가
Xamarin.Forms는 이미 보안 패치, iOS/Android 새 SDK 지원, 신규 기기 대응이 모두 중단된 상태입니다. 2026년 기준 Apple은 iOS 18 SDK 이상으로 App Store 제출을 강제하고 있고, Google Play는 API 레벨 35(Android 15)를 타깃으로 요구합니다.
Xamarin.Forms로 이 최소 요건을 맞추려면 비공식 패치에 의존하거나, 아예 충족이 불가능한 상황이 자주 벌어져요. 한 마디로, 더는 미룰 수 없는 타이밍입니다.
.NET MAUI 10의 핵심 개선 사항
- CollectionView 2가 기본값으로 활성화되어 iOS/Android에서 스크롤 성능이 최대 40% 향상
- HybridWebView 안정화 및 JavaScript ↔ C# 상호운용 API 개선
- NativeAOT iOS 정식 지원으로 시작 시간 단축과 앱 크기 감소(체감이 꽤 큽니다)
- Shell 내비게이션의 TitleView, FlyoutItem 렌더링 버그 대량 수정
- Mac Catalyst 17+ 전용 API와 macOS 15 Sequoia 대응
- XAML 컴파일러 오류 진단 메시지가 훨씬 명확해짐 — 개인적으로 이게 가장 반갑습니다
마이그레이션 사전 체크리스트
코드 한 줄 바꾸기 전에 반드시 확인해야 할 항목들입니다. 이 단계를 건너뛰면 마이그레이션 중간에 되돌릴 수 없는 상황에 처할 수 있습니다. (직접 겪어봐서 하는 말이에요.)
- .NET 10 SDK 및 workload 설치:
dotnet --list-sdks로 10.0.x가 설치됐는지 확인하고,dotnet workload install maui를 실행하세요. - Visual Studio 2026 또는 VS Code + .NET MAUI 확장 준비. 구버전 Visual Studio 2022는 .NET 10 빌드에 제약이 꽤 많습니다.
- 기존 프로젝트 브랜치 백업:
git tag xamarin-final로 태그를 남겨 롤백 경로를 확보합니다. 안 하면 나중에 후회해요. - 써드파티 라이브러리 호환성 조사: NuGet 패키지 목록을 뽑아 각 라이브러리의 .NET MAUI 지원 여부를 확인합니다. Xamarin.Essentials는
Microsoft.Maui.Essentials로 통합됐고, Prism.Forms는Prism.Maui로 교체됩니다. - 커스텀 Renderer 목록화:
CustomRenderer를 사용한 모든 컨트롤을 문서화하세요. MAUI에서는 Handler 아키텍처로 재작성해야 합니다. - UI 자동화 테스트 현황 확인: Xamarin.UITest는 MAUI와 호환되지 않습니다. Appium 2 또는 .NET MAUI UITest로 이전 계획이 반드시 필요해요.
방법 1: .NET Upgrade Assistant로 자동 마이그레이션
소규모부터 중간 규모(최대 약 8만 줄)의 프로젝트라면 Microsoft 공식 도구가 70~80%의 작업을 자동화합니다. 2026년 버전은 CollectionView 속성 매핑과 Shell 변환이 눈에 띄게 개선됐어요.
dotnet tool install -g upgrade-assistant
cd /path/to/your/XamarinSolution
upgrade-assistant upgrade MyApp.sln --non-interactive \
--targetFramework net10.0 \
--operation Migrate
도구는 대략 다음 작업을 처리해줍니다.
- csproj를 SDK 스타일로 변환하고
TargetFrameworks를net10.0-android;net10.0-ios;net10.0-maccatalyst로 설정 - AssemblyInfo.cs의 중복 attribute 제거
- Xamarin.Forms → Microsoft.Maui 네임스페이스 자동 치환(일부)
- App.xaml.cs를 MauiProgram.cs 기반 구조로 변경 제안
Upgrade Assistant가 놓치는 부분
자동화는 편하지만, 실무에서 반드시 수동으로 손봐야 할 영역은 꽤 많습니다.
- Custom Renderer: 사실상 100% 수동 이전. Handler로 재작성 필요.
- Effect 클래스: 일부는 유지되지만 대부분 Handler의
Mapper로 옮겨야 합니다. - DependencyService: MAUI의 의존성 주입(
IServiceCollection)으로 전환 권장. 이 기회에 DI를 제대로 도입해두는 게 좋아요. - MessagingCenter: .NET MAUI 10에서 Obsolete로 표시되었으며, CommunityToolkit.Mvvm의
WeakReferenceMessenger사용이 공식 권장입니다. - 플랫폼별 AppDelegate / MainActivity: Platforms/iOS, Platforms/Android 폴더 구조로 재배치.
방법 2: 새 프로젝트로 수동 이전(개인적으로 권장)
10만 줄 이상의 레거시 코드베이스, 복잡한 Custom Renderer가 많은 경우, 혹은 기술 부채를 함께 청산하려는 팀이라면 — 새 .NET MAUI 10 솔루션을 만들고 코드를 복사해오는 방식이 장기적으로 훨씬 빠릅니다. 저도 대부분의 중형 이상 프로젝트에서 이쪽을 선택합니다.
1단계: 새 MAUI 프로젝트 생성
dotnet new maui -n MyApp.Maui -f net10.0
cd MyApp.Maui
dotnet build
2단계: 공유 프로젝트 구조 재구성
Xamarin.Forms의 공유 프로젝트에 있던 폴더(Views/, ViewModels/, Services/, Models/)를 그대로 MAUI 프로젝트로 복사합니다. 단, XAML 파일의 네임스페이스는 반드시 바꿔야 합니다. (이 한 줄 차이로 빌드가 멈춥니다.)
<!-- Xamarin.Forms (이전) -->
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<!-- .NET MAUI (이후) -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
3단계: MauiProgram.cs 설정
이 파일이 기존 App.xaml.cs의 초기화 로직과 AppDelegate의 일부 역할을 대체합니다. 사실 .NET 호스트 빌더에 익숙하다면 훨씬 직관적이에요.
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// 의존성 주입 등록
builder.Services.AddSingleton<IDataService, DataService>();
builder.Services.AddTransient<MainPageViewModel>();
builder.Services.AddTransient<MainPage>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
Custom Renderer를 Handler로 변환하기
.NET MAUI의 가장 큰 아키텍처 변화는 Renderer 패턴을 Handler 패턴으로 교체한 겁니다. Handler는 더 가볍고, 플랫폼 뷰와 크로스 플랫폼 가상 뷰를 명확히 분리하죠.
Xamarin.Forms Entry Renderer 예시(이전)
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
public class CustomEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
Control.SetPadding(20, 0, 20, 0);
}
}
}
.NET MAUI 10 Handler 예시(이후)
// MauiProgram.cs에서 Mapper 추가
builder.ConfigureMauiHandlers(handlers =>
{
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
"CustomEntryStyling",
(handler, entry) =>
{
#if ANDROID
handler.PlatformView.SetBackgroundColor(
Android.Graphics.Color.Transparent);
handler.PlatformView.SetPadding(20, 0, 20, 0);
#elif IOS
handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
handler.PlatformView.Layer.BorderWidth = 0;
#endif
});
});
이 접근은 상속 대신 조합을 사용하기 때문에 런타임 비용이 적고, 여러 Mapper를 누적해 적용할 수 있다는 장점이 있습니다. 한 번 익히면 정말 손이 편해져요.
자주 발생하는 마이그레이션 오류 TOP 10
1. MauiAppCompatActivity를 찾을 수 없다
MainActivity가 FormsAppCompatActivity를 상속하고 있다면 MauiAppCompatActivity로 교체하세요. 네임스페이스는 Microsoft.Maui입니다. (간단하지만 놓치기 쉬운 포인트.)
2. Application.Current.MainPage가 null
.NET MAUI 10에서 MainPage는 Deprecated입니다. 대신 Window.Page 또는 Shell을 사용하세요.
// 이전
Application.Current.MainPage = new NavigationPage(new HomePage());
// 이후(.NET MAUI 10)
Application.Current.Windows[0].Page = new NavigationPage(new HomePage());
3. Xamarin.Essentials API 호출 실패
using Xamarin.Essentials;를 using Microsoft.Maui.ApplicationModel;, using Microsoft.Maui.Devices;, using Microsoft.Maui.Storage;로 분리해서 교체합니다. IDE가 대부분 자동 제안해주지만, 일괄 치환이 안 될 때가 있으니 주의하세요.
4. XAML에서 x:Static이 동작하지 않음
MAUI 10은 XAML Hot Reload 시 x:Static 해석 방식이 더 엄격해졌습니다. 참조하는 클래스의 네임스페이스를 XAML 상단에 xmlns:local로 명시해야 합니다.
5. CollectionView 헤더가 고정되지 않음
.NET MAUI 10의 CollectionView 2에서는 ItemsLayout의 SnapPointsType 기본값이 변경됐습니다. IsStickyHeader="True" 속성을 명시적으로 추가해야 하는데, 릴리스 노트 어디에도 잘 안 적혀 있어서 헤매는 분이 많아요.
6. iOS 앱이 Splash 화면에서 멈춤
Info.plist의 UILaunchStoryboardName을 Platforms/iOS/Resources/LaunchScreen.storyboard로 갱신하고, Xamarin 시절의 Default.png 참조를 삭제하세요.
7. Android에서 Resource.designer.cs 충돌
MAUI는 해당 파일을 사용하지 않습니다. bin, obj, Resource.designer.cs를 모두 삭제하고 재빌드하세요. 클린 빌드가 정답입니다.
8. MessagingCenter obsolete 경고
CommunityToolkit.Mvvm의 WeakReferenceMessenger로 이전합니다.
// 이전
MessagingCenter.Send(this, "UserLoggedIn", user);
// 이후
WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(user));
9. 네이티브 뷰에서 ScaleX, Rotation 애니메이션 끊김
MAUI 10의 새 애니메이션 엔진은 하드웨어 가속이 기본이지만, UseOptimizedDisplay = true 플래그가 Android 15에서 일부 기기와 충돌합니다. MauiProgram.cs에서 Microsoft.Maui.Controls.Hosting.MauiAppBuilderExtensions.ConfigureLifecycleEvents로 개별 제어하세요.
10. Debug 빌드는 되지만 Release에서 앱이 죽음
AOT 컴파일 시 리플렉션을 쓰는 Serialization 라이브러리(Newtonsoft.Json의 일부 기능)가 trim됩니다. TrimmerRootDescriptor를 csproj에 추가하거나 System.Text.Json + Source Generator로 전환하세요. 장기적으로는 후자가 훨씬 깔끔합니다.
마이그레이션 후 검증 체크리스트
- 모든 페이지가 iOS, Android, Windows, Mac Catalyst에서 렌더링되는지 확인
- 푸시 알림, 딥링크, 파일 선택, 카메라 등 플랫폼 API 스모크 테스트
- Release 빌드로 App Store / Play Store 내부 테스트 트랙 배포
- 메모리 누수 측정(dotnet-gcdump 및 Instruments)
- 시작 시간 벤치마크: Xamarin.Forms 대비 20~35% 단축이 일반적인 목표
- 크래시 리포팅 통합(App Center는 2025년 단종됐으니 Sentry 또는 Firebase Crashlytics 권장)
실제 마이그레이션 일정 추정
Microsoft 고객 사례와 커뮤니티 데이터를 종합한 보수적 추정치입니다. 실제로는 팀 상황에 따라 천차만별이니 참고용으로만 보시면 좋아요.
- 소형 앱(50화면 미만, Custom Renderer 5개 이하): 2~3주
- 중형 앱(50~150화면, 자체 UI 프레임워크 포함): 6~10주
- 대형 엔터프라이즈 앱(200화면 이상, 복잡한 백엔드 연동): 3~6개월
팀 규모와 테스트 자동화 성숙도에 따라 이 수치는 크게 달라집니다. 일반적으로 코드 변환은 전체 공수의 40%에 불과하고, 나머지 60%는 회귀 테스트와 UI 미세 조정에 녹아들어 갑니다. 이 비율을 미리 경영진에게 공유하면 일정 협상이 훨씬 수월해져요.
자주 묻는 질문(FAQ)
Xamarin.Forms 앱을 그냥 계속 유지하면 안 되나요?
단기적으로는 동작합니다. 하지만 2026년 이후 Apple과 Google이 요구하는 최소 SDK 버전을 충족할 수 없어 스토어 업데이트 제출이 점점 어려워집니다. 보안 취약점도 더는 패치되지 않으니, 금융·의료 등 규제 산업에서는 사실상 운영이 불가능하다고 보셔도 됩니다.
.NET MAUI와 Flutter 중 지금 마이그레이션한다면 어느 쪽이 유리한가요?
기존 C#/XAML 코드 자산을 최대한 재활용하고 Visual Studio·Azure 에코시스템을 계속 활용하려면 .NET MAUI가 압도적으로 빠른 선택입니다. 완전한 재작성을 감수할 수 있고 웹·임베디드 확장이 필요하면 Flutter가 대안이지만, 학습 곡선과 팀 전환 비용까지 함께 계산해야 해요.
.NET MAUI 10과 .NET MAUI 9 중 어느 버전으로 마이그레이션해야 하나요?
신규 마이그레이션이라면 묻지도 따지지도 말고 .NET MAUI 10(.NET 10 LTS)입니다. .NET 10은 2028년 11월까지 장기 지원되고, CollectionView 2·NativeAOT iOS 같은 핵심 개선이 이쪽에 집중돼 있습니다. .NET 9는 STS라 2026년 5월에 이미 지원 종료예요.
Prism.Forms를 사용 중인데 그대로 쓸 수 있나요?
Prism 9.0부터 Prism.Maui 패키지가 공식 릴리스됐습니다. API는 대부분 호환되지만 IContainerProvider 초기화와 모듈 등록 방식이 바뀌었으므로 Prism 공식 마이그레이션 문서를 반드시 참고하세요.
CI/CD 파이프라인은 어떻게 업데이트하나요?
GitHub Actions와 Azure DevOps 모두 setup-dotnet 액션에서 dotnet-version: '10.0.x'를 사용하고, 이후 dotnet workload install maui 단계를 추가하면 됩니다. macOS 러너는 Xcode 16.2 이상, Windows 러너는 Visual Studio 17.12 이상이 필요해요.
결론
Xamarin.Forms에서 .NET MAUI 10으로의 마이그레이션은 단순한 네임스페이스 치환이 아니라, 사실상 아키텍처 현대화 프로젝트입니다. 계획 단계에서 의존성을 철저히 조사하고, Handler 패턴과 의존성 주입으로 전환하며, 자동화 테스트를 먼저 구축한다면 일정 안에 안전하게 끝낼 수 있습니다.
2026년 현재 .NET MAUI 10은 모바일 크로스플랫폼 C# 개발의 기본값입니다. 솔직히, 지금 이전을 시작하는 것이 향후 유지보수 비용을 가장 크게 절감하는 결정이 될 거예요. 행운을 빕니다.