[MVVM] 2.커맨드와 바인딩 고급 활용, 마이그레이션을 위한 DependencyProperty와 INotifyPropertyChanged의 활용 차이

WPF(Windows Presentation Foundation)는 C#으로 데스크탑 애플리케이션을 개발하기 위한 강력한 프레임워크입니다. 이 글에서는 MVVM(Model-View-ViewModel) 디자인 패턴을 사용하여 WPF 애플리케이션을 구축할 때 커맨드와 바인딩의 고급 활용 및 마이그레이션 시 DependencyProperty와 INotifyPropertyChanged의 활용 차이점에 대해 깊이 있는 설명을 제공하겠습니다.

1. MVVM 디자인 패턴 개요

MVVM은 M(Model), V(View), VM(ViewModel) 세 가지 구성 요소로 나뉩니다. 이 패턴은 UI 요소와 비즈니스 로직을 분리함으로써 코드의 유지 보수성을 높이고, 테스트 가능성을 증가시킵니다. View는 사용자 인터페이스 요소로 구성되어 있으며, ViewModel은 View와 Model 간의 데이터 바인딩을 처리하는 역할을 수행합니다. Model은 데이터와 비즈니스 로직을 포함합니다.

2. 커맨드(Command)와 바인딩의 고급 활용

WPF에서 커맨드는 사용자 상호작용에 대한 논리를 캡슐화합니다. 커맨드를 사용하면 UI 요소의 동작을 ViewModel과 쉽게 연결할 수 있습니다.

2.1 RelayCommand 클래스

RelayCommand는 ICommand 인터페이스를 구현한 클래스입니다. 이 클래스는 훨씬 더 직관적인 커맨드를 구현할 수 있도록 도와줍니다. 다음은 RelayCommand의 기본 구조입니다.

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);

    public void Execute(object parameter) => _execute(parameter);

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

2.2 ViewModel에서 RelayCommand 사용하기

RelayCommand를 ViewModel에서 사용하여 UI와 비즈니스 로직을 효율적으로 연결할 수 있습니다. 아래는 ViewModel에서 RelayCommand를 설정하는 예제입니다.

public class MainViewModel : INotifyPropertyChanged
{
    public ICommand SaveCommand { get; }

    public MainViewModel()
    {
        SaveCommand = new RelayCommand(OnSave, CanSave);
    }

    private void OnSave(object parameter)
    {
        // 저장 로직 구현
    }

    private bool CanSave(object parameter)
    {
        // 저장 가능 여부 결정
        return true; // 조건에 따라 반환
    }

    // INotifyPropertyChanged 구현
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2.3 XAML에서 커맨드 바인딩

XAML에서 ViewModel의 커맨드를 UI 요소에 바인딩할 수 있습니다. 아래는 버튼의 Click 이벤트에 커맨드를 바인딩하는 방법입니다.

<Button Content="Save" Command="{Binding SaveCommand}" />

3. DependencyProperty와 INotifyPropertyChanged

WPF에서는 데이터 바인딩을 위해 DependencyProperty와 INotifyPropertyChanged를 사용할 수 있습니다. 두 기술은 서로 다른 상황에서 유용하게 쓰이며, 각각의 쓰임새와 장단점이 있습니다.

3.1 DependencyProperty

DependencyProperty는 WPF에서 UI 요소의 특성(property)을 정의하는 데 사용됩니다. DependencyProperty는 다음과 같은 특징을 가지고 있습니다:

  • 성능 향상: WPF는 DependencyProperty를 사용하여 변경된 프로퍼티에 대한 자동 업데이트를 제공합니다.
  • 모든 빈 객체(Binding Target)와의 호환성: 이는 다양한 사용자 정의 속성 및 데이터 템플릿을 지원합니다.
  • 스타일과 애니메이션 지원: DependencyProperty는 스타일 및 애니메이션을 쉽게 적용할 수 있도록 도와줍니다.

DependencyProperty를 정의하는 방법은 다음과 같습니다:

public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty));

public string MyProperty
{
    get { return (string)GetValue(MyPropertyProperty); }
    set { SetValue(MyPropertyProperty, value); }
}

3.2 INotifyPropertyChanged

INotifyPropertyChanged는 데이터 바인딩을 위한 프로퍼티의 변경 알림을 제공합니다. 이는 주로 ViewModel에서 데이터 바인딩을 구현할 때 사용됩니다.

INotifyPropertyChanged를 사용하는 기본 구현은 다음과 같습니다:

public class MyViewModel : INotifyPropertyChanged
{
    private string _myProperty;
    public string MyProperty
    {
        get { return _myProperty; }
        set
        {
            if (_myProperty != value)
            {
                _myProperty = value;
                OnPropertyChanged(nameof(MyProperty));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

4. 마이그레이션 시의 고려 사항

마이그레이션 시 WPF 애플리케이션에서 DependencyProperty와 INotifyPropertyChanged의 사용을 고려해야 합니다. 두 기술의 선택은 다음과 같은 요소에 의해 달라질 수 있습니다:

  • UI 요소와의 관계: UI 요소와 밀접하게 관련된 프로퍼티는 DependencyProperty로 정의하는 것이 좋습니다.
  • 로직 파트: 비즈니스 로직이나 복잡한 데이터 상태를 관리하는 ViewModel에서는 INotifyPropertyChanged를 사용하는 것이 바람직합니다.

4.1 마이그레이션 예시

예를 들어 이전 애플리케이션에서 매개 변수를 DependencyProperty로 전환하려면 다음과 같은 과정을 거쳐야 합니다:

public class MyNewControl : Control
{
    public static readonly DependencyProperty MyDependencyProperty =
        DependencyProperty.Register("MyDependency", typeof(string), typeof(MyNewControl), new PropertyMetadata(string.Empty));

    public string MyDependency
    {
        get { return (string)GetValue(MyDependencyProperty); }
        set { SetValue(MyDependencyProperty, value); }
    }
}

이와 같은 과정을 통해 UI 요소와의 상호작용 빈도를 줄이고, 애플리케이션의 유지 보수를 더욱 용이하게 만들 수 있습니다.

5. 결론

이번 글에서는 WPF에서 MVVM 패턴을 활용한 커맨드 사용 및 DependencyProperty와 INotifyPropertyChanged 간의 차이를 다뤘습니다. 이러한 원리를 이해하고 활용한다면, 더욱 효율적이고 유지 보수하기 쉬운 WPF 애플리케이션을 개발할 수 있을 것입니다. 커맨드와 바인딩의 이해는 MVC, MVVM 패턴을 넘어 더 넓은 영역으로의 확장을 가능하게 할 것입니다.

추가적으로 DependencyProperty와 INotifyPropertyChanged를 잘 활용함으로써 WPF의 특성을 최대한 활용할 수 있으며, 비즈니스 로직과 UI 코드 분리를 통해 애플리케이션의 품질을 향상시킬 수 있습니다.

자주 발생하는 질문이나 추가적인 코드가 필요하시다면 댓글을 남겨주시기 바랍니다. 함께 고민하고 발전할 수 있는 커뮤니티가 되길 바랍니다.