Dev

React + MVVM

Jun Park 🎵 2023. 1. 11. 17:12

React 기반 프론트엔드 프로젝트에 MVVM을 접목할 수 있을까?

MVVM

https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm

  • 소프트웨어 아키텍쳐 패턴으로 Model + View + ViewModel의 약자
  • View -> ViewModel -> Model 방향으로 참조하며 역방향은 금지

View

e.g. LoginView

  • 화면의 모양, 배치, 구조 및 애니메이션 등 시각적 효과
  • Model, ViewModel에 있는 비즈니스 로직, 유즈 케이스 등으로부터 분리
  • View는 ViewModel과 1:1 관계
    • 왜 1:1 관계인가? UX 디자이너가 View, Logic 디자이너가 ViewModel을 독립하여 개발하는 관점에서 1:1 관계가 적절
  • 화면에 표시하는 데이터, 상태와 이벤트 함수는 ViewModel로부터 획득
  • View와 ViewModel 구현 간의 참조 분리를 위해 의존성 주입을 사용하여 ViewModel Instance를 실행 시점에 주입받음

ViewModel

e.g. LoginViewModel

  • View가 사용할 데이터와 함수 및 View에 데이터 갱신을 알리기 위한 Notifying을 구현
  • UI thread를 막지 않기 위해 I/O 작업, View에 데이터 갱신 Notifying 등은 비동기로 구현
  • ViewModel은 Model과 1:N 관계
  • Model의 데이터를 View가 사용하기 쉽게끔 데이터 객체로 변환 가능
  • Model을 View에 노출하는 경우 Model에서 ViewModel의 역할을 대신하기 위해 Data Binding, Notifying 등을 지원해야 함

Model

e.g. Account, Email, Password, LoginValidator, AccountRepository...

  • 도메인 모델, 비즈니스 로직, 검증 로직 및 이에 필요한 객체를 포함
  • 데이터 접근을 위해 Service 또는 Repository를 함께 사용

React + MVVM

React는 다음과 같은 편리한 DX로 많은 개발자들이 선호한다.

  • Data와 State를, 그것들이 필요한 Element와 독립시킨 Component 단위로 관리할 수 있다.
  • State가 갱신되었을 때 개발자 대신 React가 DOM을 수정한다.

React + MVVM에서 MVVM의 참조 관계, 의존성 주입 등은 문제가 아니다. 고민은 React의 State Management System과 MVVM의 ViewModel-View Notifying을 어떻게 일치시킬 것인가이다.

1. ViewModel을 useCustomHook으로 대체

ViewModel-View의 Notifying 관계를 React System을 활용하여 대체한다. 비즈니스 로직을 컴포넌트로부터 독립시킬 때 흔히 사용하는 useCustomHook 사례와 동일하다.

 

문제는 LoginViewModel이라는 이름이 React에 맞지 않다. useState를 사용하는 함수는 use를 접두사로 사용해야 한다. LoginViewModel로 만들면 Lint에서 오류가 발생한다.

 

useLoginViewModel로 이름을 붙이고 State, Method를 생성하여 반환하는 방법이 있다.

2. 가짜 setState로 Notifying 구조 대체

https://www.evozon.com/model-view-viewmodel-in-reactjs

ViewModel은 React와 독립된 순수 Observable 객체로 만든다. useViewModel<ViewModel>은 초기값으로 전달받은 ViewModel Instance를 useRef<ViewModel>로 참조하고 ref를 반환한다.

 

View는 useViewModel이 반환한 ViewModel의 Field와 Method를 사용한다. 이때 Field는 State가 아니기 때문에 갱신되더라도 React가 DOM을 조작하지 않는다. 이를 해결하기 위해 View에서 가짜 State를 사용한다. 이 State는 오직 Field가 갱신되었음을 알리기 위해 사용된다. Observable인 ViewModel의 Subscription에 View의 setState를 등록한다.

 

이 방법은 이전 방법보다 체계적으로 보인다. 하지만 useRef의 데이터 갱신을 반영하기 위해 가짜 State를 사용하는 점이 React의 철학과 부합하지 않다. 

+ Atomic Design

MVVM을 적용하면 동일 관심사 아래 State와 Element를 독립시킨 Component에서 State를 분리하기 때문에 React스럽지 않아 보인다. View의 목적인 "화면의 모양, 배치, 구조 및 애니메이션 등 시각적 효과"만 포함한 순수 UI Component와 비즈니스 로직을 주입한 Component를 분리하여 생각해 봤다.

  • Atom, Molecule, Organism: 순수 UI Component
  • Template: 위 3개를 조합하여 만든 Page Component로 ViewModel을 참조하지 않고 필요한 데이터와 함수를 Props로 전달받는 순수 UI Component
  • Page: ViewModel Instance를 사용하여 Template에 데이터와 함수를 전달하는 View Level

'Dev' 카테고리의 다른 글

2023년 1분기 회고  (0) 2023.04.06
Type (Alias) over Interface  (0) 2023.04.05
Implementing Async/Await From Scratch  (0) 2022.11.29
Async는 어떻게 기다려지는 걸까?  (1) 2022.11.29
Redux 대신 getServerSideProps + SWR  (0) 2022.11.26