MVVM 은 WPF와 함께 소개되기는 했지만, WPF에 한정된 것은 아닙니다.
윈폼에도 적용할 수 있으며, 하다 못해 콘솔앱에도 적용할 수 있습니다.
질문의 그림으로 설명을 드리면,
첫째로 그러한 모양으로 보이도록 하는 것은 View의 역할입니다.
우측의 네모난 상자가 버튼이라면, 버튼 모양으로 보이게 하는 것도, 버튼 처럼 행동하는 것 - 클릭하면 눌려지는 액션을 하는 것도 뷰의 역할입니다.
둘째로, 버튼이 우측에서 좌측으로 넘어가는 것처럼 보이게 만드는 것도 View 의 일입니다.
그런데, 그 넘김의 의미가, 로직적 의미 - 예를 들면, 추가나 삭제할 항목을 표현하는 것이라면, 그 항목의 관리는 ViewModel 의 업무입니다.
public class ViewModel
{
// 로직을 위한 상태
public List<Item> Stored {get; set;} = [];
public List<Item> Temp { get set; } = [];
// 로직
public void MoveToTemp(string itemName)
{
if (Stored.FirstOrDefault(x => x.Name == itemName) is Item item)
Temp.Add(item);
}
}
// Psuedo View
<LeftSection>
<ItemsGrid/> // _viewModel.Temp 에 바인딩
</LeftSectoin>
<RightSection>
<ItemsStack/> // _viewModel.Stored 에 바인딩
</RightSection>
// View Code-behind
private ViewModel _viewModel;
private void OnButtonOnItemStackClicked(object? s, EventArgs e)
{
if (s is Button button)
{
_viewModel.MoveToTemp(button.Text);
}
}
보시다시피, 뷰는 뷰모델의 상태를 화면에 반영하기만 할 뿐 스스로 상태를 저장하지 않습니다.
또한 뷰는 로직을 구현하지 않고, 뷰모델에 있는 로직을 (코드 비하인드에서) 호출할 뿐입니다.
뷰와 뷰모델 사이에 통신은 코드 비하인드(Command)와 데이터 바인딩(Query)을 통해 이뤄짐을 알 수 있습니다.
뷰모델이 이렇게 설계되어 있다면, 이 뷰모델은 윈폼, 콘솔 뿐만 아니라, 어떤 UI 프레임워크에도, 그대로 사용할 수 있습니다.
이렇게 설계하면 뷰와 로직이 분리됐다고 보는 것이며, MVVM이 추구하는 바입니다.
MVVM이 잘 지켜지고 있는 지, 보다 엄밀히 말하면, 로직과 뷰가 적절하게 분리되었는 지 궁금하다면 뷰는 손을 안대고, 로직만 변화시켜도 전체 소프트웨어에 아무런 문제가 없는지를 확인하면 됩니다.
public class ViewModel
{
// ...
// 로직 구현의 수정
public void MoveToTemp(string itemName)
{
if (Stored.FirstOrDefault(x => x.Name == itemName) is Item item
&& Temp.FirstOrDefault(x => x.Name == itemName) is null)
Temp.Add(item);
}
}
WPF는 MVVM을 지원하는 여러 가지 객체를 제공하기 때문에 그 패턴을 따르면 소프트웨어를 작성하고 유지 보수할 때 편리한 것은 맞습니다.
그러나, 반드시 엄밀하게 따라야 하는 것이 진리일 수는 없습니다.
왜냐하면, 대부분의 디자인 패턴은 미래의 유지보수라는 수익을 위해 오늘의 손가락 품을 열심히 팔 것을 요구기 때문입니다.
그 만큼 보일러 플레이트 코드가 많다는 의미입니다.
지금 당장, 자원이 부족하다면 코드 비하인드에 다 때려 넣는 선택을 하더라도, 소프트웨어를 동작시키는 것에는 아무런 문제가 없습니다.
실제로 해보시면, 어떤 게 분리되어야 할 로직인 지를 결정하는 게 훨씬 더 어렵다고 느낄 것입니다.