VesselWheel

MVVM 아키텍처 활용하기 본문

Xcode Study

MVVM 아키텍처 활용하기

JasonYang 2024. 2. 3. 12:28

https://vesselwheel.tistory.com/165

 

iOS 아키텍처 패턴 이해하기(MVC, MVVM)

https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52 iOS Architecture Patterns Demystifying MVC, MVP, MVVM and VIPER medium.com MVC 패턴 이란? 더보기 https://developer.apple.com/library/archive/documentation/General/Concept

vesselwheel.tistory.com

MVVM 개념을 알아보았다면, 코드 단에서 어떻게 활용되는지 알아보자. 


1. 타입을 지정하기 위한 구조체 생성 

import Foundation

struct User {
    var name: String
    var age: Int
}

- 이름과 나이가 프로퍼티로 지정된 User타입 구조체 생성


2. 다리 역할을 할 ViewModel 생성 

import Foundation

protocol UserViewModelDelegate: AnyObject {
    func updateUserName(name: String)
    func updateUserAge(age: Int)
}

class UserViewModel {
    private var user: User  // User 타입의 user 프로퍼티 생성
    weak var delegate: UserViewModelDelegate? // delegate pattern 활용
    
    init(user: User, delegate: UserViewModelDelegate? = nil) {
        self.user = user
        self.delegate = delegate
    }
    
    lazy var userName: String = user.name {
        didSet {
            delegate?.updateUserName(name: userName)
        }
    }
    lazy var userAge: Int = user.age {
        didSet {
            delegate?.updateUserAge(age: userAge)
        }
    }
}

3. 사용자에게 보여지는 UI 부분인 View 생성(xcode는 ViewController가 View와 Controller의 역할을 함께 한다.)

//
//  MVVMViewController.swift
//  Instagram
//
//  Created by Jason Yang on 2/3/24.
//

import UIKit
import SnapKit

class MVVMViewController: UIViewController {

    lazy var nameLabel: UILabel = {
        let label = UILabel()
        label.textColor = .blue
        return label
    }()
    lazy var ageLabel: UILabel = {
        let label = UILabel()
        return label
    }()
    
    lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("random age", for: .normal)
        button.setTitleColor(.gray, for: .normal)
        button.addTarget(self, action: #selector(changeAge), for: .touchUpInside)
        return button
    }()

    //viewModel 프로퍼티 생성
    private var viewModel: UserViewModel
    
    //ViewModel 을 뷰에서 사용하기 위해 초기화
    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    // 인터페이스 빌더를 통해 뷰 컨트롤러가 생성될 때 호출되는 초기화 메소드
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.viewModel.delegate = self
        self.view.backgroundColor = .white
        self.updateLayout()
        self.nameLabel.text = viewModel.userName
        self.ageLabel.text = String(viewModel.userAge)

        // Do any additional setup after loading the view.
    }
    private func updateLayout() {
        self.view.addSubview(nameLabel)
        self.view.addSubview(ageLabel)
        self.view.addSubview(button)
        
        nameLabel.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        ageLabel.snp.makeConstraints { make in
            make.top.equalTo(nameLabel.snp.bottom)
            make.centerX.equalToSuperview()
        }
        button.snp.makeConstraints { make in
            make.top.equalTo(ageLabel.snp.bottom)
            make.centerX.equalToSuperview()
        }
    }
    
    @objc func changeAge() {
        viewModel.userAge = Int.random(in: 0..<100)
    }
}

extension MVVMViewController: UserViewModelDelegate {
    func updateUserName(name: String) {
        DispatchQueue.main.async {
            self.nameLabel.text = name
        }
    }
    
    func updateUserAge(age: Int) {
        DispatchQueue.main.async {
            self.ageLabel.text = String(age)
        }
    }
    

}

 

MVVM(Model-View-ViewModel) 아키텍처를 사용하여 Xcode 객체를 구현한 것입니다. MVVM은 UI 개발에서 가장 많이 사용되는 디자인 패턴 중 하나로, 모델(Model), 뷰(View), 뷰모델(ViewModel) 세 가지 구성 요소로 이루어져 있습니다.

  1. Model: 데이터와 비즈니스 로직을 처리하는 부분입니다. 여기서는 User 구조체가 Model에 해당합니다.
  2. View: 사용자에게 보여지는 UI 부분입니다. 여기서는 MVVMViewController 클래스가 View에 해당합니다.
  3. ViewModel: Model과 View 사이의 '다리' 역할을 합니다. View에서 필요한 데이터를 Model에서 가져와 가공한 후 View에 제공합니다. 이 코드에서는 UserViewController 클래스가 ViewModel에 해당합니다.

이 코드에서 ViewModel은 User 객체를 가지고 있고, User의 이름(name)과 나이(age)를 가져옵니다. 이 때, 이름과 나이가 변경되면, ViewModel의 delegate를 통해 View에게 알립니다.

View인 MVVMViewController는 ViewModel을 이용해 UI를 구성합니다. 뷰가 로드되면 ViewModel의 delegate를 자신으로 설정하고, ViewModel에서 제공하는 이름과 나이를 화면에 표시합니다.

또한, 나이를 변경하는 버튼이 있고, 이 버튼을 누르면 ViewModel의 나이를 랜덤한 값으로 변경합니다. 이 때 ViewModel의 나이가 변경되면, ViewModel의 delegate를 통해 View에게 알리고, View는 화면에 표시된 나이를 업데이트합니다.

이렇게 MVVM 패턴을 사용하면 Model, View, ViewModel 간의 역할이 명확하게 분리되어 코드의 가독성과 유지보수성이 향상됩니다.

 

Q & A


UserViewModel에서 초기화할 때 delegate: UserViewModelDelegate? = nil 을 nil값으로 준 이유가 무엇일까요?

더보기

UserViewModel 클래스에서 delegate 변수는 타입이 UserViewModelDelegate?이므로 옵셔널 타입입니다. 이는 delegate가 nil일 수도 있음을 의미합니다.

초기화 시 delegate를 nil로 설정하는 것은 일반적인 패턴입니다. delegate는 대개 뷰 컨트롤러와 같은 다른 객체에 의해 설정되며, 그 시점은 UserViewModel이 초기화된 이후일 수 있습니다.

따라서 UserViewModel이 초기화되는 시점에서는 delegate가 아직 설정되지 않았을 수 있기 때문에 nil로 초기화해두는 것입니다. 이후에 delegate가 필요한 시점에서는 delegate에 실제 객체를 할당합니다.

이 코드에서는 ProfileViewController의 viewDidLoad 메소드에서 self.viewModel.delegate = self라는 코드를 통해 delegate를 설정하고 있습니다. 이렇게 하면 UserViewModel은 delegate를 통해 ProfileViewController에게 필요한 정보를 전달할 수 있게 됩니다.