
2020년은 오프라인 비지니스 환경에서 언텍트 비지니스 환경으로 변화하는 변곡점이라고 할 수 있을 것 같습니다. 그에 따라 이제는 PC 뿐만 아니라 다양한 모바일 디바이스에서 실행되는 크로스플랫폼 개발 환경이 더욱더 발전을 할 것이라고 생각됩니다.
2018년 5월 최초로 공개된 Uno Platform은 크로스 플랫폼에서 실행되는 비지니스 응용 프로그램을 개발하는 가장 빠른 방법이라고 생각하며, 이 포스트에서 Microsoft Contoso UWP앱을 Uno Platform Prism으로 포팅할 때 참고할 사항이나 필요한 기술에 대해서 이야기를 하려고 합니다. Uno Platform에 대한 더 자세한 사항은 여기를 참고하시기 바랍니다.
Contoso UWP app
https://github.com/microsoft/Windows-appsample-customers-orders-database
UnoContoso app – Uno Platform
Uno.Samples/UI/UnoContoso at master · unoplatform/Uno.Samples (github.com)
포팅시 어려웠던 부분?
- Entity Framework Core 버전이 2.1에서 3.x 버전으로 변경되면서 발생한 문제를 해결하는데 시간이 오래 걸렸습니다. 변경 사항은 여기를 참고하시기 바랍니다.
- Sqlite를 Entity Framework Core를 이용해서 앱에서 직접 접근해서 사용하는 방식이 Wasm, iOS, macOS 등에서 원활하지 않아서, Web API를 통해서만 사용할 수 있도록 수정하는데 시간이 좀 걸렸습니다.
- Contoso UWP앱이 Event-driven 방식을 사용하고 있어서, 그 부분을 MVVM Pattern으로 변경하는 부분도 시간이 걸렸습니다.
프로젝트에 대한 간단한 설명
- UnoContoso.Droid : 안드로이드 해더 프로젝트
- UnoContoso.iOS : iOS 헤더 프로젝트
- UnoContoso.macOS : macOS 헤더 프로젝트
- UnoContoso.Uwp : Uwp 헤더 프로젝트
- UnoContoso.Wasm : Wasm 헤더 프로젝트
- UnoContoso.Models : 모델 프로젝트, .NET Standard 2.0
- UnoContoso.Repository : 레파지토리 프로젝트, .NET Standard 2.0
- UnoContoso.Service : 서비스 프로젝트, .NET Core 3.1
- UnoContoso.Shared : 공유 프로젝트
Tips
- NavigationView
- AlwaysShowHeader=”False” :상단에 헤더를 표시하지 않습니다. 헤더를 표시하는 것 보다 표시 하지 않는 것이 아래의 이유로 인해서 개발이 용이 합니다.
- iOS의 경우 헤더의 위치가 다른 플랫폼과 다르게 위로 올라가서 표시됩니다.
- Windows, Wasm, macOS에서는 헤더 위치에 CommandBar를 표시하고, Android, iOS에서는 화면 하단에 표시하기 때문에, NavigationView.HeaderTemplate을 사용이 어렵습니다.
- NavigationViewBehavior : NavigationView에 표시되는 메뉴를 동적으로 구현하고, 선택된 Menu를 ViewModel에 바인딩하기 위해서 Behavior를 만들었습니다. Behavior의 일반적인 사항은 여기를 참고하시기 바랍니다.
- ContentControl : Prism에서 일반적으로 Region을 지정하는 컨트롤입니다. 이 컨트롤을 통해서 View가 네비게이션 됩니다. 더 자세한 사항은 Prism의 RegionNavigation을 참고하시기 바랍니다.
- 이 컨트롤의 Padding은 각 화면의 기본 Margin입니다.
- iOS의 경우 ios:Padding=”10,20,0,0” 을 사용해야 합니다.
- AlwaysShowHeader=”False” :상단에 헤더를 표시하지 않습니다. 헤더를 표시하는 것 보다 표시 하지 않는 것이 아래의 이유로 인해서 개발이 용이 합니다.
- x:Bind
- Android와 iOS 앱의 성능 향상을 위해서 x:Bind를 주로 사용했습니다.
- x:Bind를 사용하기 위해서 code behind에서ViewModel이라는 프로퍼티를 추가합니다. (Code2-1)
- x:Bind의 기본 mode는 OneTime입니다. 그래서, 프로퍼티의 데이터가 변경되는 곳이라면 mode=OneWay를 반드시 추가해야 합니다.
- 컨트롤의 이벤트와 뷰모델의 메소드를 x:Bind를 직접 연결하는 방법은 사용할 수 없습니다.
- IValueConverter를 이용하는 경우 Android에서 x:Bind는 사용할 수 없기 때문에, Binding을 사용합니다. (Code2-2)
- Prism
- View와 ViewModel을 자동으로 연결해 줍니다. prismMvvm:ViewModelLocator.AutowireViewModel="True" 코드를 참고 합니다. 자세한 사항은 여기를 참고 합니다.(Code3-1)
- RegionNavigation을 사용하기 위해서는 App.xaml.cs → void RegisterTypes() 메소드에 RegisterForNavigation을 이용해서 등록해야 합니다. (Code3-2)
- DialogService를 사용하는 화면은 View와 ViewModel을 수동으로 연결 합니다. App.xaml.cs -} void RegisterTypes() 메소드에 있는 코드를 참고 합니다. 더 자세한 사항은 여기를 참고 합니다. (Code3-3)
- DelegateCommand의 ObservesProperty를 이용하면, Command의 사용여부를 쉽게 변경할 수 있습니다. 더 자세한 사항은 여기를 참고 합니다. (Code3-4)
- EventAggregator를 이용하면 뷰모델과 뷰모델 사이에 커뮤니케이션을 할 수 있습니다. 이는 Loosely Coupled 연결입니다. 더 자세한 사항은 여기를 참고 합니다. (Code3-5)
- RelativePanel
- 각 컨트롤을 관계에 의해 배치 합니다. 자세한 사항은 여기를 참고 하시기 바랍니다.
- VisualState를 이용해서 상단에 위치한 CommandBar의 위치를 하단으로 변경할 때 CommandBar 컨트롤에 RelativePanel.LeftOf와 RelativePanel.RightOf에 입력되어있는 이름을 지워주어야 하단으로 이동이 가능합니다. 이 부분은 UWP에서의 동작과는 다르게 동작합니다.
- CommandBar
- 커맨드 버튼을 배치하는 컨트롤 입니다. 기본적인 사항은 여기를 참고하시기 바랍니다.
- CommandBarBehavior는 크기가 변하면 DefaultLabelPosition을 Right에서 Bottom으로 이동 시키기 위해서 추가한 Behavior입니다.
- DefaultLabelPosition 프로퍼티가 Uno에서 미구현 프로퍼티이기 때문에, 대부분의 플랫폼에서는 기본값 Bottm을 가지고 있습니다.
- CollapsibleSearchBox
- UserControl을 이용해서 만든 검색용 컨트롤입니다.
- Uno에서 그래픽 아이콘은 SymbolIcon 컨트롤을 이용해야, 각 플랫폼에서도 정상적으로 아이콘이 출력됩니다.
- ListView
- 모바일 디바이스에서는 화면에 출력되는 항목의 갯수와 가로 스크롤바의 유무에 따라서 세로 스크롤 성능에 영향을 미치기 때문에 DataGrid보다는 ListView를 이용하는 것이 좋습니다. 더 자세한 사항은 여기를 참고 합니다.
- macOS에서는 아직 DataGrid를 사용할 수 없기 때문에 ListView를 사용합니다. 하지만, macOS의 ListView도 컨트롤 내부에 ScrollView가 없기 때문에 세로 스크롤이 불가능한 것으로 보입니다. macOS 지원은 점점 추가될 것으로 생각합니다.
- DataGrid
- Data를 Grid 형태로 보여주며, 수정도 가능한 컨트롤이며, Windows Community Toolkit을 설치해야 사용할 수 있습니다. 더 자세한 사항은 여기를 참고 합니다.
- DataGridBehavior는 Sort기능과 마우스 오른쪽 버튼을 지원하기 위해서 만들었습니다.
- 컬럼에 프로퍼티를 바인딩 할때 x:Bind를 사용할 수 없습니다.
- VisualState
- 특정 상태일 때 UI 요소의 시각적 모양을 지정할 수 있는 기능입니다. 자세한 사항은 여기를 참고 합니다.
- AdaptiveTrigger : 윈도우 속성을 기반으로 시각적 상태를 선언하는 트리거 입니다. 자세한 사항은 여기를 참고 합니다.
- VisualState를 이용해서 디자인 작업을 할 때, 기본 크기에 대한 디자인은 화면에 왼쪽 상단에서8” Tablet(1280x800)을 선택하고, 최소 크기에 대한 디자인은 6”Phone(1920x1080)을 선택해서 작업하면 약간 편하게 작업할 수 있습니다.
- 최소 크기인 경우 PageTitle 컨트롤의 왼쪽에 30의 Margin을 추가해서 햄버거 버튼과 겹치는 현상을 방지 합니다.
- MobileScreenTrigger는 윈도우 크기가 변경될 때 실행 중인 디바이스의 이름을 가지고 오고, Mobile이라는 이름을 포함하면 SetActive()를 이용해서 VisualState를 변경합니다. UIViewSettings.GetForCurrentView().UserInteractionMode는 Uno에서 미구현 상태라 주석 처리를 했습니다.
- BackButton
- 뒤로가기 버튼입니다. GoBackCommand 커맨드는 ViewModelBase에 선언되어 있습니다.
- TextBox
- 일반 텍스트를 표시하고 편집하는 데 사용하는 컨트롤 입니다. 자세한 사항은 여기를 참고 합니다.
- Text 프로퍼티에 TwoWay바인딩을 할 때는 UpdateSourceTrigger=PropertyChanged를 이용하는 것이 좋습니다. 왜냐하면, 내용을 수정하면 바로 바인딩된 프로퍼티가 수정되기 때문입니다. 기본값은 LostFocus입니다.
- InvoiceTemplate (DataTemplate)
- 데이터 개체의 시각적 구조를 만드는데 사용합니다. 자세한 사항은 여기를 참고 합니다.
- HyperlinkButton이 1개가 존재하며 Command가 win과 not_win으로 나누어져 있습니다. 각 플랫폼별 xaml 코드를 적용하는 방법은 여기를 참고 합니다.
- Windows에서는 win:Command="{Binding Source={StaticResource ViewModelElement}, Path=ViewModel.ViewInvoiceCommand}"를 사용해야 Command가 실행됩니다.
- Windows가 아닌 경우에는 not_win:Command="{Binding ElementName=DataGrid, Path=DataContext.ViewInvoiceCommand}"를 사용해야 Command가 실행됩니다.
- Windows에서 ElementName=DataGrid를 이용하는 방법이 않되는 이유는 모릅니다.
- Etc
- 만약 솔루션에 .Net Standard 프로젝트를 포함하고, 사용한다면, Wasm 프로젝트의 LinkerConfig.xml 파일에 해당 내용을 추가해야 합니다.
이상으로 작업을 하면서 알게된 몇가지 팁들을 정리했습니다. 앞으로도 좋은 정보가 있으면 꾸준히 올리도록 하겠습니다.
Facebook : https://www.facebook.com/kaki104
Twitter : https://twitter.com/kaki104
Youtube : http://youtube.com/FutureOfDotNet