[MAUI 활용] BookStore 만들기 (4) – BaseViewModel 만들기

이전 포스트에서 우리는 Book, BookStore 에 대한 Model 클래스를 생성했습니다. 이제 Book 과 BookStore 에 관련한 ViewModel 을 만들 차례입니다. 앞서 MVVM 패턴을 쉽게 적용하기 위해서 Community Toolkit 을 설치했습니다. 우선 처음에는 해당 ToolKit 을 사용하지 않고 ViewModel 을 만들어 보겠습니다. 

MVVM에서 뷰모델을 만들 때는  INotifyPropertyChanged라는 인터페이스를 상속받게 됩니다.  INotifyPropertyChanged 인터페이스는 .NET에서 데이터 바인딩을 지원하기 위해 사용되는 인터페이스입니다. 이 인터페이스는 속성 값이 변경될 때 알림을 제공하여 UI가 자동으로 업데이트되도록 할 수 있는 기능을 제공하며,  주로 MVVM 패턴에서 ViewModel 클래스에 사용됩니다.

INotifyPropertyChanged 를 이용한 구현방법

  • 인터페이스 상속 : 클래스에서 INotifyPropertyChanged 인터페이스를 상속합니다.
  • PropertyChanged 이벤트 선언 : PropertyChanged 이벤트를 선언합니다.
  • OnPropertyChanged 메서드 구현 : 속성 값이 변경될 때 PropertyChanged 이벤트를 호출하는 OnPropertyChanged 메서드를 구현합니다.
  • 속성 값 변경 시 OnPropertyChanged 호출 : 속성의 set 접근자에서 OnPropertyChanged 메서드를 호출하여 변경 사항을 알립니다.

전체 소스를 한번 살펴보겠습니다. 

 public class BaseViewModelWithoutToolKit : INotifyPropertyChanged
 {
     public event PropertyChangedEventHandler? PropertyChanged; 
     
     private string title;
     private bool isBusy;
     
     public string Title
     {
         get => title;
         set
         {
             if (title != value)
             {
                 title = value;
                 OnPropertyChanged();
             }
         }
     }
     public bool IsBusy
     {
         get => isBusy;
         set
         {
             if (isBusy != value)
             {
                 isBusy = value;
                 OnPropertyChanged();
             }
         }
     }
     public void OnPropertyChanged([CallerMemberName] string propertyName = null)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }
 }

구현 코드에 대해서 조금  더 자세히 살펴보겠습니다. 

public event PropertyChangedEventHandler? PropertyChanged;

PropertyChanged 이벤트는 속성 값이 변경될 때 이를 알리기 위해 사용됩니다. PropertyChangedEventHandler 델리게이트 타입을 사용하여 이벤트를 선언할  수 있으며, 속성 값이 변경될 때 OnPropertyChanged 메서드에서  PropertyChanged 이벤트를 호출하여 UI가 자동으로 업데이트되도록 합니다.

public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

OnPropertyChanged 메서드는 INotifyPropertyChanged 인터페이스를 구현하는 클래스에서 속성 값이 변경될 때 이를 알리기 위해 사용됩니다. 이 메서드는 PropertyChanged 이벤트를 호출하여 바인딩된 UI 요소나 다른 구독자에게 속성 값이 변경되었음을 알립니다.

위 코드에서 OnPropertyChagned 메서드는 디폴트 파라미터로  CallerMemberName을 사용해서 호출된 속성의 이름을 자동으로 가져오게 됩니다. 이렇게 하면 속성 이름을 수동으로 전달할 필요 없이 자동으로 속성 이름을 가져올 수 있습니다. 

즉, OnPropertyChanged 메서드는 속성값이 변경될 때 변경된 속성 이름과 함께 PropertyChanged 이벤트를 호출하고, 변UI 에서는 DataBinding 을 사용해서 변경된 속성의 이벤트를 감지하여 화면에 반영할 수 있는 구조입니다.

속성 정의

속성을 정의한 부분을 살펴보겠습니다.  title은 현재 페이지의 제목을 나타내고, isLoading 는 페이지가 데이터를 로드하거나 작업을 수행 중인지를 나타냅니다. 작업이 완료되면 이 값을 변경합니다.

이제 속성을 정의해보겠습니다. 살펴볼 속성은 페이지의 제목을 설정하는 Title 입니다. 

 public string Title
 {
     get => title;
     set
     {
         if (title != value)
         {
             title = value;
             OnPropertyChanged(nameof(Title));

         }
     }
 }

get 메서드는 필드 Title 의 값을 반환하고, set 메서드는 값을 설정하기 전에 값이 동일한지 확인합니다. 만약 동일한 값이라면 아무런 동작을 하지 않고, 그렇지 않으면 새로운 값을 설정한 후 OnPropertyChanged() 메서드를 호출해 UI에 변경 사항을 알립니다. 이러한 방식으로, 속성이 변경될 때마다 UI에 자동으로 반영될 수 있도록 처리할 수 있습니다.

 그러나,  만일 속성이 많다면 이와 같은  작업을 계속해서 반복해야 하는 문제에 직면할 수 있습니다 . 다행히 여러가지 MVVM 패턴 구현을 편하게 구현할 수 있도록 도와주는 라이브러리들이 제공되고 있습니다.  우리는 이전 강의에 MS 에서 제공하는 Community ToolKit 을 설치하였으며,   이러한 라이브러리를 사용하면 우리가 작성해야 하는 코드의 양을 줄일 수 있으며, 코딩 가독성도 높일 수 있습니다. 이제 Community ToolKIt 을 사용해 보겠습니다. 

Community ToolKit 사용

 우선  첫 번째 변경 사항은 INotifyPropertyChanged를 상속받는 대신 Community ToolKit 에서 제공하는 기본 클래스인 ObservableObject를 상속받는 것입니다.. 이를 통해서 애플리케이션에 이 객체의 모든 것이 관찰 가능하다는 것을 알릴 수 있습니다. 우선 전체 소스를 확인해 보겠습니다. 

 public partial class BaseViewModel : ObservableObject
 {
     [ObservableProperty]
     string title;

     [ObservableProperty]
     [NotifyPropertyChangedFor(nameof(IsNotLoading))]
     bool isLoading;
      
     public bool IsNotLoading => !IsLoading;
 }

코드가 무척 간결해진 것을 확인할  수 있습니다. 조금  더 자세히 살펴보도록 하겠습니다. 


    [ObservableProperty]
    string title;

우선 생성하고자 하는 BaseViewModel 클래스는 ObservableObject 를 상속받고 있는 것을 확인할 수 있습니다. 그리고, 필드를 선언하면서 ObservableProperty 라는 어노테이션을 사용하였습니다.

ObservableProperty

.NET MAUI에서 ObservableProperty는 MVVM (Model-View-ViewModel) 패턴을 구현할 때 매우 유용한 속성입니다. ObservableProperty는 INotifyPropertyChanged 인터페이스를 쉽게 구현하도록 도와주며, 속성 값이 변경될 때 자동으로 UI에 반영되도록 하는 역할을 합니다.

역할 및 작동 방식

ObservableProperty는 주로 데이터 바인딩 시 ViewModel과 View 간의 데이터 동기화를 원활하게 하기 위해 사용됩니다. 일반적으로 다음과 같은 역할을 합니다.

  • 속성 변경 알림 : ViewModel에서 속성 값을 변경하면 PropertyChanged 이벤트가 자동으로 발생하여, 바인딩된 UI 요소가 변경된 값을 자동으로 업데이트할 수 있도록 합니다.
  • 코드 간소화 :  수동으로 INotifyPropertyChanged를 구현하려면 속성마다 이벤트를 발생시키는 코드를 작성해야 합니다. 하지만 ObservableProperty를 사용하면 이러한 작업이 자동으로 처리됩니다.
  • 데이터 바인딩에 최적화 : ObservableProperty는 XAML 파일에서 UI 요소와 바인딩될 때, 속성의 변경을 즉시 UI에 반영할 수 있는 환경을 제공하여, 사용자가 더 적은 코드로 효율적으로 바인딩을 구현할 수 있게 합니다.

isLoading 은 현재 페이지가 로딩 중이라는 것을 나타냅니다. 이번에는 isNotLoading 이라는 속성을 추가해 보겠습니다. 속성명에서 알 수 있듯이 isLoading 의 반대입니다. isLoading 값이 변경될 때 isNotLoading 도 자동으로 업데이트 되도록 코드를 수정해 보겠습니다. 

 public partial class BaseViewModel : ObservableObject
 {
     [ObservableProperty]
     string title;

     [ObservableProperty]
     [NotifyPropertyChangedFor(nameof(IsNotLoading))]
     bool isLoading;
      
     public bool IsNotLoading => !IsLoading;
 }

NotifyPropertyChangedFor(nameof(IsNotLoading)) 를 추가하게 되면  특정 속성이 변경될 때 다른 속성도 변경 알림을 발생시키도록 합니다. 즉,  isLoading 필드가 변경될 때 IsNotLoading 속성도 변경 알림을 발생시키도록 설정한 것입니다. 이를 통해 IsLoading 속성이 변경될 때 IsNotLoading 속성도 자동으로 업데이트됩니다.

ObservableProperty 로 필드를 정의하면,  필드값 변경시 이에 대한 알림을 처리할 수 있는 구조가 자동으로 생성이 되며, 기존 소스에 있던 속성과 이벤트 , 메서드들을 더 이상 선언하지 않아도 동일한 기능을 제공할 수  있게 됩니다. 내부적으로 생성된 소스 코드를 확인할 수 있는데요, 아래 그림을 참고하시면 Community ToolKit 에서 생성한 소스를 확인할 수 있습니다.  위에서 Community ToolKit  없이 작성했던 내용과 비슷하다는 것을 알 수 있습니다. 

 /// <inheritdoc cref="title"/>
 [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.2.0.0")]
 [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
 public string Title
 {
     get => title;
     [global::System.Diagnostics.CodeAnalysis.MemberNotNull("title")]
     set
     {
         if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(title, value))
         {
             OnTitleChanging(value);
             OnTitleChanging(default, value);
             OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Title);
             title = value;
             OnTitleChanged(value);
             OnTitleChanged(default, value);
             OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Title);
         }
     }
 }

Community ToolKit 에서 자동으로 생성한 소스 코드의 일부입니다. 우리가 먼저 작성했던 소스와 유사하다는 것을 알 수 있습니다. 

  이렇게 해서 우리의 MVVM 패턴을 위한  BaseViewModel을 구현했습니다.  이제 실제로 BookStore 에서 사용하는  실제 View를 위한 ViewModel을 만드는 것을 살펴보도록 하겠습니다. 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다