[MVVM] 7.MVVM에서의 ViewModel 간 통신과 메시징 시스템, 다양한 메시징 시스템 예제와 적용 시나리오

MVVM(Model-View-ViewModel) 패턴은 WPF(Windows Presentation Foundation) 애플리케이션에서 데이터와 UI를 분리하는 데 매우 효과적입니다. 그러나 복잡한 UI 애플리케이션에서는 ViewModel 간의 통신이 필요할 수 있습니다. 이 글에서는 MVVM에서 ViewModel 간 통신의 필요성과 함께 다양한 메시징 시스템의 예제와 적용 시나리오를 살펴보겠습니다.

1. MVVM과 ViewModel 간 통신의 필요성

MVVM 패턴의 주요 구성 요소인 ViewModel은 UI와 비즈니스 로직을 연결하는 역할을 합니다. 그러나 애플리케이션이 복잡해질수록 여러 ViewModel 간에 데이터를 전달하거나 이벤트를 통지해야 할 경우가 발생합니다. 이런 상황에서는 ViewModel 간 직접적인 의존 관계를 피하면서도 효과적으로 통신할 수 있는 메시징 시스템이 필요합니다.

2. 메시징 시스템의 정의

메시징 시스템은 애플리케이션 내의 컴포넌트 간의 통신을 위한 방법을 제공합니다. 이를 통해 서로 다른 ViewModel이 협업하고 데이터 또는 이벤트를 효율적으로 공유할 수 있습니다. 메시징 시스템은 다음과 같은 기능을 제공합니다:

  • Loose Coupling: ViewModel 간의 의존성을 줄이고 재사용성을 높입니다.
  • Asynchronous Communication: 비동기적으로 데이터를 교환할 수 있습니다.
  • Decoupled Architecture: 애플리케이션의 각 부분이 독립적으로 작동하게 만듭니다.

3. MVVM에서 메시징 시스템 구현하기

대표적인 메시징 시스템으로는 Prism의 EventAggregator, MVVM Light의 Messenger, Simple Messenger 등이 있습니다. 이번 섹션에서는 Prism의 EventAggregator를 사용한 예제를 통해 ViewModel 간 통신을 살펴보겠습니다.

3.1. Prism의 EventAggregator 사용 예제

Prism의 EventAggregator는 ViewModel 간의 이벤트를 전달하는 간단한 방법을 제공합니다. 아래의 예제를 통해 EventAggregator의 사용 방법을 살펴보겠습니다.

3.1.1. 프로젝트 설정

먼저 Prism 라이브러리를 설치해야 합니다. NuGet 패키지 관리자 콘솔에서 다음 명령어를 입력하여 Prism과 관련된 패키지를 설치합니다.

Install-Package Prism.Core

3.1.2. EventAggregator 클래스 생성

EventAggregator를 사용하기 위해 먼저 해당 클래스를 인스턴스화합니다. 이 클래스는 이벤트를 게시하고 구독하는 기능을 제공합니다.

using Prism.Events;

public class MyEvent : PubSubEvent
{
}

public class EventAggregatorService
{
    private static EventAggregator _eventAggregator;
    
    public static EventAggregator Instance
    {
        get
        {
            if (_eventAggregator == null)
            {
                _eventAggregator = new EventAggregator();
            }
            return _eventAggregator;
        }
    }
}

3.1.3. Publisher(ViewModel1) 구현

이제 첫 번째 ViewModel에서 이벤트를 게시하는 방법을 구현하겠습니다. 이 ViewModel에서는 특정 버튼 클릭 시 메시지를 발행합니다.

public class ViewModel1 : BindableBase
{
    public DelegateCommand PublishMessageCommand { get; }

    public ViewModel1()
    {
        PublishMessageCommand = new DelegateCommand(OnPublishMessage);
    }

    private void OnPublishMessage()
    {
        var message = "안녕하세요, ViewModel2에 메시지를 보냅니다!";
        EventAggregatorService.Instance.GetEvent().Publish(message);
    }
}

3.1.4. Subscriber(ViewModel2) 구현

두 번째 ViewModel에서 받은 메시지를 처리하는 코드를 작성합니다. 이 ViewModel은 첫 번째 ViewModel의 메시지를 수신하고 처리합니다.

public class ViewModel2 : BindableBase
{
    private string _receivedMessage;
    public string ReceivedMessage
    {
        get { return _receivedMessage; }
        set { SetProperty(ref _receivedMessage, value); }
    }

    public ViewModel2()
    {
        EventAggregatorService.Instance.GetEvent().Subscribe(OnMessageReceived);
    }

    private void OnMessageReceived(string message)
    {
        ReceivedMessage = message;
    }
}

4. 다양한 메시징 시스템 예제

일반적으로 사용되는 메시징 시스템 몇 가지를 더 소개하겠습니다. 각 시스템은 서로 다른 요구 사항과 구현 스타일에 따라 선택할 수 있습니다.

4.1. MVVM Light의 Messenger

MVVM Light는 Messenger 클래스를 제공하여 간단하고 유연한 메시징 시스템을 구현할 수 있도록 합니다.

using GalaSoft.MvvmLight.Messaging;

public class MessageClass
{
    public string Content { get; set; }
}

public class ViewModel1 : ViewModelBase
{
    public void SendMessage()
    {
        Messenger.Default.Send(new MessageClass { Content = "안녕하세요, ViewModel2에 메시지를 보냅니다!" });
    }
}

public class ViewModel2 : ViewModelBase
{
    public ViewModel2()
    {
        Messenger.Default.Register(this, OnMessageReceived);
    }

    private void OnMessageReceived(MessageClass message)
    {
        // 메시지 처리 로직
    }
}

4.2. Simple Messenger

Simple Messenger는 간단한 메시징 기능을 제공하는 패턴이고, MVVM 패턴의 기본적인 구조를 활용합니다. 뒷단에서 이를 쉽게 사용할 수 있도록 추가적인 구현이 필요할 수 있습니다.

public class MessageBus
{
    private readonly Dictionary>> _subscribers = new();

    public void Subscribe(Action callback)
    {
        var type = typeof(T);
        if (!_subscribers.ContainsKey(type))
        {
            _subscribers[type] = new List>();
        }
        _subscribers[type].Add(x => callback((T)x));
    }

    public void Publish(T message)
    {
        var type = typeof(T);
        if (_subscribers.ContainsKey(type))
        {
            foreach (var action in _subscribers[type])
            {
                action(message);
            }
        }
    }
}

5. 적용 시나리오

메시징 시스템은 다양한 애플리케이션 시나리오에서 활용될 수 있습니다. 여기서는 몇 가지 구체적인 예를 설명하겠습니다.

5.1. 복잡한 Forms 애플리케이션

데이터 입력 폼을 가진 애플리케이션에서는 여러 개의 ViewModel이 서로 다른 입력 필드를 관리할 수 있습니다. 이 경우, 사용자가 입력한 데이터를 다른 ViewModel에 전달하고 이를 사용하여 특정 연산이나 집계를 수행할 수 있습니다.

5.2. 다중 문서 인터페이스(MDI) 애플리케이션

MDI 애플리케이션에서는 여러 문서가 동시에 열려 있을 수 있습니다. 이 경우 각 문서별 ViewModel이 서로에게 메시지를 전송하여 특정 시행이나 정보를 갱신하게 할 수 있습니다.

5.3. 사용자 설정 및 설정 창

애플리케이션에서 사용자 설정을 관리할 때, 설정이 변경되면 이를 다른 ViewModel에서 처리해야 할 수 있습니다. 이때 각각의 ViewModel이 메시징 시스템을 통해 설정 변경 사항을 간편하게 공유할 수 있습니다.

6. 결론

MVVM 패턴에서 ViewModel 간의 통신은 애플리케이션의 복잡성이 증가할수록 더 중요해집니다. 다양한 메시징 시스템을 통해 ViewModel 간의 loosely coupled 방식으로 통신할 수 있도록 하여, 애플리케이션의 유지 관리성과 확장성을 높일 수 있습니다. Prism의 EventAggregator, MVVM Light의 Messenger 등은 이러한 요구를 충족시키기 위해 널리 사용됩니다. 앞으로의 WPF 애플리케이션 개발에 있어서 messaging 시스템을 적절히 활용하여 더 나은 구조와 가독성을 제공하는 것이 중요합니다.