정상에서 IT를 외치다

[번역] MVVM 디자인 패턴의 기본 이해 본문

안드로이드

[번역] MVVM 디자인 패턴의 기본 이해

Black-Jin 2018. 12. 13. 10:42
반응형


안녕하세요. 블랙진입니다. 


Microsoft 의 MVVM 에 관한 문서 번역글 입니다.

(100% 원본 번역이 아닌 개인적인 의견을 바탕으로 한 번역입니다. 그렇기 때문에 원본과 상이한 해석이 나올 수 있는 부분 미리 참고 부탁드립니다. 제가 잘못 이해한 부분은 언제든지 댓글 부탁드립니다.)



Model-View-ViewModel(MVVM) 디자인 패턴을 알고싶어하는 초심자를 위한 글입니다.



왜 MVVM을 사용하는가?


 전통적인 UI 개발에서 - 개발자는 윈도우, 사용자 컨트롤, 페이지를 사용해서 View 를 만들곤 했습니다. 그리고 모든 논리 코드(핸들링, 초기화 그리고 데이터 모델 등)를 뷰 클래스에 정의합니다. 이 방법은 뷰 클래스의 크기를 늘리고 UI 와 데이터 그리고 비즈니스 사이의 매우 강한 의존성을 형성합니다. 이러한 상황에서, 두 개발자들은 동시에 같은 뷰 에서 작업하기 힘들어 집니다. 또한 한 개발자가 코드를 바꾸면 다른 부분을 망가뜨릴 수 있습니다. 모든 코드들이 한 클래스에 있는것은 유지 보수 그리고 테스트를 하는데 있어 매우 안좋습니다. 만약 당신이 큰 그림을 그리고 있다면, 각 요소들이 매우 답답하게 연결되어 있기 때문에 문제가 생긴다는 것을 느껴야 합니다.


1. 뷰 (UI)

2. 모델 (UI 상에 보여지고 있는 데이터)

3. 글루 코드 (핸들링과 바인딩 그리고 비즈니스 로직)


* 글루코드? 서로 다른 코드를 접착시키는 코드 입니다.

* 비즈니스 로직? 데이터의 처리가 이루어 지는 부분


MVVM 에서의 글루 코드는 뷰 모델입니다. 그래서 어플리케이션의 구조를 더욱 간단하고 유지가능 할 수 있게 만들기 위해서는 관심사 분리에 집중해야 합니다.


만약 뷰 모델에서의 프로퍼티 값이 변하게 되면, 데이터 바인딩과 노티피를 통해 자동으로 새로운 값을 알려줍니다. 만약 사용자가 저장하기 버튼을 클릭하는 액션을 뷰에서 취했을 때, 뷰모델은 요청된 명령을 실행합니다. 이 과정에서, 뷰모델은 모델의 데이터를 수정하고 뷰는 데이터를 수정하지 않습니다. 뷰는 모델의 존재를 모릅니다. 반면 뷰모델과 모델은 뷰를 인식하지 않습니다. 사실 모델은 뷰와 뷰모델의 존재를 모릅니다.



MVVM 은 무엇인가?


MVVM은 3가지 부분을 가집니다.


1. 모델 (비즈니스 역활, 데이터 접근, 모델 클래스)

2. 뷰 (사용자 인터페이스)

3. 뷰모델 (뷰와 뷰모델 사이의 에이전트)


뷰모델은 뷰와 모델 사이의 인터페이스 역활을 합니다. 이것은 뷰와 모델 데이터 사이의 데이터 바인딩을 전달하며 명령을 통해 모든 UI 행동들을 다룹니다.


뷰는 뷰모델의 컨트롤 속성에 바인딩합니다 뷰모델은 모델 객체에 포함 된 데이터를 노출합니다.



C#은 사용한 간단한 MVVM 예제

Model:

public class Book   
{
        public string Title { get; set; }
        public string Author { get; set; }
        public string Category { get; set; }
        public string Language { get; set; }
}

View Model:

public class MainPageViewModel : BindableBase
   {
       private List<Book> books;
       public List<Book> Books         
      { 
           get
           {
            return books;
           } 
           set
           {
            SetProperty(ref books, value);
           } 
       public MainPageViewModel()
       {
           Books = new List<Book>();
           Books.Add(new Book
               {
                   Title = “Harry Potter”, 
                   Author = “J. K. Rowling”,
                   Category = “Young-adult fiction”,
                   Language = “English”
               });
           Books.Add(new Book
           {
                   Title = “Written Lives”, 
                   Author = “Javier Marias”,
                   Category = “Biography”,
                   Language = “Spanish”
           });
   }
}

BindableBase Class:

public class BindableBase : INotifyPropertyChanged
    {
          ///
        /// Multicast event for property change notifications.
        ///
        public event PropertyChangedEventHandler PropertyChanged;

        ///
        /// Checks if a property already matches a desired value.  Sets the property and
        /// notifies listeners only when necessary.
        ///
        ///Type of the property.
        ///Reference to a property with both getter and setter.
        ///Desired value for the property.
        ///Name of the property used to notify listeners.  This
        /// value is optional and can be provided automatically when invoked from compilers that
        /// support CallerMemberName.
        ///True if the value was changed, false if the existing value matched the
        /// desired value.
        protected bool SetProperty(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }

        ///
        /// Notifies listeners that a property value has changed.
        ///
        ///Name of the property used to notify listeners.  This
        /// value is optional and can be provided automatically when invoked from compilers
        /// that support .
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

View:

<TextBlock x:Name="bookTitle” HorizontalAlignment="Left" TextWrapping="Wrap"
Text="{Binding Title}" VerticalAlignment="Top"/>

<TextBlock x:Name="bookAuthor” HorizontalAlignment="Left" TextWrapping="Wrap"
Text="{Binding Author}" VerticalAlignment="Top" Margin="0,142,0,0"/>

<TextBlock x:Name="bookCategory” HorizontalAlignment="Left" TextWrapping="Wrap"
Text="{Binding Category}" VerticalAlignment="Top" Margin="0,242,0,0"/> 

<TextBlock x:Name="bookLanguage” HorizontalAlignment="Left" TextWrapping="Wrap" 
Text="{Binding Language}" VerticalAlignment="Top" Margin="0,342,0,0"/> 



정리


개인적인 정리 글입니다.


1. 뷰, 뷰모델, 모델 이 셋은 서로의 존재를 몰라야 한다.

-> 서로간의 의존성을 최대한 없애야지 유지 보수가 쉽고 테스트 가능한 코드가 된다.


2. 뷰에서 액션을 받으면 뷰모델에서 비즈니스 로직을 실행합니다.


3. 뷰모델은 모델의 데이터를 수정합니다.


4. 뷰모델은 데이터 바인딩과 노티피를 통해 뷰를 수정합니다.


5. 뷰와 뷰모델은 n:m 의 관계입니다.


6. 오직 뷰모델만이 모델에 접근할 수 있고 뷰는 모델에 접근하지 못합니다.

위 그림을 보면 뷰모델은 서로를 알고 있고 뷰모델만이 모델을 알고 있고 모델은 아무것도 모르는 상태입니다.

정리 1번 글에서 처럼 서로의 존재를 아예 모를 수는 없고 최대한으로 줄여야 된다고 이해하면 될 것 같습니다.


MVVM에 관한 권태환님의 글도 읽어보면 정말 좋을것 같아 링크를 남깁니다.


반응형
Comments