티스토리 뷰

반응형

2022.09.06 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내v1.0 part9-2 StyleSelector

2022.08.31 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part9-1 DataTemplateSelector

2022.08.08 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-3 Template

2022.08.02 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-2 Template

2022.07.21 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part8-1 Template

2022.07.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part7 Behavior

2022.07.05 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part6 Command

2022.06.27 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part5 Converter

2022.06.15 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-2 Data Binding

2022.06.13 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part4-1 Data Binding

2022.06.07 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-3

2022.06.02 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-2

2022.05.30 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 V1.0 part3-1

2022.05.11 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part2

2022.05.09 - [WPF .NET] - MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part1

 

MVVM Pattern을 사용하는 개발자를 위한 안내 v1.0 part1

2021.02.18 - [UWP & Windows App/Beginner] - MVVM Pattern을 사용하는 개발자를 위한 안내 (업데이트 : 2022/03/21) 제 블로그에서 가장 인기있는 포스팅이 바로 MVVM Pattern 관련 포스팅입니다. 그런데, 오래..

kaki104.tistory.com

여기에서 이야기 했던 각 항목을 개별 포스팅 완료 했습니다.

추가 포스팅 예정 : 컨트롤 상속 받아서 컨트롤 재정의하기, Custom Control 만들기

0. 참고

The MVVM Pattern

Todo list Universal & UWP app

 

MVVM pattern 설명 동영상(오래전에 녹화한 내용입니다.)
http://youtu.be/f9aQkuoiPz4
http://youtu.be/uGxboAUwciI
http://youtu.be/2lQQiBEjbtU

 

Using MVVM Pattern

* new Microsoft MVVM Toolkit 사용 가이드

- MVVM pattern을 사용하실 때 이 nuget package를 이용하시면 좋을 것 같습니다.

 

[교안 공유] WPF XAML MVVM에서부터 Prism,Reactive(Rx) Framework 개발(All Coding 과정) (tistory.com)

 

[교안 공유] WPF XAML MVVM에서부터 Prism,Reactive(Rx) Framework 개발(All Coding 과정)

안녕하세요 Microsoft MVP 박문찬입니다. 7월 초에 강의할 때 사용한 교안을 공유합니다. 강의할 때 사용했던 것이라 자세한 설명은 없지만, 전체적으로 WPF 응용 프로그램 개발 기술들에 대해서 살

kaki104.tistory.com

 

1. MVVM Pattern의 시작

MVVM은 WPF (Windows Presentation Foundation) 및 Silverlight의 기능을 활용하여 사용자 인터페이스의 이벤트 중심 프로그래밍을 간소화하기 위해 Microsoft 아키텍처 Ken Cooper 및 Ted Peters가 개발했습니다. 그리고, Microsoft의 WPF 및 Silverlight 아키텍트 중 한 명인 John Gossman은 2005 년 자신의 블로그에서 MVVM을 발표했으며, 벌써 12년이 지났습니다. 하지만, 아직까지 국내에서는 MVVM에 대한 인지율이 낮고, 제대로 알지 못한 상태에서 사용하여 프로젝트가 산으로 가는 경우가 많은 것 같습니다.

 

 

2. kaki's MVVM Pattern

처음 MVVM Pattern을 접한 것이 2010년 10월 교육센터에서 였으며, 그 때부터 지금까지, WPF, Silverlight, Windows app 개발 프로젝트를 진행하였고, 2016년에는 S사에서 Flex로 개발된 솔루션을 WPF로 컨버전하는 프로젝트에서 아키텍처를 담당하였습니다. Prism 5.0을 기반으로 MVVM Pattern, Telerik UI Control을 이용해서 프레임웍을 구축하고, Custom Control, 공통 모듈 개발을 진행했습니다.

 

 

3. MVVM Pattern에 대한 Q&A

Q. MVVM Pattern을 꼭 사용해야 하나?

A. MVVM는 WinForm이나 ASP.Net 개발에서는 사용할 수 없으며, 오직, WPF, Silverlight, UWP app 개발에서만 사용이 가능합니다. 만약, WPF로 개발하는데 MVVM를 사용하지 않는다고 하면, 그냥 WinForm 프로젝트로 변경해서 진행하는 것이 더 효과적이라고 생각합니다. 결과적으로는 WPF, Silverlight, UWP app 실무 프로젝트 개발은 MVVM를 사용해야 한다고 보시면 됩니다.

 

Q. 코드 비하인드(MainPage.xaml.cs)에 코딩을 하지 못하나?

A. MVVM의 기본 방향은 View와 ViewModel의 분리입니다. 그렇기 때문에 될 수 있으면, 코드 비하인드에 직접 코딩을 하지는 않습니다. 하지만, Control에 내장되어있는 Method를 호출해야하는 경우라든지, View에 붙어서 해야하는 작업인 경우에는 코드 비하인드에 직접 코딩을 하는 것이 더 효율적입니다. 이렇게 작업을 하는 경우에는 메모리 누수가 발생하지 않도록 추가적으로 코딩을 해주는 것이 중요합니다.

 

Q. 개발 속도가 느리다.

A. MVVM Pattern의 목적은 개발 속도 향상이 아닌 유지보수의 비용 감소에 있습니다. 그렇기 때문에 WinForm 개발 속도와 비교를 할 수는 없습니다. 정말 빠르게 개발하고 완료를 해야하는 프로젝트라면, WinForm 프로젝트로 진행하는 것을 권장 합니다. 다만, 개발 완료 후 다년간 성능 개선과 유지보수를 진행한다고 했을 때는 제대로 만들어진 MVVM 프로젝트가 WinForm 프로젝트보다는 적은 리소스가 들어간다고 할 수 있습니다. 특히! Unit Test를 도입을 한다면, 유지보수에 들어가는 비용은 더욱더 줄어들 것입니다.

 

Q. 뷰 혹은 컨트롤을 뷰모델에 바인딩해서 사용해도 되나요?

A. MVVM의 기본은 뷰와 뷰모델을 서로 직접 연결하지 않는 것에 있습니다. 그러므로, 뷰 혹은 컨트롤을 직접 바인딩해서 뷰모델에서 프로퍼티를 변경하거나, 조작하는 방식은 사용하지 말아야 합니다. 만약 이렇게 사용하게 되면 메모리 누수의 직접적인 원인이 되어, 차후에 문제가 발생합니다.

 

 

4. MVVM Pattern의 핵심 개념

1) Model : 데이터를 처리하는 기본 단위로 데이터 클래스를 이야기 합니다.

2) View : 사용자가 보고, 입출력하는 화면으로 xaml 파일을 이야기 합니다

3) ViewModel : View의 추상화 클래스로, 비지니스 로직이 구현되어 있습니다.

 

 

 

 

5. UWP 앱을 예를 들어서 설명을 하도록 하겠습니다.

 

Template10을 이용한 UWP Blank app 프로젝트를 만든 후 버튼을 하나 추가했습니다.

버튼을 클릭하면 Hello UWP World라는 글씨로 변경됩니다.

 

 

 

 

 

1) MVVM Pattern을 사용하지 않는 일반적인 event-driven 방식을 이용해서 변경되는 과정을 보겠습니다.

 

화면에 Hello World라는 글씨가 나오는 부분과 버튼에 해당하는 부분의 MainPage.xaml 코드를 보겠습니다.

 

        <RelativePanel EntranceNavigationTransitionInfo.IsTargetElement="True"
                       RelativePanel.AlignBottomWithPanel="True"
                       RelativePanel.AlignLeftWithPanel="True"
                       RelativePanel.AlignRightWithPanel="True"
                       RelativePanel.Below="pageHeader">

            <!--  content  -->
            <TextBlock x:Name="textBlock"
                       Margin="16,12,0,0"
                       Text="Hello World" />
            <Button Content="Change Text" RelativePanel.Below="textBlock" Margin="10"
                    Click="Button_Click"/>
        </RelativePanel>

 

. TextBlock 컨트롤의 Text 프로퍼티에 "Hello World"라는 글씨가 입력되어 있습니다. 앱을 실행하면 바로 이 내용이 화면에 출력됩니다.

. 그 컨트롤 아래 Button 컨트롤의 Content라는 프로퍼티에 "Change Text"라는 글씨를 넣어 놓았습니다. 또한, 버튼을 클릭하면 Button_Click이라는 코드비하인드에 있는 메소드를 실행하게 됩니다.

 

 

MainPage.xaml.cs 코드 비하인드에 있는 Button_Click이라는 메소드의 내용을 살펴보겠습니다.

 

        private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            textBlock.Text = "Hello UWP World";
        }

 

textBlock.Text라는 곳에 "Hello UWP World"라는 문자열을 입력하도록 되어있습니다. 즉, textBlock이라는 컨트롤의 Text 프로퍼티의 값을 직접 변경함으로 화면에 출력되는 내용이 변경됩니다.

 

위의 예제에서 Button 컨트롤의 Click 이벤트를 이용해서 어떤 작업을 하도록 지시를 했기 때문에 이런 처리 방식을 event-driven이라고 이야기 합니다. 또한, 화면을 구성하는 MainPage.xaml 파일에 Click="Button_Click"이라고 입력된 내용과 MainPage.xaml.cs에 Button_Click 메소드는 서로 직접 연결이 되어 있기 때문에 tightly coupled 연결이라고 이야기 합니다.

 

* event-driven 정리

이 방식에서 필요한 것은 딱 2가지 입니다. 화면과 코드 비하인드..그만큼 알아야 할 내용도 추가적으로 공부할 내용도 없는 매우 간단한 구조입니다.

 

그런데 이렇게 간단한 구조이지만, 프로젝트의 규모가 커지고 투입 인원이 늘어나고, 유지보수 기간이 늘어 날 수록 여러가지 문제가 발생하게 되는데, 메모리 누수, 고정된 패턴이 존재하지 않기 때문에 개발자마다 코딩 방법이나 표현 방법이 달라 유지보수에 리소스가 많이 소비됩니다.

 

 

2) MVVM Pattern으로 변경해 보겠습니다.

 

MainPage.xaml를 보겠습니다.

 

        <RelativePanel EntranceNavigationTransitionInfo.IsTargetElement="True"
                       RelativePanel.AlignBottomWithPanel="True"
                       RelativePanel.AlignLeftWithPanel="True"
                       RelativePanel.AlignRightWithPanel="True"
                       RelativePanel.Below="pageHeader">

            <!--  content  -->
            <TextBlock x:Name="textBlock"
                       Margin="16,12,0,0"
                       Text="" />
           
            <Button Content="Change Text" RelativePanel.Below="textBlock" Margin="10"
                    Command=""/>
        </RelativePanel>

 

. TextBlock 컨트롤에 Text 프로퍼티에 ""라고 되어 있습니다. MVVM에서 가장 중요한 Binding이라는 기술을 이용해서 ViewModel에 있는 HelloText 프로퍼티를 연결해 놓은 것입니다.

. Button 컨트롤에 Command라는 프로퍼티에 ""라고 되어 있습니다. Button은 기본적으로 Command라는 프로퍼티를 가지고 있으며, Click 이벤트가 발생하면 Command 프로퍼티와 연결되어 있는 ViewModel에 TextChangeCommand라는 커맨드를 실행합니다.

 

위에서 사용한 Binding이란 개념과 Command에 대해서 잠시 살펴 보겠습니다.

 

* Binding은 Data Binding을 이야기 한 것으로, ViewModel에 특정 프로퍼티와 컨트롤의 특정 프로퍼티를 서로 연결해서 ViewModel의 프로퍼티의 값이 변경되면, 자동으로 컨트롤의 프로퍼티 값도 변경되는 형태를 이야기 합니다.

여기서 프로퍼티의 값이 변경되었을 때 바인딩 대상에서 알리는 방법은 INotifyPropertyChanged 인터페이스를 이용합니다.

* UWP 앱에서는 Binding 문으로 사용하는 duck type 바인딩과 x:Bind 문으로 사용 non duck type 바인딩을 사용할 수 있으며, 더 자세한 사항은 데이터 바인딩 개요 페이지를 참고하시면 됩니다.

 

 

* Command는 화면에서 발생하는 사용자의 Interaction을 ViewModel에 전달하는 중간자 역할을 합니다. 명령을 실행 시키는데 필요한 Parameter를 전달하기 위해서 CommandParameter라는 프로퍼티도 사용할 수 있습니다.

 

 

MainPageViewModel.cs 코드를 보겠습니다.

 

    public class MainPageViewModel : ViewModelBase
    {
        private string _helloText;

        public MainPageViewModel()
        {
            Init();
        }

        private void Init()
        {
            HelloText = "Hello World";

            TextChangeCommand = new DelegateCommand(() =>
            {
                HelloText = "Hello UWP World";
            });
        }

        public string HelloText
        {
            get { return _helloText; }
            set { Set(ref _helloText ,value); }
        }

        public ICommand TextChangeCommand { get; set; }
    }

 

처음에 우리가 보았던 화면은 Hello World라는 글씨가 출력되어 있었고, 버튼을 눌러서 글씨를 변경하는 내용이 였습니다. 이 중 비지니스 로직만 분리를 하면,

 

- Hello World라는 글씨가 처음부터 출력되어 있어야 함

- 버튼을 누르면 Hello UWP World라는 글씨로 변경한다.

 

입니다. 이 2가지의 기능만을 수행하는 별도의 클래스를 ViewModel이라고 부르게 되는 것입니다.

 

뷰모델의 코드를 보면 HelloText라는 프로퍼티는 뷰모델이 생성이 되면서 Hello World라는 문자열 값을 가지게 되고, TextChangeCommand가 실행되면, Hello UWP World라는 문자열로 변경됩니다. 이때 Set(ref _helloText, value) 문장에서 기존 문자열 값과 새로 들어온 값이 서로 다르다면, NotifyPropertyChanged 이벤트가 발생됩니다. 그러면, 뷰에 HelloText 프로퍼티와 Binding되어있던, textBlcok의 Text 프로퍼티의 값이 변경되면서, 바뀐 문자열이 표시 됩니다.

 

 

3) 그렇다면.. 여기서 궁금한 것이 있습니다. 어떻게 뷰모델과 뷰가 서로 연결되는 것일까요?

 

연결 방법은 몇가지가 있지만, 가장 근본적으로는 뷰모델이 인스턴스가 되어서 뷰의 DataContext에 입력이 되면 됩니다. 일반적으로 뷰의 DataContext는 뷰모델이 들어가 있다고 생각하면 될 것 같습니다.

 

위의 예제에서 연결한 방법은 다음과 같습니다.

 

<Page x:Class="T10MVVMSample.Views.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
      xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:controls="using:Template10.Controls"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:T10MVVMSample.Views"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:vm="using:T10MVVMSample.ViewModels"
      mc:Ignorable="d">

 

    <Page.DataContext>
        <vm:MainPageViewModel/>
    </Page.DataContext>

 

 

디자인 타임이나, 런타임시에 뷰가 인스턴스가 되면, 뷰모델도 인스턴스를 시켜서 DataContext 프로퍼티와 연결해 줍니다.

 

이 방법 이외에도, 디자인타임 뷰모델과 런타임 뷰모델을 서로 다르게 인스턴스 시켜서 사용하는 방법도 있습니다. 주로 Prism을 이용하거나 Container을 이용해서 객체를 생성하는 경우는 다른 방법을 사용해야 하겠습니다.

 

* MVVM Pattern 정리

이 예제에서는 MainPage.xaml, MainPageViewModel.cs 2개의 파일을 사용하였으며, Data Binding이라는 기술과, Command를 이용한 사용자 인터렉션 처리를 했습니다. 이렇게 MVVM를 이용해서 개발을 진행하기 위해서는 추가적으로 개념을 정리하고, 배워야 할 내용들이 많습니다. 아마도 그런 이유 때문에 어렵게 느끼는 것이 아닌가 생각합니다. 하지만, 그렇다고 포기하면 프로젝트를 하는 것은 요원한 일이 됩니다.

 

2022.03.11 - [WPF] - Microsoft.Toolkit.Mvvm을 이용한 간단한 프레임워크 part5 - 금액 표시 컨트롤 만들기

2022.03.03 - [WPF] - Microsoft.Toolkit.Mvvm을 이용한 간단한 프레임워크 part4 - LayerPopup 추가

2022.03.02 - [WPF] - Microsoft.Toolkit.Mvvm을 이용한 간단한 프레임워크 part3 - Busy 화면 구현

2022.02.24 - [WPF] - Microsoft.Toolkit.Mvvm을 이용한 간단한 프레임워크 part2 - Frame Navigation

2022.02.21 - [WPF] - Microsoft.Toolkit.Mvvm을 이용한 간단한 프레임워크 part1

6. MVVM Pattern을 사용할 때 알고 있어야 하는 내용들

 

1) Blend for Visual Studio 2019, 20022

. Visual Studio 2019, 2022는 코딩용 툴입니다. 디자인 작업은 Blend For Visual Studio 2019, 2022를 이용하는 것이 좋습니다. 블랜드에서만 가능한 작업이 몇가지 있기 때문입니다. (특히 애니메이션 관련)

. Xaml Hot-Reload 기능을 이용하면 실행 중에 xaml을 수정해서 디자인을 바로 확인 하실 수 있습니다.

 

.2) Data Binding

. 가장 중요한 기술로 UWP에서는 OneTime, OneWay, TwoWay 방식을 이용할 수 있습니다.

. UWP에서는 추가적으로 x:Bind를 사용할 수 있습니다. x:Bind에 대한 자세한 사항은 여기를 참고하세요. en-us를 ko-kr로 변경하면 한글로 확인하실 수 있습니다. 다만, 영어로 보는 것이 더 편하실 것으로 판단됩니다.

. WPF에서는 x:Bind를 지원하지 않습니다.

 

3) IValueConverter

: Model의 value를 View에 출력할 때 형태를 변경해서 출력하는 것을 도와줍니다.

: Value conversion with IValueConverter 내용을 참고하세요

 

4) ICommand

: View에서 발생하는 사용자 Interaction을 ViewModel에 전달하는 용도로 사용합니다.

: WPF - ICommand 동작 방식 포스트를 참고하세요

 

5) Behavior, Action

: Control에 이벤트를 직접적으로 처리를 해야하는 경우에 만들어 사용합니다.

: Creating custom behaviors 내용을 참고하세요

 

2022.02.23 - [WPF] - DataGrid에 Row번호를 출력하기

6) DataTemplate

: Model의 value를 양식에 맞추어서 ListView, GridView 등의 컨트롤에 연속적으로 출력하기 위해 사용합니다.

 

7) XAML Style

: 컨트롤의 디자인을 변경하거나, 컨트롤에 기능을 추가하는 용도로 만들거나, 수정해서 사용합니다.

 

8) Selector

: DataTemplateSelector, StyleSelector 등 Model의 value값이나 조건에 따라서 다르게 적용해야 하는 경우에 사용합니다.

 

9) 기타

2022.03.21 - [WPF] - [기초] ComboBox, ListBox 중요 프로퍼티 사용법 part3

2022.03.15 - [WPF] - [기초] ComboBox, ListBox 중요 프로퍼티 사용법 part2

2022.03.14 - [WPF] - [기초] ComboBox, ListBox 중요 프로퍼티 사용법 part1

2022.03.10 - [WPF] - List vs ObservableCollection? part2

2022.03.08 - [WPF] - List vs ObservableCollection? part1

2022.02.16 - [WPF] - ListBox, DataGrid, ListView 차이점 비교

2022.02.18 - [WPF] - ListView에 CustomView를 만들고 사용하기

2022.02.11 - [WPF] - ResourceDictionary 사용하기 (활용편)

2022.01.27 - [WPF] - ResourceDictionary 사용하기 (기본편)

 

7. MVVM Pattern이 제대로 적용된 프로젝트가 조금 더 활성되었으면..

 

위에서 언급했던 S사에서도 사내에서 .Net 개발을 하지 않았기 때문에, WPF도 처음, Prism도 처음, MVVM Pattern도 처음 사용하는 분이 대부분이였습니다. 하지만, 기본 프레임웍을 구축하고 셈플 화면을 만드는 과정에서 꼼꼼하게 주석을 추가하고, 개발 방법에 대한 문서를 정리하고, 약간의 교육만을 진행했는데도 불구하고, 기본 화면 만드는 작업 일정은 무리 없이 진행이 되었습니다.

 

프로젝트를 진행하는 주체와 프로젝트를 리딩하는 PM, PL이 조금만 MVVM Pattern에 대한 이해와 관심을 가져주고, 프로젝트에 투입되는 인력도 모른다고, 귀찮다고, 이게 내스타일인데~를 외치지 말고 표준 형태의 Pattern을 배우고, 모르면 물어보면서 확인하면서 진행한다면 더욱더 성공하지 않을까 생각됩니다.

 

2021-02-18 추가

1. Q&A 중에 코드 비하인드에 코딩을 하지 않는다고 말씀해 주셨는데 Button_Click Event 처럼 Form을 열거나 하는 정도는 해주는게 나은지

A : 가능하면 코드 비하인드에 코딩을 하지 않습니다. 코드 비하인드에 코딩을 하지 않아도 거의 대부분의 기능을 구현하는데는 문제가 없으며, XAML 재사용성이 떨어지고 MVVM 패턴의 방향성과 맞지 않기 때문입니다. 하지만, 컨트롤에 있는 메소드를 실행시키기 위해서는 가끔 사용할 수도 있지만, 이런 경우에는 Behavior를 만들어서 사용하는 것이 더 좋은 방법입니다.

2. Control이라고 얘기해 주시는 부분은 User Control이 맞나요?

A: Control로 검색을 해보니 UserControl이 아닌 일반 컨트롤에 대한 내용이였습니다. TextBox, ComboBox 등을 이야기 합니다.


Memory Leak가 있는데 정확히 확인할 수 있는 방법이 있는지도 궁금합니다.

A: 참고 포스트를 알려드리겠습니다. 포스트를 참고해서 MemoryViewer를 이용해서 확인하셔도 되고, Visual Studio에 있는 Memory Usage - Snapshort 기능을 이용하셔도 됩니다. 요즘은 Snapshort 기능을 주로 이용하고 있습니다. 첫 화면에서 스냅샷을 찍고 팝업 등의 화면을 여러번 호출 하고 다시 첫화면 상태로 만든 후 다시 스냅삿을 찍어서 2개의 스냅샷을 비교하면서, 어떤 클래스가 남아있는지 확인하는 방법입니다. 더 자세한 방법은 포스팅을 하도록 하겠습니다.

MemoryViewer - Memory leak check (tistory.com)

 

MemoryViewer - Memory leak check

Memory leak check sample 이번에 내용은 메모리 누수 현상(Memory leak)을 눈으로 확인 할 수 있는 MemoryViewer의 소스 코드와 사용법, 사용 이미지를 포스트 한다. MemoryViewer는 사용 메모리 양을 알 수 있는..

kaki104.tistory.com

 

반응형
댓글