- MVVM 패턴
- https://www.raywenderlich.com/34-design-patterns-by-tutorials-mvvm 에 따르면
- Massive View Controller 때문에
- Model을 View에 보여줄 값으로 바꾸는 ViewModel을 도입한다
- ViewModel을 각기 View의 property에 주입하는 코드를 ViewModel로 옮길 걸 추천한다. 하지만 이는 ViewModel이 한 View에만 적용될 때고
- 여러 View에 사용된다면 configure하는 코드를 View에 넣을 것을 추천한다
- 아니 그러면 ViewModel이 View를 알게 되는 것/ View가 ViewModel을 알게 되는 것으로 나뉘어버린다.
- 구조가 확 갈린다고 볼 수 있다.
- 그리고 ViewModel은 Model을 소유한다. Controller는 View와 ViewModel을 소유한다.
- 또다른 coupling이 생기는 것은 원치 않기 때문에 나는 ViewController가 ViewModel의 값을 View에 configure하는 게 좋다고 생각한다.
- 그럼 이런 형식으로 보여주는 tutorial이 있나?
- https://youtu.be/bFoLlwuzAtk
- 여기에서는 그렇다!
- ViewController를 생성할 때 아예 model을 이용한 viewModel을 넣어줌
- 그걸로 View를 보여준다.
- ViewController가 property로 model을 갖고 있던 것이 viewModel로 대체된다
- 여기서 피어난 궁금증은 그럼 유저 액션이 있을 때는 어떻게 되냐는 거다.
- ViewModel에 알려주고, 새로워진 값을 View에 업데이트를 한다면. 이것 역시 똑같이 Controller에서 하는 것이 되겠다.
- 근데 MVVM은 항상 binding하는 개념을 끌고 온다. 위의 과정이 보통 그런식으로 수행된다. Reactive나 Combine으로
- 왜 그런거지?
- https://en.wikipedia.org/wiki/Model–view–viewmodel
- MVVM 개념을 보면 알 수 있다.
- MVVM 자체의 목적은 Presentation Logic을 (Business Logic과 Data)에서 분리하기 위함이다.
- ViewModel은 Display Logic을 위한 것이다
- 마틴 파울러의 Presentation Model Design Pattern의 변형이다
- https://martinfowler.com/eaaDev/PresentationModel.html Represent the state and behavior of the presentation independently of the GUI controls used in the interface
- 인터페이스의 GUI 컨트롤에서 presentation의 상태와 행동을 독립적으로 분리하여 표현하는 것.
- 특히 event-driven programming에 특화되어 있다
- model-view-binder라고 말하기도 한다
- Components
- View
- Event handling을 data binding(property나 event callback 등)을 통해서 ViewModel에 전달한다
- View Model
- Public property와 command를 가진 View의 추상
- MVC의 Controller나 MVP의 Presenter 대신에 Binder가 view와 view model의 bind된 properties 사이의 커뮤니케이션을 한다
- Model의 State
- Presenter와 Binder의 차이는 Presenter는 view에 대한 reference가 있다면 binder은 없다.
- 대신에 view가 직접적으로 view model의 property와 bind된다. update를 보내고 받기 위해서.
- reference를 통한 update가 아니라 bind!
- 효과적으로 동작하려면 binding 기술이 있던 bind를 위한 boilerplate 코드가 있던 해야 함
- Binder
- View와 ViewModel을 연결한다
- binding 기술이 이 패턴을 가능하게 한다. binder가 없다면 MVC나 MVP를 boilerplate 코드와 함께 쓰는 것과 다름없다.
- View
- Rationals
- View Code에서 GUI(User Interface, 유저와의 상호작용 코드)를 없애는 것에 있다.
- 그래서 UX 개발자는 GUI 코드를 작성하는 게 아니라 application 개발자가 작성한 view model에 binding만 한다.
- 비즈니스 로직은 모두 View Layer에서 빠진다.
- 이로써 interactive 디자이너들은 programming이나 비즈니스 로직이 아닌 UX에만 집중할 수 있다
- 결과적으로 binding을 사용하므로서 code-behind가 아닌 model과 framework가 최대한 많은 operation을 수행한다. view를 직접적으로 manipulate하는 application logic을 없애거나 최소화한다.
- 왜냐하면 MVVM은 그 패턴 자체가 Model과 ViewModel을 자동적으로 binding하는 걸 특징으로 하기 때문이다. 그래서 유저 이벤트를 viewModel에 알려주면 ViewModel이 자동적으로 View를 업데이트해야한다.
- 그러려면 View가 ViewModel을 갖고 있으면서 이벤트를 전달하고, 이를 ViewModel이 처리해서 View에 알려주면, ViewModel의 property에 직접 binding된 View의 요소들이 업데이트되면 된다.
- View가 ViewModel을 갖고 있는 것이 좋아 보임.
- https://www.raywenderlich.com/34-design-patterns-by-tutorials-mvvm 에 따르면
- 그럼 Combine은? https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine
- iOS 13+
- 컴바인은 이벤트 처리를 위한 프레임웍이다
- delegate이나 completion handler 없이 이벤트 소스에 하나의 processing chain을 달 수 있다
let sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: filterField)
.map( { ($0.object as! NSTextField).stringValue } )
.assign(to: \MyViewModel.filterString, on: myViewModel)
- 애플 문서에 ViewModel이 등장한다. MVVM의 Binding 기술이 Combine이다.
- SwiftUI tutorial에 MVVM 구조가 나오진 않는다. View가 직접 model의 리스트(modelData)를 갖고 있고
- 유저 이벤트가 발생하면
- model의 값을 직접 변경한다
- 사실 이 model의 리스트를 ViewModel이라고 보면 된다.
- 이 안의 property가 Published이다.
- view가 modelData에 user action을 전달하면 값이 변경되고 다양한 view가 Combine을 통해 modelData에 bind되어 있기 때문에 별다른 코드가 필요 없이 변경된 값이 바로 반영이 된다.
- 결국 ViewModel이 View를 Configure하는 것이 아니라, View가 ViewModel을 소유하고 ViewModel 값에 스스로 구독한다. 위에서 고민했던 view와 viewmodel의 configure의 위치가 SwiftUI로의 확장까지 고민한다면 View에 위치하는 것이 맞아보인다. 결국 Controller는 사라질 것이므로.
- binding은 View에 존재하고(ViewModel에의 연결)
- 즉, ViewModel은 View를 모른다
- display logic, presentation logic은 ViewModel에 위치시킨다
- 내 나름의 MVVM Design Pattern의 적용을 그려본다면(화살표는 “안다”라는 의존성을 의미함)
- binding은 View에 존재하고(ViewModel에의 연결)
요약하자면 MVVM의 핵심은
- ViewController의 역할을 줄인다.
- Presentation Logic은 ViewModel이 수행한다.
- View Layer에서 GUI Code, 비즈니스 로직을 제거한다.
- 뭔가가 뒤에서 View를 변경하거나 하지 않는다. 투명하게 View는 그냥 ViewModel에 Bind된다.
- ViewModel은 View를 모르고, View의 추상으로서 존재한다. 그래서 View Layer는 View의 추상에 구독만 하니까 Business Logic에서 철저히 분리되고. ViewModel은 View가 아닌 View의 추상에만 의존하게 된다.(의존이라기 보다도 자기 자신이 그 추상 그자체임). 그래서 View의 세부 구현이나 변경에서는 분리되게 된다.