- 사용자의 Action들은 View를 통해 들어옵니다.
- View에 Action이 들어오면 ViewModel에 Action을 전달합니다.
- ViewModel은 Model에게 데이터를 요청합니다.
- Model은 ViewModel에게 요청받은 데이터를 응답합니다.
- ViewModel은 응답 받은 데이터를 가공하여 저장합니다.
- View는 Data Binding을 이용해 UI를 갱신시킵니다.
🟥 MVVM 패턴이란?
Model, View, ViewModel 로 구성된 패턴이다.
- Model: 애플리케이션의 데이터를 나타낸다. 비즈니스 로직과 관련된 데이터를 처리한다.
- View: 사용자 인터페이스를 나타낸다. 사용자에게 정보를 보여주고, 사용자 입력을 받는다.
- ViewModel: View와 Model 사이의 중간자 역할을 한다. View를 위한 데이터를 준비하고, View에서 발생한 이벤트를 Model에 전달한다.
✅ MVVM 패턴은 swiftUI에 주로 사용된다.
✅ 아래 예시는 UIKIT에서 사용할 경우이다.
1️⃣ Model
- User라는 구조체는 사용자의 이름과 나이를 속성으로 가지고 있다.
struct User {
let name: String
let age: Int
}
2️⃣ View
import UIKit
class UserListView: UIView {
let tableView = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
setupTableView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupTableView() {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UserCell")
addSubview(tableView)
}
private func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: topAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
}
3️⃣ ViewController (UIViewController 및 UITableView)
- UserListViewController는 UITableView를 사용하여 사용자 목록을 표시한다.
- 데이터 소스 및 델리게이트 메서드를 통해 ViewModel과 상호작용한다.
import UIKit
class UserListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private var viewModel = UserViewModel()
private var userListView: UserListView {
return view as! UserListView
}
override func loadView() {
view = UserListView()
}
override func viewDidLoad() {
super.viewDidLoad()
userListView.tableView.dataSource = self
userListView.tableView.delegate = self
viewModel.fetchUsers { [weak self] in
self?.userListView.tableView.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfUsers
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath)
let user = viewModel.user(at: indexPath.row)
cell.textLabel?.text = "\(user.name), Age: \(user.age)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
4️⃣ ViewModel
- UserViewModel 클래스는 사용자 목록을 관리하며, 사용자 데이터를 비동기로 가져오는 메서드를 포함한다.
- ViewModel은 View에 필요한 데이터를 제공하며, View에서 발생하는 이벤트를 처리한다.
import Foundation
class UserViewModel {
private var users: [User] = []
var numberOfUsers: Int {
return users.count
}
func user(at index: Int) -> User {
return users[index]
}
func fetchUsers(completion: @escaping () -> Void) {
// Simulating network call
DispatchQueue.global().async {
// Simulated delay
sleep(2)
// Simulated fetched data
self.users = [
User(name: "Alice", age: 30),
User(name: "Bob", age: 25),
User(name: "Charlie", age: 35)
]
DispatchQueue.main.async {
completion()
}
}
}
}
5️⃣ SceneDelegate (iOS 13이상)
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = UINavigationController(rootViewController: UserListViewController())
window?.makeKeyAndVisible()
}
...
}
🟧 MVC 패턴과 비교
🟧 MVC 구성요소
- Model: 애플리케이션의 데이터를 나타내며, 비즈니스 로직과 데이터 처리를 담당한다.
- View: 사용자 인터페이스를 나타내며, 사용자에게 정보를 보여주고, 사용자 입력을 받는다.
- Controller: Model과 View를 연결하는 역할을 하며, View에서 발생한 사용자 입력을 Model에 전달하고, Model의 변경 사항을 View에 반영한다.
1️⃣ Model
struct User {
let name: String
let age: Int
}
2️⃣ View
import UIKit
class UserListView: UIView {
let tableView = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
setupTableView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupTableView() {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UserCell")
addSubview(tableView)
}
private func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: topAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
}
3️⃣ Controller
import UIKit
class UserListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private var users: [User] = []
private var userListView: UserListView {
return view as! UserListView
}
override func loadView() {
view = UserListView()
}
override func viewDidLoad() {
super.viewDidLoad()
userListView.tableView.dataSource = self
userListView.tableView.delegate = self
fetchUsers()
}
private func fetchUsers() {
DispatchQueue.global().async {
sleep(2)
self.users = [
User(name: "Alice", age: 30),
User(name: "Bob", age: 25),
User(name: "Charlie", age: 35)
]
DispatchQueue.main.async {
self.userListView.tableView.reloadData()
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = "\(user.name), Age: \(user.age)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
✅ 차이점 요약
🟥 MVC 패턴
- View와 Controller가 명확히 구분되지 않는 경우가 많다.
- Controller는 Model을 직접 조작하고, View를 업데이트한다.
- View는 Controller와 긴밀히 연결되어 있다.
🟥 MVVM 패턴
- View와 ViewModel은 완전히 분리되어 있다.
- ViewModel은 Model을 조작하고, View에 필요한 데이터를 제공한다.
- View는 ViewModel과 바인딩하여 데이터를 표시하고 업데이트한다.
- ViewModel은 View에 대한 참조를 가지지 않으며, 이는 테스트와 재사용성을 높인다.
✅ MVVM 패턴은 View와 비즈니스 로직의 명확한 분리를 제공하여 코드의 유지보수성과 테스트 용이성을 향상시킨다.
✅ MVC 패턴은 구조가 단순하지만, View와 Controller 간의 강한 결합으로 인해 복잡한 애플리케이션에서는 코드 관리가 어려울 수 있다.
'iOS > Swift' 카테고리의 다른 글
Combine (MVVM 패턴) (0) | 2024.05.30 |
---|---|
MVVM 패턴 - Binding 개념 (0) | 2024.05.30 |
고차함수 (Map, Filter, Reduce) (0) | 2024.05.26 |
MVC 패턴 (Model - View - Controller) (0) | 2024.05.25 |
확장 (Extension) (0) | 2024.05.24 |