티스토리 뷰

지극히 저의 주관적인 생각입니다. 

MVVM의 장점 : Testable하다. -> ViewModel이 View와 독립적이다!

 

MVVM

View에서의 Action을 ViewModel로 전달합니다. 이후 ViewModel은 해당 로직에 맞는 작업을 다른 Model에게 요청하고, 이의 응답을 ViewModel State에 업데이트합니다.

View는 ViewModel State를 구독하여 State의 변화가 있을 때 View를 업데이트합니다.

 

오늘도 +, - 버튼을 넣어보겠습니다. :)

저번 MVC글에서는 오토레이아웃을 적용했는데.. SnapKit없이 쓰기에 정말 간단한 예제임에도 layout관련 코드가 너무 늘어다더라구요..

그래서 이번엔 스토리보드를 사용하였습니다. 

 

- View ( ViewController )

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var countLabel: UILabel!
    @IBOutlet weak var plusButton: UIButton!
    @IBOutlet weak var minusButton: UIButton!
    
    private let viewModel = ViewModel()
    
    func bind() {
        viewModel.countUpdated = { [weak self] count in
            guard let self = self else { return }
            self.countLabel.text = "\(count)"
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        bind()
        
        countLabel.text = "0"
        plusButton.setTitle("+", for: .normal)
        minusButton.setTitle("-", for: .normal)
        
        plusButton.addAction(UIAction(handler: { [weak self] action in
            guard let self = self else { return }
            self.viewModel.plusButtonTapped()
        }), for: .touchUpInside)
        
        minusButton.addAction(UIAction(handler: { [weak self] action in
            guard let self = self else { return }
            self.viewModel.minusButtonTapped()
        }), for: .touchUpInside)
    }
}

먼저 ViewModel과 binding해줍니다. 말씀 드렸던 ViewModel State를 View가 구독하는 로직이라 생각하시면 될 듯 합니다.

(State가 변했을 시 어떤 View를 업데이트 할 지 미리 클로져로 지정해 둡니다.)

 

다음 View의 버튼이 눌림에 따라 ViewModel에게 어떤 액션이 일어났는지 알려줍니다.

 

- ViewModel

import Foundation

final class ViewModel {
    var state: ViewState = ViewState() {
        willSet(state) {
            if let countUpdated = countUpdated {
                countUpdated(state.count)
            }
        }
    }
    
    var countUpdated: ((Int) -> Void)?
    
    func plusButtonTapped() {
        state.count += 1
    }
    
    func minusButtonTapped() {
        state.count -= 1
    }
    
    struct ViewState {
        var count = 0
    }
}

ViewModel에서는 View의 액션에 맞게 로직을 취한 후 State를 업데이트합니다.

그럼 끝!

 

이제 테스트를 위한 구조로 리펙토링 해볼까요?

- ViewModelType

protocol ViewModelInput {
    func plusButtonTapped()
    func minusButtonTapped()
}

protocol ViewModelOutput {
    var countUpdated: ((Int) -> Void)? { get set }
}

protocol ViewModelType: ViewModelInput, ViewModelOutput {
    var state: ViewModel.ViewState { get set }
}

ViewModel의 입, 출력을 나누었습니다.

또 ViewModel은 ViewModelType을 채택하도록 추가하였습니다.

private let viewModel: ViewModelType = ViewModel()

ViewController에서도 ViewModel이 아닌 ViewModelType에 의존하게 하였습니다.

지금은 ViewModel()을 하고 있지만 실제 사용 시 의존성주입을 통해 주입 받을 것입니다.

 

그럼 추상화를 함으로써 갖는 이득인 Stub객체를 만들어보겠습니다.

import Foundation
@testable import WhatIsMVVM

final class ViewModelStub: ViewModelType {
    var state: ViewModel.ViewState = ViewModel.ViewState()
    
    func plusButtonTapped() {
        state.count += 1
    }
    
    func minusButtonTapped() {
        state.count -= 1
    }

    var countUpdated: ((Int) -> Void)?
}

 

테스트코드입니다.

import XCTest
@testable import WhatIsMVVM

class WhatIsMVVMTests: XCTestCase {

    var viewModel: ViewModelType!

    func test_ViewModel에서_plusButtonTapped가_호출되었을때_count가_늘어난_후_ViewStateCount는_업데이트된다() throws {
        //give
        viewModel = ViewModelStub()
        
        //when
        viewModel.plusButtonTapped()
        
        //then
        XCTAssertEqual(viewModel.state.count, 1)
    }
}

 

MVVM, MVC에 대한 주관적인 회고

 

처음 스위프트를 공부할 때 정말 근본없는 상태에서 Rx부터 훑기 시작했었고, 채용공고에 나와있는 RxSwift + MVVM을 본 후, 간단하게 나와있는 예제를 흉내내며 개발했었습니다.

그러고 RxSwift + MVVM이 정말 편하고 좋다 말하였습니다.

RxSwift없이 MVVM을 구현할 줄 몰랐으며 MVVM의 장점이 무엇인지도 잘 몰랐습니다.

면접에서도 왜 MVVM을 쓰냐 물어 본다면 "View로부터 ViewModel이 독립적이다. Massive ViewController를 해결할 수 있다."라고 대답하는게 전부였습니다.

어느 블로그를 가든 쉽게 볼 수 있는 형식적인 말들에 넘지 않는다고 생각합니다.

 

 MVC에서도 View와 독립적인 Usecase를 작성할 수 있으며 역할 별 객체를 잘 분리하여 Massive ViewController 또한 해결할 수 있다고 생각합니다.

또한 추상화를 많이하게 되면 가독성은 떨어지고 구조복잡도는 올라가게 되죠.

 

하지만 MVVM의 ViewState는 어떠한 로직이 잘 행하여 졌는지 알기 좋은 방법이며, ViewModel을 바인딩하는 View의 방식 또한 좋다고 생각합니다.

 

상황에 따라 본인이 더 선호하는 방식의 아키텍쳐를 선택하면 좋을  것 같습니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함