본문 바로가기

TIL

[TID] #6 - MVVM에서 Cell 내부 Control의 Binding / List API에 대한 생각

MVVM에서 Cell 내부 Control을 Binding하는 깔끔하고 합리적인 방법에 대해 고민중이다.

기존에는 ViewModelType이라는 Protocol을 채택하여 Input과 Output을 미리 정의하여 ViewModel과 ViewController의 관계를 확실히 정의하고자 했다. 이러한 방식에서는 cell 내부에 존재하는 Observable에 대한 Binding을 해주기 위한 몇 가지 장치가 필요하다. 물론 이러한 장치는 스스로 생각해본 것들이고, 무엇이 좋은 방법인지 아직 확신이 서지 않는다. 처음에는 Cell 내부의 Control에 대한 Binding에서부터 시작해서 테이블뷰와 컬렉션뷰 구조에 대한 재고...까지 오게 되었다.

 

1. ViewModelType 사용을 고수하며 Cell Binding을 해보기

- 방법 1 : VC에 Subject를 선언해서, cell의 action을 구독하여 다시 전달하는 식으로 구현

- 방법 2 : CellViewModel을 만들어서 관리한다. CellViewModel은 아래와 같은 형식이고, ViewModel이 의존하는 구조이다.

class MemberCellViewModel {
    let index: Int
    let name: String
    let age: Int
    let inputTextChanged: PublishSubject<(String, Int)>
}

위와 같이 구현하고 cell에서는 이벤트를 보내고, viewModel에서는 이를 구독한다.

https://medium.com/p/a2386ee817a9#8ecf 에서 따온 아이디어이다.

 

https://stackoverflow.com/questions/38033596/creating-view-model-for-each-uitableviewcell 여기에도 View는 Model에게 요구하지 못하며, viewModel과만 소통해야 한다는 의견이 있다. indexPath마다 적절한 cellViewModel을 return해주는 메서드를 만들어두고, viewModel 내부에서 cellViewModel을 관리하고 바인딩한다. VM에서 VC -> Cell로 CellViewModel을 전달해준다고 생각하면 된다. list API가 reload 될 때마다 상태를 전달해주면 되는 것이다.

 

다만 아래의 경우와 같이 복잡한 UI의 경우 Protocol로 각 Cell의 데이터를 관리해야 하는데, 모델을 짜는데에 혼선이 생길 경우가 다분해 보인다.

(Protocol로 Complex UI 관리하기)https://medium.com/ne-digital/mvvm-pop-to-deal-with-complex-ui-d4c5b76a92e1

 

2. ViewModelType은 그대로 두고, Input을 조금 분산해보기

- 방법 : Cell Input Struct를 따로 만들어서 Binding한다.

 

3. ViewModelType Protocol을 버리기

- 방법 : ViewModelType을 통해 Input과 Output의 Struct를 강제하지 않고, ViewModel마다 고유한 Protocol을 통해 Input과 Output을 관리하기.


1차적인 결론은 1번에 있는 두 방법 중 하나를 쓰자는 것이다. 현재의 Input Output 구조가 코드의 가독성 및 응집력에 큰 도움을 주고 있다고 생각하며, 2번이나 3번의 방식은 결국 1번에 있는 아이디어에 대한 문제로 돌아오게 된다. 1번의 아이디어 중에서 VC에 새로운 Subject를 두는 방식은 개인적으로 편법? 같이 느껴진다. 세운 원칙에 맞게 개발을 하는 것이 탄탄한 아키텍쳐의 기본이라고 생각하기 때문이다. 또한 View Model이라는 것이 결국 View의 State를 기억하고 View가 최대한 dumb해야한다고 생각했을 때, Cell도 하나의 View이기 때문에 Cell에 ViewModel을 두는 것이 옳을 것이다. 라고 생각했지만 개발을 하다보면 항상 예상치 못한 이슈에 부딪힐 수 있기에 어느 방향으로든 가능성을 열어놓고 있다..


이와 별개로 아래와 같이 Binder를 만들어서 state를 Binding하는 것도 좋아 보여서 아카이빙.. https://github.com/tuan188/MGCleanArchitecture

// MARK: - Binders
extension EditProductViewController {
    var nameValidationBinder: Binder<ValidationResult> {
        return Binder(self) { vc, result in
            let viewModel = ValidationResultViewModel(validationResult: result)
            vc.nameTextField.backgroundColor = viewModel.backgroundColor
            vc.nameValidationLabel.text = viewModel.text
        }
    }
    
    var priceValidationBinder: Binder<ValidationResult> {
        return Binder(self) { vc, result in
            let viewModel = ValidationResultViewModel(validationResult: result)
            vc.priceTextField.backgroundColor = viewModel.backgroundColor
            vc.priceValidationLabel.text = viewModel.text
        }
    }
}

+ 또한 iOS에서 MVVM의 비합리성에 대해 주장한 글이 있었다. 주요 논지는 MVVM도 프로그래밍 방법론의 일부이고, 여러 객체지향 원칙에서 불리한 점이 보인다는 것이다. https://www.danielhall.io/the-problems-with-mvvm-on-ios

 

+ disposeBag이 cell의 life cycle로 인해 해제되는 문제를 prepareForReuse를 extension해서 해결한 소스가 있다. 그러나 cell cycle 관련되어서 sentMessage가 정확한 시점에 호출되는지에 대한 확신을 할 수 없다는 문제가 있다고 하는 것 같다.

https://github.com/ReactiveX/RxSwift/issues/821

 

+ Cell Control Binding에 대한 의견이 있다면 누구라도 남겨주시면 감사하겠습니다...