ReactiveUI는 코드비하인드를 사용해서 MVVM을 합니다.

저는 MVVM을 처음 배울 때부터 굉장히 오랜 시간을 WPF에서 MVVM을 할 때는 코드 비하인드에 코드를 쓰면 안된다고 착각하며 살았습니다.

그런데 객체지향 개념을 배워가고,
캡슐화가 무엇이고,
추상화가 무엇인지 알아가게 되면서,

결국 ‘ViewModel 과 View의 의존성이 결국 끊어지는 것이 중요하구나’ 라고 생각이 들었고, View에 관련된 로직들만큼은 컨트롤들에 있어도 되는구나 생각이 들었었습니다.

사실 MVVM과 관련은 없지만 CustomControl을 개발할 때는 코드 비하인드에 코드를 칠 수 밖에 없습니다.
이런 식으로 제로 코드 비하인드는 개발을 불편하게 하는 요소임에는 분명합니다.

물론 MVVM을 처음 연습할 때는 제로 코드비하인드를 지향하면서 연습하는 것이 좋다고 생각합니다. 그로 인해서 WPF의 다양한 MVVM 스킬에 대해서 성장할 수 있기 때문입니다.

대표적으로 이벤트를 어떻게든 안쓰기 위해 Behavior라는 개념을 배우는 것입니다.

아무튼 View와 ViewModel이 View가 봤을 때도, ViewModel이 봤을 때도, 서로가 서로를 모르도록 의존성이 끊어지는 것이 MVVM의 핵심이구나 하고 알게되었습니다.

그런데 RxUI의 이 개념은 제가 그동안 알아왔던 것을 부정하는 기분이었어서 처음 배울 때 많이 불편했습니다.

https://www.reactiveui.net/docs/handbook/view-models/#common-mistakes-and-misconceptions

Another common misconception is that of separation - while it is very important that the ViewModel have no reference to the View or any of the controls that the View creates, the reverse is not true . The View is free to be very tightly bound to the ViewModel, and in fact, it is often useful for the View to “reach into” the ViewModel via WhenAny, WhenAnyValue and WhenAnyObservable.

DeepL 번역하면

또 다른 일반적인 오해는 분리에 대한 오해입니다. 뷰모델이 뷰 또는 뷰가 생성하는 컨트롤에 대한 참조가 없어야 한다는 것은 매우 중요하지만, 그 반대는 사실이 아닙니다. 뷰는 뷰모델에 매우 긴밀하게 바인딩할 수 있으며, 실제로 뷰가 언제든, 언제든값, 언제든옵저버블을 통해 뷰모델에 "도달"하는 것이 유용할 때가 많습니다.

여기서 관련해서 자료를 좀 찾아보니, MVVM에도 ViewModel First vs View First 개념을 알 수 있었습니다.

ViewModel을 먼저 정의하고 View를 정의하는 관점과, View를 먼저 정의하고 그에 맞는 ViewModel을 정의하는 방법인데요.

ViewModel을 먼저 정의하고 View를 정의할 때는 ViewModel이 View에 의존하지 않게 개발을 시작할 수 있어서 ViewModel에 대해서도 UnitTest를 가능하게 하는 완벽한 의존성 제거를 할 수 있습니다. 또한 여러 View에 DataContext로 ViewModel을 작게 작게 껴넣을 수 있어서 이론상 ViewModel과 View가 1:N 관계가 될 수 있습니다.

View를 먼저 정의하고 ViewModel을 정의하는 관점은 View에 딱 맞는 ViewModel을 설계하므로서 View와 ViewModel이 1:1 관계가 되는 것입니다.
명시적인만큼 유지보수도 쉽습니다. 어차피 ViewModel 이라는 게 View 의 데이터를 다루는 객체라면 유지보수하면서 굳이 분리시켜서 할 필요가 없다는 관점입니다.

이 View First의 관점일 때 RxUI의 지침을 적용하면 ViewModel에 대해서 UnitTest는 불가할지라도 편하게 개발할 수 있는 것입니다.

물론 View First를 할 때도 WPF의 흔한 방식인 DataTemplate의 DataType을 이용해서 ViewModel과 View를 매칭시켜서 ViewModel First를 할 때처럼 코드 비하인드를 최대한 제거하면서 개발할 수는 있지만, RxUI의 관점을 익혀보는 차원에서 지침대로 해보는 것도 좋을 것 같습니다.


여담이지만 제가 RxUI를 쓰면서 지침대로 하지 않으면서 제로 코드비하인드 형태로 개발을 해봤는데 문제없이 동작도 했고 나름 편리하기도 했습니다.

여기서 말하는 지침은 ReactiveWindow를 사용해서 코드비하인드에서 Generic에 ViewModel 타입을 지정하는 것입니다.

이렇게 하지 않고 그냥 RxUI를 쓰더라도 ReactiveWindow같은 RxUI의 컨트롤을 쓰지 않고 WPF의 Binding 문법으로 해도 잘 동작은 했다는 의미입니다.

11개의 좋아요

좋은 내용들이 많습니다. 감사드립니다!
XAML 기반의 개발을 시작하시거나 하고 계신 분들에게서 많이 읽혀졌으면 좋겠습니다. :laughing:

2개의 좋아요

관계를 1:1 이 되지 않게, Unit 테스트가 가능하게 하는 것은 뷰모델 설계의 묘의 문제인 것 같습니다.
뷰 우선이든, 뷰모델 우선이든 상관없이 말이죠.

제가 사용한 방법은 뷰모델의 코드를 작성하면서 아래의 것들을 자문하는 것이었습니다.

지금 작성하고 있는 뷰모델은,

“콘솔앱에도 그대로 사용가능한가?”
“웹앱에도 그대로 사용가능한가?”
“윈폼에도 그대로 사용가능한가?”

이런 질문들을 스스로에게 던지면서 뷰모델을 설계하다 보면, 특정 UI 프레임워크 종속적인 객체들은 뷰모델에서 거의 다 제거되고, 결국에는 뷰와 독립적인 뷰모델-모델의 덩어리만 남게 되더군요.

이 상태에서는 어떤 뷰 도구를 사용해도 OCP 위배가 없는 - 뷰모델-모델 덩어리를 손댈 필요가 없게 됩니다.

[바이트론] C# 윈폼 개발자 구인합니다. - :raised_hand: 직업 게시판 / 구인 - 닷넷데브 (dotnetdev.kr)

위 글의 요청은 사실 항상 일어날 수 있는 일일 것입니다.
내가 작성한 뷰도 항상 아쉽거나, 몇 달 지나면 지겨워 지잖아요?

뷰를 수정할 때마다 뷰모델-모델을 수정해야 한다면, 혹은 더 좋은 뷰 도구로 갈아 타는 것이 망설여진다면, 뷰모델에 뷰의 객체가 섞여 있을 확률이 매우 높고, 이는 유지 보수를 어렵게 만드는 요인입니다.

위 글의 답변에도 있듯이, 재작성 이외에는 달리 떠오르는 대안이 없게 되는 것이죠.

제 개인적으로는, 요즘 블레이저를 사용하면서, (MVVM과 상관없는) View First 에 대한 확신이 점점 차오르고 있습니다. 클린아키텍쳐, 엔터프라이즈 패턴, MVVM 이런 거 다 부질 없다는 생각이 많이 듭니다.

뷰(컴포넌트) 설계 후

Blazor WASM에 실으면 프론트 엔드앱
Blazor Server에 실으면 웹앱
WPF, 윈폼에 실으면 윈도우 데스탑 앱.
MAUI에 실으면 모바일 앱.
또 최근에는 리눅스에도 실을 수 있는 패키지까지 등장해서…

로직이 컴포넌트의 코드 파트(비하인드와 유사)에 있던, 뷰모델에 있던, 서비스에 있던, 앱의 형태가 바뀐다고 코드를 수정할 일이 없고, 컴포넌트는 그 자체로 클래스이기 때문에 Unit 테스트도 가능합니다.

다만, 페이지 컴포넌트는 항상 하나의 UseCase를 전담한다는 전제가 중요한데, 이는 사실 어려운 일도 아니고, 매우 상식적인 것입니다.

사용자가 쓰지도 않을 메뉴를 만들 필요도 없고, 사용자가 요구하는 메뉴는 반드시 만들어야 하니까요.

디자인 측면에서도 XAML 전문가에 비해 CSS 전문가의 공급이 매우 많다는 것도 경제적인 이득 중에 하나입니다.

5개의 좋아요

의견 감사드립니다.
결국 말씀하시고자 하시는 요지는 저와 비슷하다고 생각은 듭니다.
제가 표현을 잘못한 것 같습니다.

저는 제가 봤던 RxUI의 용법이 그랬다라는 것을 기록으로 쓴 것이고, RxUI를 사용해 보신 후에 제가 쓴 글과 느끼신 용법이 달랐다면 달랐다면 당연히 다른 의견이 있을 수 있다고 생각합니다. 제가 RxUI를 사용해본 수준이 View와 ViewModel 1:1 해온 수준 밖에 안되어서 이런 글이 나온 것일 수 있습니다.

말씀하신 패턴, 아키텍쳐들이 추상화 개념에서 출발하여 확장된 개념은 맞기 때문에 결국 중요한 게 추상화다 라는 의미로 보면 필요 없는 게 맞지만, 확장된 개념 또한 여러사람들이 범용적으로 사용하는 개념입니다. 말씀하신 것 이외에도 CQRS 같은 개념도 결국 성장한 도메인에 대해 유지보수의 시간을 줄이고자 추상화가 발전된 하나의 형태로 예를 들 수도 있죠. 더 있을 것입니다.

마치 처음 오르는 산에 대해 정상을 목표로 바라보며 여러 등산로 중 하나를 선택하고,
정상에 올라서고 나면 나와 다른 등산로를 통해 올라온 등산객도 만날 것이고,
정상에서 같은 풍경을 보면서 등산객들과 다른 생각을 갖는 것과 비슷하다고 생각합니다.
스스로 동시에 여러 곳에 존재해서 다른 등산로를 모두 경험하지 않는 이상 불가능한 일이죠.

제 결론은 그냥.
유지보수를 위해 모든 과정에 있는 개념은 모두 다 필요하고 적절히 섞여야 한다는 것입니다.
내가 결론이라고 생각한 수준도 더 발전한 누군가에겐 그저 과정일 수 있습니다.
뭐든 극단적인 답은 절대로 이 세상에는 존재할 수 없다고 생각합니다.
그래서 그냥 ‘너는 음~ 그랬구나~’ 정도로 존중하는 형태가 있는 거라고 생각합니다.
그런 벼는 익을 수록 고개를 숙이는 리스펙의 마음은 수 많은 고수들도 초보에게 뭐라도 배울 수 있게 하는 기회를 제공할 것입니다.

그 어떤 뛰어난 추상화가 있어도 불안정한 인간이 만든 시스템에는 언제나 헛점이 있습니다. 그래서…다른 말이긴 한데, 결국 중요한 것이 뭔지 강조하고자 한다면…사실 이런 추상화나 패턴보다는,

혼자 일하는 게 아닌 이상 나와 수준이 다른 동료들과 좋은 소통하려는 마인드셋을 갖추는 것이 우선이 되어야 한다고 봅니다. 코딩 컨벤션이나 아키텍쳐를 정의하기전에 같이 협업하는 동료들과 라포 조차 쌓여 있지 않다면 수많은 개념은 모두 무용지물일 것이라고 생각합니다.

이러는 과정에서도 생각을 정리하면서 뭐라도 하나 배울 수 있음에 감사드립니다.

6개의 좋아요

제가 블로그 때려치고, 여기에 글을 많이 쓰는 이유도 거기에 있습니다.

글의 내용이 덜 어설퍼 보이도록 노력하게 되고, 혹시라도 딴지거는 분이 있다면, 그 분께 배우고자 하는 것이죠.

5개의 좋아요