Xamarin으로 플랫폼 간 앱 빌드: Android 개발자의 관점

게시 됨: 2022-03-11

코드를 한 번 작성하고 여러 플랫폼에서 사용하는 것은 많은 소프트웨어 개발자의 꿈이었습니다. 이것이 얼마 동안 가능했지만 항상 유지 보수, 테스트 용이성 또는 더 나쁜 사용자 경험을 희생했습니다.

네이티브 SDK를 사용하여 모바일 애플리케이션을 개발하는 것은 데스크탑 애플리케이션 개발 영역에 뿌리를 두고 있는 모든 개발자의 시작점일 것입니다. 프로그래밍 언어는 일부에게는 장벽이 될 것입니다. 누군가 Java 데스크탑 또는 백엔드 애플리케이션 개발 경험이 있는 경우 모바일 앱 개발 회사로 이동하여 Android로 작업하는 것이 iOS용 Objective-C로 처음부터 시작하는 것보다 훨씬 쉽게 느낄 것입니다.

저는 항상 크로스 플랫폼 애플리케이션 개발에 회의적이었습니다. Sencha, Cordova, Titanium 등과 같은 JavaScript 기반 프레임워크는 성능이 중요할 때 결코 현명한 선택이 아닙니다. API의 부족과 기발한 사용자 경험은 이러한 프레임워크에서 제공되었습니다.

그러다가 Xamarin을 만났습니다.

Xamarin을 사용한 플랫폼 간 개발

이 문서에서는 Xamarin을 사용하여 모바일 애플리케이션 개발의 다른 측면을 손상시키지 않고 여러 플랫폼에서 코드를 공유하는 방법을 배웁니다. 이 문서에서는 특히 Android 및 iOS에 중점을 둘 것이지만 Xamarin이 지원하는 다른 플랫폼에 대한 지원을 추가하는 유사한 접근 방식을 사용할 수 있습니다.

자마린이란?

Xamarin은 C# 및 .NET에서 iOS, Android 및 Windows Phone용 플랫폼 간(그러나 기본) 응용 프로그램을 작성할 수 있는 개발 플랫폼입니다.

Xamarin은 네이티브 Android 및 iOS API에 대한 C# 바인딩을 제공합니다. 이를 통해 Android 및 iOS의 모든 기본 사용자 인터페이스, 알림, 그래픽, 애니메이션 및 기타 전화 기능을 모두 C#으로 사용할 수 있습니다.

Android 및 iOS의 각 새 릴리스는 새 API에 대한 바인딩을 포함하는 새 릴리스와 함께 Xamarin과 일치합니다.

Xamarin의 .NET 포트에는 데이터 형식, 제네릭, 가비지 수집, LINQ(언어 통합 쿼리), 비동기 프로그래밍 패턴, 대리자 및 WCF(Windows Communication Foundation)의 하위 집합과 같은 기능이 포함됩니다. 라이브러리는 참조된 구성 요소만 포함하도록 링거로 관리됩니다.

Xamarin.Forms는 완전히 플랫폼 간 사용자 인터페이스 라이브러리를 제공하는 다른 UI 바인딩 및 Windows Phone API 위에 있는 계층입니다.

Xamarin의 범위

크로스 플랫폼 애플리케이션 작성

Xamarin을 사용하여 플랫폼 간 애플리케이션을 작성하려면 개발자는 사용 가능한 두 가지 프로젝트 유형 중 하나를 선택해야 합니다.

  • 이식 가능한 클래스 라이브러리(PCL)
  • 공유 프로젝트

PCL을 사용하면 여러 플랫폼에서 공유할 수 있는 코드를 작성할 수 있지만 한 가지 제한이 있습니다. 모든 .NET API를 모든 플랫폼에서 사용할 수 있는 것은 아니므로 PCL 프로젝트를 사용하면 대상 플랫폼에서 실행하도록 제한합니다.

Xamarin의 연결 및 제한 사항

아래 표는 어떤 플랫폼에서 어떤 API를 사용할 수 있는지 보여줍니다.

특징 .넷 프레임 워크 Windows 스토어 앱 실버라이트 윈도우 폰 자마린
핵심 와이 와이 와이 와이 와이
링크 와이 와이 와이 와이 와이
쿼리 가능 와이 와이 와이 7.5+ 와이
직렬화 와이 와이 와이 와이 와이
데이터 주석 4.0.3+ 와이 와이 와이 와이

빌드 프로세스 동안 PCL은 별도의 DLL로 컴파일되고 런타임 중에 Mono에 의해 로드됩니다. 런타임 중에 동일한 인터페이스의 다른 구현이 제공될 수 있습니다.

반면에 공유 프로젝트를 사용하면 지원하려는 각 플랫폼에 대해 플랫폼별 코드를 작성할 수 있으므로 더 많은 제어가 가능합니다. 공유 프로젝트의 코드에는 코드를 사용하는 애플리케이션 프로젝트에 따라 코드 섹션을 활성화하거나 비활성화하는 컴파일러 지시문이 포함될 수 있습니다.

PCL과 달리 공유 프로젝트는 DLL을 생성하지 않습니다. 코드는 최종 프로젝트에 직접 포함됩니다.

MvvmCross를 사용하여 크로스 플랫폼 코드에 구조 부여

재사용 가능한 코드는 개발 팀의 비용과 시간을 절약할 수 있습니다. 그러나 잘 구조화된 코드는 개발자의 삶을 훨씬 더 쉽게 만듭니다. 버그가 없는 훌륭하게 작성된 코드를 개발자만큼 높이 평가하는 사람은 없습니다.

Xamarin 자체는 재사용 가능한 플랫폼 간 코드를 훨씬 쉽게 작성할 수 있는 메커니즘을 제공합니다.

모바일 개발자는 iOS, Android 및 기타 플랫폼을 지원하기 위해 동일한 로직을 두 번 이상 작성해야 하는 시나리오에 익숙합니다. 그러나 이전 장에서 설명한 것처럼 Xamarin을 사용하면 한 플랫폼용으로 작성된 코드를 다른 플랫폼에서도 쉽게 재사용할 수 있습니다.

그렇다면 MvvmCross는 어디에 있습니까?

이름에서 알 수 있듯이 MvvmCross를 사용하면 Xamarin 애플리케이션에서 MVVM 패턴을 사용할 수 있습니다. 플랫폼 간 애플리케이션 개발에 정말 편리한 라이브러리, API 및 유틸리티가 함께 제공됩니다.

MvvmCross는 애플리케이션 개발에 대한 다른 접근 방식으로 작성해야 하는 상용구 코드의 양을 크게 줄일 수 있습니다(때로는 다른 언어로 여러 번).

MvvmCross 솔루션의 구조

MvvmCross 커뮤니티는 MvvmCross 솔루션을 구성하는 매우 간단하고 효율적인 방법을 권장합니다.

 <ProjectName>.Core <ProjectName>.UI.Droid <ProjectName>.UI.iOS

MvvmCross 솔루션의 핵심 프로젝트는 재사용 가능한 코드와 관련이 있습니다. Core 프로젝트는 재사용성에 중점을 둔 Xamarin PCL 프로젝트입니다.

Core로 작성된 모든 코드는 최대한 플랫폼에 구애받지 않아야 합니다. 모든 플랫폼에서 재사용할 수 있는 논리만 포함해야 합니다. Core 프로젝트는 Android 또는 iOS API를 사용하거나 특정 플랫폼에 액세스해서는 안 됩니다.

비즈니스 로직 계층, 데이터 계층 및 백엔드 통신은 모두 Core 프로젝트에 포함하기에 완벽한 후보입니다. 보기 계층 구조(활동, 조각 등)를 통한 탐색은 Core에서 수행됩니다.

계속하기 전에 MvvmCross 및 작동 방식을 이해하는 데 중요한 하나의 아키텍처 디자인 패턴을 이해해야 합니다. 이름에서 알 수 있듯이 MvvmCross는 MVVM 패턴에 크게 의존합니다.

MVVM은 비즈니스 로직 및 백엔드 데이터에서 그래픽 사용자 인터페이스의 분리를 용이하게 하는 아키텍처 디자인 패턴입니다.

이 패턴은 MvvmCross에서 어떻게 사용됩니까?

글쎄, 우리는 코드의 높은 재사용성을 달성하기를 원하기 때문에 PCL 프로젝트인 Core에서 가능한 한 많이 갖고 싶어합니다. 보기는 플랫폼마다 다른 코드의 유일한 부분이므로 여러 플랫폼에서 재사용할 수 없습니다. 그 부분은 플랫폼과 관련된 프로젝트에서 구현됩니다.

Mvvm교차 구조

MvvmCross는 ViewModel을 사용하여 Core에서 애플리케이션 탐색을 오케스트레이션하는 기능을 제공합니다.

기본 및 기술 세부 정보를 제외하고 자체 MvvmCross Core 프로젝트를 만들어 Xamarin을 시작하겠습니다.

MvvmCross Core 프로젝트 만들기

Xamarin Studio를 열고 ToptalExampleSolution 이라는 솔루션을 만듭니다.

솔루션 만들기

Core 프로젝트를 만들고 있으므로 명명 규칙을 따르는 것이 좋습니다. Core 접미사가 프로젝트 이름에 추가되었는지 확인합니다.

MvvmCross 지원을 받으려면 프로젝트에 MvvmCross 라이브러리를 추가해야 합니다. 추가하기 위해 Xamarin Studio에서 NuGet에 대한 기본 제공 지원을 사용할 수 있습니다.

라이브러리를 추가하려면 패키지 폴더를 마우스 오른쪽 버튼으로 클릭하고 패키지 추가… 옵션을 선택합니다.

검색 필드에서 MvvmCross를 검색하면 아래와 같이 MvvmCross와 관련된 결과를 필터링할 수 있습니다.

필터링 결과

패키지 추가 버튼을 클릭하면 프로젝트에 추가됩니다.

MvvmCross가 프로젝트에 추가되어 핵심 코드를 작성할 준비가 되었습니다.

첫 번째 ViewModel을 정의합시다. 폴더를 만들려면 다음과 같이 폴더 계층을 만듭니다.

폴더의 권장 계층

각 폴더에 대한 내용은 다음과 같습니다.

  • 모델: 실제 상태 콘텐츠를 나타내는 도메인 모델
  • Services: 서비스(비즈니스 로직, 데이터베이스 등)를 보관하는 폴더
  • ViewModel: 모델과 통신하는 방식

첫 번째 ViewModel은 FirstViewModel.cs 라고 합니다.

 public class FirstViewModel : MvxViewModel { private string _firstName; private string _lastName; private string _fullName; public string FirstName { get { return _firstName; } set { _lastName = value; RaisePropertyChanged(); } } public string LastName { get { return _lastName; } set { _lastName = value; RaisePropertyChanged(); } } public string FullName { get { return _fullName; } set { _fullName = value; RaisePropertyChanged(); } } public IMvxCommand ConcatNameCommand { get { return new MvxCommand(() => { FullName = $"{FirstName} {LastName}"; }); } public IMvxCommand NavigateToSecondViewModelCommand { get { return new MvxCommand(() => { ShowViewModel<SecondViewModel>(); }); } } }

이제 첫 번째 ViewModel이 있으므로 첫 번째 보기를 만들고 함께 바인딩할 수 있습니다.

안드로이드 UI

ViewModel의 내용을 표시하려면 UI를 만들어야 합니다.

Android UI를 만드는 첫 번째 단계는 현재 솔루션에서 Android 프로젝트를 만드는 것입니다. 그렇게 하려면 솔루션 이름을 마우스 오른쪽 버튼으로 클릭하고 추가 -> 새 프로젝트 추가... 를 선택합니다. 마법사에서 Android 앱을 선택하고 프로젝트 이름을 ToptalExample.UI.Droid 로 지정했는지 확인합니다.

앞에서 설명한 것처럼 이제 Android용 MvvmCross 종속성을 추가해야 합니다. 이렇게 하려면 NuGet 종속성을 추가하기 위한 Core 프로젝트와 동일한 단계를 따르세요.

MvvmCross 종속성을 추가한 후 Core 프로젝트에 대한 참조를 추가해야 거기에 작성된 코드를 사용할 수 있습니다. PCL 프로젝트에 대한 참조를 추가하려면 참조 폴더를 마우스 오른쪽 버튼으로 클릭하고 참조 편집… 옵션을 선택합니다. 프로젝트 탭에서 이전에 만든 핵심 프로젝트를 선택하고 확인을 클릭합니다.

PCL 프로젝트에 대한 참조 추가

다음 부분은 이해하기 조금 어려울 수 있습니다.

이제 MvvmCross에게 애플리케이션을 설정하는 방법을 알려야 합니다. 그렇게 하려면 Setup 클래스를 만들어야 합니다.

 namespace ToptalExample.UI.Droid { public class Setup : MvxAndroidSetup { public Setup(Context context) : base(context) { } protected override IMvxApplication CreateApp() { return new Core.App(); } } }

클래스에서 볼 수 있듯이 Core에 정의되고 아래에 표시된 클래스인 Core.App 구현을 기반으로 MvvmCross에 CreateApp 에 지시합니다.

 public class App : MvxApplication { public override void Initialize() { RegisterAppStart(new AppStart()); } } public class AppStart : MvxNavigatingObject, IMvxAppStart { public void Start(object hint = null) { ShowViewModel<FirstViewModel>(); } }

App 클래스에서 첫 번째 ViewModel을 표시할 AppStart 인스턴스를 생성합니다.

이제 남은 것은 MvvmCross에 의해 바인딩될 Android 레이아웃 파일을 만드는 것뿐입니다.

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro xmlns:local="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FirstName" /> <EditText android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text LastName" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" local:MvxBind="Text FullName" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click ConcatNameCommand" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="Click NavigateToSecondViewModelCommand" /> </LinearLayout>

레이아웃 파일에는 MvvmCross에 의해 자동으로 해결되는 바인딩이 있습니다. EditText 의 경우 양방향 바인딩이 될 Text 속성에 대한 바인딩을 생성합니다. ViewModel 측에서 호출된 모든 변경 사항은 자동으로 뷰에 반영되며 그 반대의 경우도 마찬가지입니다.

View 클래스는 액티비티 또는 프래그먼트일 수 있습니다. 단순화를 위해 주어진 레이아웃을 로드하는 활동을 사용하고 있습니다.

 [Activity(Label = "ToptalExample.UI.Droid", Theme = "@style/Theme.AppCompat", MainLauncher = true, Icon = "@mipmap/icon")] public class MainActivity : MvxAppCompatActivity<FirstViewModel> { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.Main); } }

첫 번째 버튼에는 명령 바인딩이 있습니다. 즉, 버튼을 클릭하면 MvvmCross가 ViewModel에서 ContactNameCommand 를 호출합니다.

두 번째 버튼의 경우 다른 ViewModel을 표시합니다.

iOS UI

iOS 프로젝트를 만드는 것은 Android 프로젝트를 만드는 것과 크게 다르지 않습니다. 새 프로젝트를 추가하려면 비슷한 단계를 따라야 합니다. 이번에는 Android 대신 iOS 프로젝트를 생성하기만 하면 됩니다. 명명 규칙을 일관되게 유지하기만 하면 됩니다.

iOS 프로젝트를 추가한 후 MvvmCross iOS에 대한 종속성을 추가해야 합니다. 단계는 Core 및 Android의 경우와 완전히 동일합니다(iOS 프로젝트에서 참조를 마우스 오른쪽 버튼으로 클릭하고 참조 추가… 클릭).

이제 Android에서 했던 것처럼 MvvmCross에 애플리케이션을 설정하는 방법을 알려주는 Setup 클래스를 생성해야 합니다.

 public class Setup : MvxIosSetup { public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter) : base(appDelegate, presenter) { } protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp() { return new App(); } }

Setup 클래스는 이제 MvxIosSetup 을 확장하고 Android의 경우 MvxAndroidSetup 을 확장했습니다.

여기에 한 가지 추가 사항은 AppDelegate 클래스를 변경해야 한다는 것입니다.

iOS의 AppDelegate 는 사용자 인터페이스 시작을 담당하므로 iOS에서 보기가 표시되는 방식을 알려야 합니다. 여기에서 발표자에 대해 자세히 알아볼 수 있습니다.

 [Register("AppDelegate")] public class AppDelegate : MvxApplicationDelegate { // class-level declarations public override UIWindow Window { get; set; } public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Window = new UIWindow(UIScreen.MainScreen.Bounds); var presenter = new MvxIosViewPresenter(this, Window); var setup = new Setup(this, presenter); setup.Initialize(); var startup = Mvx.Resolve<IMvxAppStart>(); startup.Start(); Window.MakeKeyAndVisible(); return true; } }

우리의 뷰 모델을 제시하기 위해 뷰를 생성해야 합니다. 이 경우 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 추가 -> 새 파일 을 선택하고 iOS 섹션에서 ViewController를 선택하여 ViewController를 생성하고 이름을 FirstViewController로 지정하겠습니다.

Xamarin은 바인딩이 무엇인지 정의할 세 개의 파일을 만듭니다. Android와 달리 iOS의 경우 코드를 통해 다른 방식으로 바인딩을 정의해야 합니다(Android에서도 그렇게 할 수 있고 일부 경우에는 그렇게 해야 함).

뷰 사이를 탐색해야 하는 경우 ViewModel을 통해 수행됩니다. NavigateToSecondViewModelCommand 명령에서 ShowViewModel<SecondViewModel>() 메서드는 적절한 보기를 찾아 탐색합니다.

그러나 MVVMCross는 로드할 보기를 어떻게 알 수 있습니까?

거기에는 마법이 없습니다. Android(Activity 또는 Fragment)용 뷰를 생성할 때 유형 매개변수( MvxAppCompatActivity<VM> )를 사용하여 기본 클래스 중 하나를 확장합니다. ShowViewMolel<VM> 을 호출할 때 MvvmCross는 유형 매개변수 VM 을 사용하여 Activity 또는 Fragment 클래스를 확장하는 View 를 찾습니다. 이것이 동일한 ViewModel에 대해 두 개의 뷰 클래스를 가질 수 없는 이유입니다.

통제의 역전

Xamarin은 네이티브 API 주위에 C# 래퍼를 제공할 뿐이므로 DI(종속성 주입) 또는 IoC(제어 역전) 메커니즘을 제공하지 않습니다.

의존성의 런타임 주입이나 컴파일 시간 주입이 없으면 느슨하게 결합되고 재사용 가능하며 테스트 가능하고 유지 관리가 쉬운 구성 요소를 만들기가 쉽지 않습니다. IoC 및 DI에 대한 아이디어는 정말 오랫동안 알려져 왔습니다. IoC에 대한 자세한 내용은 온라인 기사에서 찾을 수 있습니다. Martin Fowler의 소개 기사에서 이러한 패턴에 대해 자세히 알아볼 수 있습니다.

IoC는 MvvmCrosses의 초기 버전부터 사용 가능했으며 애플리케이션이 시작될 때와 필요할 때마다 런타임에 종속성을 주입할 수 있습니다.

느슨하게 결합된 구성 요소를 얻으려면 클래스의 구체적인 구현이 필요하지 않습니다. 구체적인 구현이 필요하면 런타임 동안 구현의 동작을 변경하는 기능이 제한됩니다(다른 구현으로 대체할 수 없음). 이러한 구성 요소를 테스트하기가 어렵습니다.

그런 이유로 우리는 하나의 구체적인 구현을 가질 인터페이스를 선언할 것입니다.

 public interface IPasswordGeneratorService { string Generate(int length); }

그리고 구현:

 public class PasswordGeneratorService : IPasswordGeneratorService { public string Generate(int length) { var val; var res = new StringBuilder(); var rnd = new Random(); while (0 < length--) { res.Append(valid[rnd.Next(valid.Length)]); } return res.ToString(); } }

ViewModel은 이제 IPasswordGenerationService 인터페이스의 인스턴스를 요구할 수 있습니다. 이 인스턴스는 우리가 제공할 책임이 있습니다.

MvvmCross가 런타임에 PasswordGeneratorService 구현을 주입하려면 MvvmCross에 사용할 구현을 알려야 합니다. 두 플랫폼 모두에 대해 하나의 구현을 사용하려면 애플리케이션 등록 후 App.cs 에 구현을 등록할 수 있습니다.

 public override void Initialize() { RegisterAppStart(new AppStart()); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, PasswordGeneratorService>(); }

위의 정적 메서드 LazyConstructAndRegisterSingleton<TInterface, TType> 대한 호출은 주입할 구현을 등록합니다. 이 메서드는 적절한 구현을 등록하지만 개체를 ​​만들지는 않습니다.

객체는 필요한 경우에만 생성되며 싱글톤으로 등록되어 한 번만 생성됩니다.

싱글톤 객체를 즉시 생성하고 싶다면 Mvx.RegisterSingleton<TInterface>() 를 호출하여 달성할 수 있습니다.

애플리케이션에 싱글톤만 포함하고 싶지 않은 경우가 있습니다. 객체가 스레드로부터 안전하지 않거나 항상 새 인스턴스를 갖고 싶어하는 다른 이유가 있을 수 있습니다. 이 경우 MvvmCross는 필요할 때마다 새 인스턴스를 인스턴스화하는 방식으로 구현을 등록하는 데 사용할 수 있는 Mvx.RegisterType<TInterface,TType>() 메서드를 제공합니다.

각 플랫폼에 대해 별도의 구체적인 구현을 제공해야 하는 경우 플랫폼별 프로젝트에서 항상 그렇게 할 수 있습니다.

 public class DroidPasswodGeneratorService : IPasswordGeneratorService { public string Generate(int length) { return "DroidPasswordGenerator"; } }

그리고 구현 등록은 Droid 프로젝트의 Setup.cs 클래스에서 수행됩니다.

 protected override void InitializePlatformServices() { base.InitializePlatformServices(); Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, DroidPasswodGeneratorService>(); }

PCL 코드 초기화 후 MvvmCross는 InitializePlatformServices 를 호출하고 플랫폼별 서비스 구현을 등록합니다.

여러 싱글톤 구현을 등록할 때 MvvmCross는 마지막에 등록된 구현만 사용합니다. 다른 모든 등록은 삭제됩니다.

Xamarin으로 플랫폼 간 앱 빌드

이 문서에서는 Xamarin을 사용하여 다양한 플랫폼에서 코드를 공유하면서도 애플리케이션의 기본 느낌과 성능을 유지하는 방법을 살펴보았습니다.

MvvmCross는 Xamarin을 사용하여 플랫폼 간 응용 프로그램을 빌드하는 경험을 더욱 향상시키는 또 다른 추상화 계층을 제공합니다. MVVM 패턴은 모든 플랫폼에 공통적인 탐색 및 사용자 상호 작용 흐름을 생성하는 방법을 제공하므로 작성해야 하는 플랫폼별 코드의 양이 보기만으로 제한됩니다.

이 문서를 통해 Xamarin을 살펴보고 Xamarin을 사용하여 다음 플랫폼 간 애플리케이션을 빌드하도록 동기를 부여했으면 합니다.