본문 바로가기

Develop/MAUI 가이드

[.NET] .NET 통합과 MAUI 란?

반응형

MAUI
.NET MAUI

.NET MAUI 란?

.NET MAUI 에 앞서, 마이크로소프트가 .NET 5 에 왜 이렇게 공을 들이는지를 이해하셔야 합니다. .NET 5 는 현재 플랫폼 별로 파편화되어 있는 프레임워크들을 통합하기 위한 노력의 산물입니다. 현재 프레임워크는 크게 플랫폼 별로 아래와 같이 나뉘어 있습니다.

파편화된 프레임워크

이렇게 파편화된 프레임워크로 인해 지원하는 기능이 프레임워크 별로 달라지기도 하고, 공통으로 사용하고 싶은 기능이 있으면 각 프레임워크마다 지원하는지 확인해야 하는 등의 문제가 끊이지 않았습니다. 그래서 나름 임시 방편으로 제공한 프레임워크가 .NET Standard 프레임워크입니다.

공통 라이브러리 추가

이러한 상황에서 2019년 05월 06일 마이크로소프트 블로그에 .NET 5 를 소개하는 글이 올라오면서 다음과 같이 파편화된 프레임워크를 통합하겠다는 원대한 계획을 발표합니다.

.NET 5 는 공개하기로 계획한 날짜대로 2020년 11월에 공개되었습니다. 다만 아쉬운 점은 .NET 5 에 모바일 플랫폼이 없고 .NET 5 가 LTS (Long Term Support) 가 아니기 때문에, 실제 업무에 .NET 5 도입은 너무 이르다고 할 수 있겠습니다.

하지만 .NET 5 가 도입된다고 하더라도 데스크톱을 개발할 때는 .NET + WPF 조합을 사용하거나, 모바일을 개발할 때는 .NET + Xamarin 조합을 사용해야 하는 불편함이 있습니다. 그래서 이러한 불편함을 해소하기 위해 MAUI (Multi-platform native UI) 가 등장하게 됩니다. MAUI 는 WPF 나 Windows Forms, UWP 와 달리 Xamarin 을 베이스로 데스크톱과 모바일 개발을 통합하겠다는 계획이며 .NET 5 가 아니라 .NET 6 부터 포함될 예정입니다. .NET 6 는 2021년 11월로 계획되어 있습니다.

마이크로소프트가 데스크톱과 모바일 개발 프레임워크를 통합하고 싶어하는 이유는 현재 각광을 받고 있는 PWA (Progressive Web Application) 에 대응하기 위해서라 판단됩니다. 마이크로소프트는 과거 윈도우 플랫폼으로 독점적인 지위를 누리던 시절, 개발자에 대한 배려가 매우 미흡했습니다. 개발자가 의존하는 프레임워크를 갑자기 만들거나 없애거나 출시 후에 실험하거나 비싼 라이선스비를 요구하는 등 여러 갑질이 난무했습니다. 또한 플랫폼 독점적 지위를 유지하기 위해 타 플랫폼 지원에 인색했습니다. 이후 마이크로소프트는 Windows 10 운영체제와 함께 야심차게 준비했던 모바일 플랫폼 선점 경쟁에서 안드로이드에게 밀렸고, 인터넷 익스플로러로 유지하던 웹 플랫폼의 독점적 지위는 Active-X 와 Sliverlight 라는 치명적인 실수로 후발 주자였던 크롬과 파이어폭스에 밀리는 등 우여곡절을 겪습니다. 이후 마이크로소프트는 전략을 대대적으로 수정하여 개발자 커뮤니티에 친화적인 전략과 타 플랫폼 친화적인 정책을 펼치고 있습니다. 그 일환으로 마이크로소프트는 Github 도 인수하고 Mono 도 인수하고 .NET Foundation 이라는 재단을 만들어 .NET 을 오픈소스로 전환하고 .NET 문서도 대대적으로 개선하고 커뮤니티 질문을 전담하여 답변하는 직원도 생기는 등 여러 변화를 만들어 가고 있습니다.


(2021.06.08) .NET Build 2021 업데이트

.NET Conf 2021
.NET Conf 2021

이제 MAUI 가 포함된 .NET 6 가 출시될 2021년 11월 9일까지 6개월 정도 남았습니다. .NET 6 는 .NET Conf 2021 에서 릴리즈됩니다. .NET 6 는 현재 Preview 4 까지 완료되었고, 6월에 출시 예정인 Preview 5 부터는 release candidate 상태가 됩니다.

.NET MAUI
.NET MAUI

.NET MAUI 는 .NET Multi-platform App UI 의 약자로, 2021년 11월에 공개될 .NET 6 와 함께 공식 출시됩니다.

.NET MAUI 는 멀티 플랫폼을 네이티브 수준으로 지원하는 프레임워크입니다. .NET MAUI 는 웹앱같은 하이브리드 앱과 달리 플랫폼에서 제공하는 UI 컨트롤을 사용합니다. 플랫폼이 제공하는 UI 컨트롤을 사용하면서 멀티 플랫폼을 지원하는게 가능한 이유는 UI 컨트롤을 플랫폼과 상관없는 개념으로 추상화한 클래스로 정의하고, 실제 플랫폼에서 동작할 때는 추상화한 클래스와 플랫폼의 UI 컨트롤을 연결해 앱이 동작하기 때문입니다. 이러한 동작 원리는 Xamarin 과 동일합니다. 그래서 Xamarin.Forms.Button 클래스는 안드로이드 플랫폼에서 android.widget.Button 이 되고, iOS 플랫폼에서는 UIKit.UIButton 이 됩니다. 따라서 네이티브 플랫폼과 비교하여 성능 차이가 크지 않으면서 동일한 코드를 여러 플랫폼에 공유하여 사용할 수 있어 많은 기대를 받고 있습니다.

Xamarin.Forms Renderer
Xamarin.Forms Renderer

.NET MAUI 는 Xamarin 소스코드에 기반한 프레임워크입니다. .NET MAUI 는 현재 Xamarin 과 동일하다고 봐도 무방할 정도입니다. 단적인 예로, Layout 클래스의 소스코드가 거의 동일합니다.

Xamarin.Forms.Layout 소스코드

public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement
{
    public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create(nameof(IsClippedToBounds), typeof(bool), typeof(Layout), false);
    public static readonly BindableProperty CascadeInputTransparentProperty = BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true);
    public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;

    protected Layout()
    {
        //if things were added in base ctor (through implicit styles), the items added aren't properly parented
        if (InternalChildren.Count > 0)
            InternalChildrenOnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, InternalChildren));

        InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
    }

    public bool IsClippedToBounds { get; set; }
    public Thickness Padding { get; set; }
    public bool CascadeInputTransparent { get; set; }

    void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue) => InvalidateLayout();
    public event EventHandler LayoutChanged;
    public static void LayoutChildIntoBoundingRegion(VisualElement child, Rectangle region);
    protected virtual void InvalidateLayout();
    protected abstract void LayoutChildren(double x, double y, double width, double height);
    protected void OnChildMeasureInvalidated(object sender, EventArgs e);
    protected override void OnSizeAllocated(double width, double height);
    protected void UpdateChildrenLayout();
}

Microsoft.Maui.Controls.Layout 클래스 소스코드

public abstract class Layout : View, ILayout, ILayoutController, IPaddingElement, IFrameworkElement, Microsoft.Maui.IContainer
{
    public static readonly BindableProperty IsClippedToBoundsProperty = BindableProperty.Create(nameof(IsClippedToBounds), typeof(bool), typeof(Layout), false);
    public static readonly BindableProperty CascadeInputTransparentProperty = BindableProperty.Create(nameof(CascadeInputTransparent), typeof(bool), typeof(Layout), true);
    public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;

    protected Layout() 
    {
        //if things were added in base ctor (through implicit styles), the items added aren't properly parented
        if (InternalChildren.Count > 0)
            InternalChildrenOnCollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, InternalChildren));

        InternalChildren.CollectionChanged += InternalChildrenOnCollectionChanged;
    }

    public bool IsClippedToBounds { get; set; }
    public Thickness Padding { get; set; }
    public bool CascadeInputTransparent { get; set; }
    void IPaddingElement.OnPaddingPropertyChanged(Thickness oldValue, Thickness newValue) => InvalidateLayout();
    public event EventHandler LayoutChanged;
    public static void LayoutChildIntoBoundingRegion(VisualElement child, Rectangle region);
    protected virtual void InvalidateLayout();
    protected abstract void LayoutChildren(double x, double y, double width, double height);
    protected void OnChildMeasureInvalidated(object sender, EventArgs e);
    Size IFrameworkElement.Measure(double widthConstraint, double heightConstraint);
    protected override void OnSizeAllocated(double width, double height);
    protected void UpdateChildrenLayout();
}

개인적으로 .NET MAUI 가 Xamarin 과 크게 달라졌다고 느꼈던 부분은 프로젝트를 관리하는 방법이었습니다. Xamarin 에서는 지원할 각 플랫폼 별로 Platform-Specific 프로젝트를 만들었는데, .NET MAUI 에서는 하나의 프로젝트로 플랫폼 종속적인 서비스와 소스 코드, 그리고 리소스를 관리합니다.

.NET MAUI 프로젝트 구조
.NET MAUI 프로젝트 구조

그리고 Xamarin 에서 부실했던 Hot Reload 기능이 대대적으로 개선될 것이라 하는데, 이 부분은 개발하면서 경험해봐야 얼마나 개선되었는지를 판단할 수 있을 거 같습니다.

.NET MAUI 가 많은 기대를 받고 있는 이유는 많은 .NET 프로그램들이 .NET Framework 기반의 WPF 로 작성되어 있는데 이번 기회가 새로운 프레임워크로 마이그레이션 할 타이밍으로 보기 때문입니다. 안정적으로 동작하는게 최우선인 legacy 코드에게는 마이그레이션 후 안정적으로 동작할지 여부가 굉장히 중요합니다. .NET 5 에도 WPF 가 들어 있지만 LTS 버전이 아니라서 마이그레이션하기 부담스러운 부분이 있었는데, .NET 6 는 LTS 버전으로 출시되기 때문에 .NET Framework 기반의 WPF 앱을 마이그레이션하기 좋은 타이밍입니다. 그리고 .NET MAUI 는 마이크로소프트가 Mono 를 인수하고 7 년 동안 Xamarin 으로 시행착오를 하며 쌓은 노하우가 담겨 있어 실험적인 프레임워크가 아니라 안정화된 프레임워크입니다. 따라서 새로운 프레임워크 도입으로 인한 리스크가 매우 적어 사용하기 매력적인 프레임워크입니다.

반응형