[MVVM] 3.Asynchronous Programming과 MVVM, 비동기 커맨드와 태스크(Task) 패턴

C# WPF(Windows Presentation Foundation)는 강력한 UI 프레임워크이며, MVVM(Model-View-ViewModel) 패턴은 WPF 애플리케이션의 아키텍처를 구성하는 주요 패턴 중 하나입니다. 이 패턴의 고려 사항 중 하나는 비동기 프로그래밍입니다. 비동기 프로그래밍을 통해 UI가 차단되지 않고 사용자 친화적인 애플리케이션을 구축할 수 있습니다. 이 글에서는 비동기 프로그래밍, MVVM 패턴 내에서의 비동기 커맨드와 태스크(Task) 패턴에 대해 설명하겠습니다.

1. 비동기 프로그래밍의 개요

비동기 프로그래밍은 작업이 완료되기를 기다리지 않고 다른 작업을 계속 수행할 수 있도록 하는 프로그래밍 패러다임입니다. C#에서 비동기 프로그래밍은 주로 asyncawait 키워드를 사용하여 구현됩니다. 이러한 키워드는 비동기 호출이 완료될 때까지 UI 스레드를 차단하지 않도록 보장합니다.

  • Async: 메서드가 비동기적으로 실행됨을 나타냅니다.
  • Await: 비동기 작업이 완료될 때까지 기다립니다.

1.1 비동기 메서드의 구조


public async Task FetchDataAsync(string url) {
    using (HttpClient client = new HttpClient()) {
        var response = await client.GetStringAsync(url);
        return response;
    }
}
    

위의 예제는 URL에서 데이터를 비동기적으로 가져오는 메서드입니다. await는 작업이 완료될 때까지 대기하며, 이 동안 UI 스레드는 여전히 응답할 수 있습니다.

2. MVVM 패턴의 이해

MVVM 패턴은 데이터와 UI를 분리해 유지보수성을 높이고, 테스트 가능한 코드를 작성할 수 있도록 합니다. MVVM은 Model, View, ViewModel 세 가지 구성 요소로 이루어져 있습니다.

  • Model: 애플리케이션의 데이터와 비즈니스 로직을 정의합니다.
  • View: 사용자 인터페이스를 구성하며, 데이터 바인딩을 통해 ViewModel과 상호작용합니다.
  • ViewModel: View와 Model을 연결하는 역할을 하며, 명령과 입력을 처리합니다.

2.1 MVVM의 코드 예시


public class UserViewModel : INotifyPropertyChanged {
    private string userName;
    
    public string UserName {
        get => userName;
        set {
            userName = value;
            OnPropertyChanged(nameof(UserName));
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string propertyName) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
    

3. 비동기 커맨드와 태스크(Task) 패턴

MVVM 패턴 내에서 비동기 작업을 구현하기 위해 커맨드(Command) 패턴을 활용할 수 있습니다. 비동기 커맨드는 사용자 인터페이스의 명령을 비동기적으로 처리할 수 있도록 해줍니다. 일반적으로, ICommand 인터페이스를 구현하여 커맨드를 정의하며, 이 커맨드 내에서 비동기 메서드를 호출할 수 있습니다.

3.1 IAsyncCommand 인터페이스 생성


public interface IAsyncCommand : ICommand {
    Task ExecuteAsync(object parameter);
    bool CanExecute(object parameter);
}
    

IAsyncCommand 인터페이스는 비동기적으로 작업을 수행할 수 있는 커맨드를 정의합니다. 이 인터페이스는 기존의 ICommand를 확장하여 ExecuteAsync 메서드를 추가합니다.

3.2 AsyncCommand 구현


public class AsyncCommand : IAsyncCommand {
    private readonly Func execute;
    private readonly Predicate canExecute;

    public AsyncCommand(Func execute, Predicate canExecute = null) {
        this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
        this.canExecute = canExecute;
    }

    public async Task ExecuteAsync(object parameter) {
        await execute(parameter);
    }

    public bool CanExecute(object parameter) {
        return canExecute?.Invoke(parameter) ?? true;
    }
    
    // ICommand 멤버를 구현합니다.
    public event EventHandler CanExecuteChanged;
    
    public void Execute(object parameter) {
        ExecuteAsync(parameter);
    }

    public bool CanExecute(object parameter) {
        return canExecute?.Invoke(parameter) ?? true;
    }
}
    

3.3 ViewModel에서 AsyncCommand 사용


public class MainViewModel {
    public AsyncCommand LoadDataCommand { get; private set; }

    public MainViewModel() {
        LoadDataCommand = new AsyncCommand(LoadDataAsync);
    }

    private async Task LoadDataAsync(object parameter) {
        // 비동기 데이터 로드 처리
        await Task.Delay(2000); // 예시로 지연
        // 데이터 로드 후 처리
    }
}
    

위의 예제에서 MainViewModelLoadDataAsync 메서드를 호출하는 비동기 커맨드를 정의합니다. 이 커맨드는 UI에서 호출되면 비동기적으로 데이터를 로드합니다.

3.4 View에서 ICommand 바인딩

WPF에서는 XAML을 사용하여 ViewModel의 커맨드에 바인딩할 수 있습니다.



    

위의 코드는 버튼 클릭 시 LoadDataCommand를 호출합니다. 이 커맨드는 비동기 작업을 처리하므로 UI는 차단되지 않습니다.

4. 결론

MVVM 패턴에 비동기 프로그래밍을 결합하면 사용자 경험을 향상시킬 수 있으며, 응답성이 뛰어난 애플리케이션을 구축할 수 있습니다. C#의 비동기 메서드와 커맨드를 활용하여 복잡한 작업을 비동기적으로 수행함으로써 UI가 차단되지 않도록 구성할 수 있습니다. 이를 통해 개발자는 보다 나은 사용자 경험을 제공하고 애플리케이션의 품질을 향상시킬 수 있습니다.

5. 참고 자료