본문 바로가기

Swift 공부

Swift MVVM

1. MVVM

1. 개요

Model-View-ViewModel(MVVM) 패턴은 UI 디자인 패턴입니다. 그것은 MV라고 알려진 더 큰 패턴 모음중에 하나이며, Model View  Controller(MVC), Model View Present(MVP), 그외 여러가지가 포함됩니다.

이러한 각 패턴은 개발과 테스트가 더 쉬운 앱을 만들기 위해 비즈니스로직으로 부터 UI 로직을 분리합니다.

 

MVC

 

MVC 패턴은 UI를 앱의 상태를 표현하는 Model로 분리하며, View는 UI 컨트롤로 구성되어 있고, Controller는 사용자 상호작용을 처리하고 모델을 적절하게 업데이트 합니다. MVC 패턴의 가장 큰 문제점은 기능이 Controller에 많이 몰려있기에 Controller가 비대해진 다는 단점이 있습니다. 해당 문제를 보완하기위해 Martin Fowler는 Presentation Model이라는 용어를 사용하는 MVC 패턴의 변형을 도입했으며 , Microsoft는 MVVM이라는 이름을 채택하고 대중화 시켰습니다.

  • 현재 알려진 MVVM과 Microsoft의 MVVM은 약간 다른 사고를 가지고있다고한다.

MVVM

이 패턴의 핵심은 ViewModel이며 , 앱의 UI 상태를 표현하는 모델의 특별한 타입입니다. ViewModel에는 모든 UI 컨트롤과 각각의 상태를 표현하는 로직들이 들어있습니다. 예를 들어, UITextField 현재 텍스트, 또는 특정 버튼의 활성화 여부입니다. 또한 버튼 탭이나 제스쳐와 같이 수행할 수 있는 로직을 표현합니다.

MVVM의 특징

  • View는 ViewModel에 대한 참조를 가지지만, 반대로는 아닙니다.
  • ViewModel은 Model에 대한 참조를 가지지만, 반대로는 아닙니다.
  • View는 Model을 참조하지 않거나 Model이 View를 참조하지 않습니다.
  • TestAble한 코드작성에 용이합니다.

여기서 Data Binding이란

Data Binding은 View를 ViewModel과 연결하는 것이라고 보면 편하다

 

그렇다면 MVVM의 내가 느끼는 MVVM 의 핵심은 무엇일가?

내 생각에는 MVVM의 핵심은 위에서 말한대로 ViewModel에 있지만 

ViewModel이 있음으로 View 와 Model 이 서로 모른다 라는게 가장 큰 핵심인 것 같다.

그리고 ViewModel과 View도 직접적으로 연결되어있는것이아닌 데이터를 바인딩 시켰기에 서로 참조를 하지만 직접적으로 연관이 없다고해야하나... 흐음 .. 이걸 참 뭐라고 표현해야할지 모르겠지만 그렇기에 서로 따로 따로 테스트 코드를 작성할 수 있다.

 

실제 예제 Data Binding 객체는 RxSwift , Combine 을 많이 사용하지만 이번 예제에서는 따로 사용하지 않고 직접 구현 하여 사용하겠습니다.

 

 

import Foundation
import UIKit

//Observable 객체 참고
//https://fomaios.tistory.com/m/entry/Design-Pattern-MVVMModel-View-ViewModel-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-feat-Swift
//RxSwift , Combine 으로 대채해서 구현해볼 예정
class Observable<T> {
    typealias Listener = (T) -> Void
    var listener: Listener?
    var value: T {
        didSet {
            listener?(value)
        }
    }
    init(_ value: T) {
        self.value = value
    }
    func bind(listener: Listener?) {
        self.listener = listener
        listener?(value)
    }
}

https://fomaios.tistory.com/m/entry/Design-Pattern-MVVMModel-View-ViewModel-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-feat-Swift

 

[Design Pattern] MVVM(Model - View - ViewModel) 패턴이란? (feat. Swift)

안녕하세요 Foma 💻 입니다! 오늘은 정말 오랜만에 디자인 패턴을 정리하게 되었는데요. 그 중에서 가장 인기(?)있고 핫한 MVVM 디자인 패턴을 다뤄보려고 합니다. (SwiftUI 또한 기본 디자인 패턴으

fomaios.tistory.com

링크에 있는 옵저버블 객체를 이용해 만들었고 다음 RxSwift , Combine 을 공부하면서 해당값들을 삭제 할 예정

 

ViewController 

메인화면

import UIKit

class ViewController: UIViewController {

    let viewModel = ViewModel()
    @IBOutlet weak var NameLabel: UILabel!
    @IBOutlet weak var MainImageView: UIImageView!
    
    @IBOutlet weak var TypeLabel: UILabel!
    @IBOutlet weak var FancamURL: UILabel!
    @IBOutlet weak var PreviousButton: UIButton!
    @IBOutlet weak var NextButton: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        ViewModelBinding()
        PreviousButton.addTarget(self, action: #selector(previousButtonAction), for: .touchUpInside)
        NextButton.addTarget(self, action: #selector(nextButtonAction), for: .touchUpInside)
        // Do any additional setup after loading the view.
    }

    func ViewModelBinding(){
        viewModel.selectedMemeber.bind{ member in
            self.NameLabel.text = member?.name
            self.MainImageView.image = UIImage(named: member?.imageName ?? "")
            self.TypeLabel.text = member?.memberType
        }
    }
    @objc func previousButtonAction(){
        viewModel.tapButton(isPrevious: true)
    }

    @objc func nextButtonAction(){
        viewModel.tapButton(isPrevious: false)
    }
}

 

ViewModel

//
//  ViewModel.swift
//  MVVMTestApp
//
//  Created by 강지윤 on 2022/03/09.
//

import Foundation

class ViewModel {
    private var members : [MVVM_GFriendModel] = []
    var selectedMemeber : Observable<MVVM_GFriendModel?> = Observable(nil)
    private var index : Int = 0
    
    init(){
        self.members.append(MVVM_GFriendModel(name: "소원", imageName: "소원", memberType: "서브보컬 , 서브댄서 , 쏘리다", fancamURL: "https://www.youtube.com/watch?v=PeCsIDzTsb0"))
        self.members.append(MVVM_GFriendModel(name: "예린", imageName: "예린", memberType: "서브보컬 , 서브댄서 , 옌니", fancamURL: "https://www.youtube.com/watch?v=gw92213ow9s"))
        self.members.append(MVVM_GFriendModel(name: "은하", imageName: "은하", memberType: "서브보컬 , 최애캐 , 으나 , 짜냥이", fancamURL: "https://www.youtube.com/watch?v=G_-SNiuZrJA"))
        self.members.append(MVVM_GFriendModel(name: "유주", imageName: "유주", memberType: "메인보컬 , 가창력갑 , 바리스타 유주", fancamURL: "https://www.youtube.com/watch?v=Rk9nbLFm_uo"))
        self.members.append(MVVM_GFriendModel(name: "신비", imageName: "신비", memberType: "메인댄서 , 춤신춤왕 , 막내", fancamURL: "https://www.youtube.com/watch?v=L9U3hcCSKIA"))
        self.members.append(MVVM_GFriendModel(name: "엄지", imageName: "엄지", memberType: "서브댄서 , 서브보컬 , 막내", fancamURL: "https://www.youtube.com/watch?v=Rrm62tCrNVw"))
        
        self.selectedMemeber.value = members[index]
        
        
    }
    func tapButton(isPrevious : Bool) {
        if isPrevious {
            index = index > 0 ? index-1 : members.count - 1
        }else {
            index = index < members.count - 1 ? index + 1 : 0
        }
        self.selectedMemeber.value = members[index]
        
    }
    
    
}

 

 

위의 코드들을 확인해보면 ViewController에서 ViewModel의 데이터를 바인딩시켜 데이터가 변화가 있을때마다 데이터에 맞춰 화면이 변화하는 코드를 볼 수 있다. 하지만 ViewController 이  ViewModel을 참조 하고 있지만 ViewModel 이 ViewController 을 참조 하고 있지 않은것을 알 수 있다 ViewController 이 Model을 직접 보지 않는다..? 라고 하기에는 조금 애매하기에 이건 추후에 변경을 진행하도록 하겠다.

 

이렇게 갖아 기초중의 기초인 MVVM 을 완성하였고 추후에 Combine으로 변환을 진행 한 후 RxSwift 를 기반으로 점점 고도화를 시킬 예정이다.

'Swift 공부' 카테고리의 다른 글

GCD - (1)  (0) 2022.03.14
MVVM RxSwift , Combine  (0) 2022.03.12
Protocol Oriented Programming(POP) - With RxSwift Mvvm  (0) 2022.02.18
WKWebView MemoryLeak 해결  (0) 2021.11.12
타입캐스팅 is , as 와 Any , AnyObject  (0) 2021.08.05