ํฐ์คํ ๋ฆฌ ๋ทฐ
์ค๋์, ์ด์ ์ ํฌ์คํ ํ ๊ธ์ ์ด์ด์ MVVM ํจํด์ ๋ํด ์์๋ณด๋ ค๊ณ ํฉ๋๋ค.
์์ ์์๋ดค๋ MVCํจํด๊ณผ ์ด๋ค์ฐจ์ด๊ฐ ์๋์ง๋ฅผ ํ์ธํ๋ฉฐ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
โป ๊ธ ์์ฑ์ ์๋ชป ๋ ๋ด์ฉ์ด ์์ ์ ์์ต๋๋ค.
๋ฐ๋ผ์, ์๋ชป๋ ๋ด์ฉ ์ง์ ํด์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.!!
MVVMํจํด์ ์์ฃผ ํ๋ฅญํ ๋์์ธ ํจํด์ ๋๋ค.
Model๊ณผ View๋ ์ฐ๋ฆฌ๊ฐ ์ด๋ฏธ ์๊ณ ์๋ component์ธ๋ฐ, MVC์ ๋ค๋ฅด๊ฒ
View Model๋ผ๋ ์๋ก์ด ์ค์ฌ์๊ฐ ๋ฑ์ฅํฉ๋๋ค.
(์ฌ์ค MVVM๊ณผ Apple's MVC ์ฌ์์ด ์๋ MVP๋ชจ๋ธ์ ์์ฃผ ๋น๊ตํ๊ณ ๋ ํ์ง๋ง, ์ฐ๋ฆฌ๋ ๋์ด๊ฐ๊ฒ ์ต๋๋ค.)
๋ํ, ์ด๋ฏธ ์๊ณ ์๋ Model๊ณผ View์ ์ญํ ๋ ์กฐ๊ธ ๋ฐ๋๊ฒ ๋๋๋ฐ, ์ด์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
1. Model
- Model์ ๋ฐ์ดํฐ, ๋คํธ์ํฌ ๋ก์ง, ๋น์ง๋์ค ๋ก์ง ๋ฑ์ ๋ด์ต๋๋ค.
- View์ ViewModel์ ๊ตฌ์ฑ๊ณผ ๋ณ๊ฒฝ์๋ ์ ํ ๊ด์ฌํ์ง ์์ผ๋ฉฐ, ๊ฐ์ง๊ณ ์์ Data๋ง ๋ค๋ฃน๋๋ค.
(์ฌ์ค ๊ธฐ์กด์ MVC์์์ Model๊ณผ ํฌ๊ฒ ๋ค๋ฅผ๊ฒ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.)
2. View
- ์ฌ์ฉ์๊ฐ App์ ์ฌ์ฉ ํ ๋, ์ค์ ๋ก ํ๋ฉด์์ ๋ณด์ด๋ (UI)์ ๋ํ layout, dataํ์๋ฑ์ ๋ด๋นํ๋ค.
- Model๊ณผ๋ ์ง์ ์ ์ธ ์ฐ๊ฒฐ์ด ์ด๋ฃจ์ด์ ธ์๋ ์๋๋ค.
๊ทธ๋ฆฌ๊ณ MVC์ MVVM์ ์ฐจ์ด์ ์ค ํ๋์ธ,
- View๋ ViewModel์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ๋ณธ์ธ์ด ์ง์ View๋ฅผ ๋ณ๊ฒฝํ๋ค.
์ด๋ฅผ ์ํด View๋ ViewModel์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฒ๋๊ณ , ์ฌ์ฉ์์ interaction์ ๊ฐ์งํด์
์ด์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ViewModel์ ์์ฒญํ๊ฒ ๋ฉ๋๋ค.
3. ViewModel
- View์์ ์ ๋ฌ๋ฐ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ก์ง์ ๊ฐ๋๋ค.
- Model์ ๋ณํ๊ฐ ์๊ธฐ๋ฉด ViewModel์ด View์๊ฒ ์๋ ค์ค๋ค
- ๋ฐ์ดํฐ์ ๋ณํ๋ฅผ View์๊ฒ ์๋ ค์ค๋ค๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค
- ์ด์ฒ๋ผ View์ Model์ฌ์ด์ ์ค์ฌ์ ์ญํ ์ ํ๋ฉฐ, Model์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ View๊ฐ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋ ํํ๋ก
์ฌ ๊ฐ๊ณตํ์ฌ ์ ๊ณตํด์ฃผ๋ Presentaion Logic์ ๊ฐ์ต๋๋ค.
์์์ ์ค๋ช ํ ํ๋ฆ์ ํ๋์ ๋์์ผ๋ก ์๊ฐํด๋ณด๋ฉด,
1) View์์ event๊ฐ ๋ฐ์ํ๋ค.
(ex ์ฌ์ฉ์๊ฐ ๋ฒํผ์ tapํจ)
2) View๊ฐ ํด๋น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ค๋ ์ฌ์ค์ ViewModel์ ์๋ ค์ค.
(ex View์ ํด๋นํ๋ VC์์ ViewModel์ ํน์ ๋ฉ์๋๋ฅผ ํธ์ถ ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ (๊ผญ ์ด๋ ๊ฒ ์ ํด๋ ๋ฉ๋๋ค!))
3) ๋ณ๊ฒฝ ๋ Data๋ฅผ ํตํด Model์ ์๋ก ์ ๋ฐ์ดํธ ํ๋ค.
4) Model์ด ๋ณ๊ฒฝ๋๋ฉด ํด๋น Model์ ์์ ํ๊ณ ์๋ ViewModel์ด ์๊ฒ๋๋ค.
5) ๋ํ, ํด๋น ViewModel๊ณผ bind๋์ด์๋ View๊ฐ ์ ๋ฐ์ดํธ ๋๋ค.
(View๊ฐ ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ค์ค๋ก UI๋ฅผ ์ ๋ฐ์ดํธ ์งํํจ)
์ ๋ฆฌํด๋ณด๋ฉด, Apples'MVC๋ ViewController๊ฐ View๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง์ ๊ฐ๊ณ ์๋ ๋์์ LifeCycle์ ๊ด๋ฆฌํ๋ ๋ฑ
Massive ํ ๋ฟ ์๋๋ผ, View์ ์๋นํ ๋ฐ์ ํ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.
ํ์ง๋ง, MVVM์ ViewModel์ View๋ฅผ ์ ์ง ๋ชปํ๊ณ , View๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง์ ๊ฐ์ง๊ณ ์์ง ์์ต๋๋ค.
์ด๋ฅผ ํตํด, ๊ฐ View ViewModel Model์ด ๋์ฑ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉ์ด ๋๊ณ
์ ๋๋จ์ ํ ์คํธ์์ ์๋นํ ์ฉ์ดํ ์ธก๋ฉด์ด ์์ต๋๋ค!
MVVM ์์ ์ฝ๋
์ด๋ก ์ ์ผ๋ก๋ ์ด๋ ๋ค๋๋ฐ, ์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด์ ํ๋ฒ ํ์ธ ํด ๋ณด๊ฒ ์ต๋๋ค.
(ํด๋น ์์ ๋ Reactive๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์์ฑํ ์์ ์ฝ๋ ์ ๋๋ค. Rx๋ฅผ ์ฌ์ฉํ ์์ ๋ ์ด ๊ณณ์ ์ฐธ๊ณ ํด์ฃผ์ธ์!)
ํด๋น ์์ ์ฝ๋๋ ์ํ์นด๋์ ๋ธ๋ก๊ทธ์์ ์ฐธ๊ณ ํ์์ต๋๋ค!
2๊ฐ์ TextField์ ์ฌ๋์ ์ด๋ฆ๊ณผ ๋์ด๋ฅผ ๋ฃ์ผ๋ฉด Label์ ํ์๋๋ ์ฑ์ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ , Model์ ๋ณด๊ฒ ์ต๋๋ค.
struct Person {
var name: String
var age: Int
}
์ด๋ฆ๊ณผ ๋์ด๋ฅผ ๊ฐ๋ Person ๊ตฌ์กฐ์ฒด ์ ๋๋ค.
๊ทธ ๋ค์, Person์ ํ์ ํ ViewModel์ ๋ง๋ค์ด์ค๋๋ค.
ViewModel
struct PersonViewModel {
var person: Person
}
ViewContoller์๋ UITextFieldDelegate์ ๋ฉ์๋์ธ [textFieldDidEndEditing]์ ํตํด์
textField์ ๋ณํ๊ฐ ์ผ์ด๋๋ฉด Lable์ ๋ฟ๋ ค์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค.
extension ViewController: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
guard let nameText = nameTextField.text,
let ageText = ageTextField.text,
let age = Int(ageText) else { return }
personViewModel.person.name = nameText
personViewModel.person.age = age
yearLabel.text = nameText + " " + "\(age)์ธ"
}
}
์ด ์ํ์์ ๋ณด๋ฉด, [UI์ ๋ณํ -> ViewModel์ ์ํฅ]์ ๊ณผ์ ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์ฆ ๋จ๋ฐฉํฅ์ผ๋ก ์์ฌ์ํต์ ํ๊ณ ์๋ค๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
์ด๊ฒ์, Property Observer๋ฅผ ํตํด DataBinding์ ํ์ฌ ์๋ฐฉํฅ์ผ๋ก ๋ณ๊ฒฝ ํด๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ , didSet์ ํตํด Model์ Person์ ๋ณํ๊ฐ ๊ฐ์ง๋๋ฉด ๋ณํ๋ Data๋ฅผ Setํด์ค๋๋ค.
๋ํ, Data๋ณ๊ฒฝ์ Event๊ฐ ๋ฐ์ํ ๋, ์ ์ ํ ์ฒ๋ฆฌ๋ฅผ ํ ๊ฐ์ฒด๊ฐ ํ์ํ๋ฐ Closure๋ฅผ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
bind(lisenter: Listener?) ๋ฉ์๋๋ฅผ ํตํด์ ์ธ๋ถ์์ PersonViewModel์ Listenerํ์ ์ listner๋ฅผ
์ง์ ํ ์ ์๊ฒ ํด์ค๋๋ค.
ViewModel + DataBinding
struct PersonViewModel {
typealias Listener = (Person) -> Void
var listener: Listener?
var person: Person {
didSet {
listener?(person)
}
}
...
mutating func bind(listener: Listener?) {
self.listener = listener
}
}
์ด๋ ๊ฒ ๋๋ค๋ฉด, ViewController์์๋ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ์ธ๋ฉ์ ์งํํ๋ฉด ๋ฉ๋๋ค!
override func viewDidLoad() {
...
personViewModel.bind { [weak self] person in
self?.yearLabel.text = person.name + " " + "\(person.age)์ธ"
}
}
์ด๋ฅผ ํตํด์ textFieldDidEndEditing์์ Person์ด ๋ณ๊ฒฝ๋์์ ๋, ์ฆ ViewModel์ด ๋ณ๊ฒฝ๋์์ ๋
์๋์ผ๋ก yearLabel์ด ์ ๋ฐ์ดํธ ๋ ๊ฒ์ ๋๋ค.
์ด ๋ฟ๋ง textField์์ฒด์ delegate๋ฅผ ์ฌ์ฉํ์ง ์๊ณ DataBinding์ ํตํด ๋ก์ง์ ์ง์ ๋ง๋ค์ด์ ์ํํ ์๋ ์์ต๋๋ค.
์ด๋ ๊ฒ MVVM์ ๋ํด MVC์ ๋น๊ตํ๊ณ , ๊ฐ๋จํ ์์ ๋ฅผ ํตํด ์ด๋ป๊ฒ ์ฝ๋๋ก ์ ์ฉํ๋์ง ์์๋ดค์ต๋๋ค.
MVVM์ ์ฅ์ ์ ์์ฃผ ๋ค์ํ๋ฐ, ๋ช ๊ฐ์ง๋ฅผ ์๊ฐํด๋ณด์๋ฉด
1) ViewController๊ฐ ๊ฐ๋ฒผ์ ์ง๊ฑฐ๋
2) View๋ ViewModel์ด ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธ ํ๊ธฐ์ ์ฉ์ดํ๋ค๋ ์ ๋ฑ์ด ์๊ฒ ์ฃ .
ํ์ง๋ง ์ด๋ฐ MVVM์๋ ํ๊ณ์ ์ ๋ช ํํฉ๋๋ค.
1) ๊ฐ๋จํ ํ๋ก์ ํธ์ ์ฌ์ฉํ๊ธฐ์๋ MVVM์ด ๊ณผํ ์ ์์ต๋๋ค.
(๋ฐ์ธ๋ฉ, ์์กด์ฑ ์ฃผ์ ๊ณผ ๊ฐ์ ์ฌ๋ฌ ์์ ์ด ํ์ํ ์ ์๋ค.)
2) ๋ฐ์ธ๋ฉ์ ๋ํ tool์ด ์์ผ๋ฉด ์์ฉ๊ตฌ ์ฝ๋๊ฐ ๋ค๋์ผ๋ก ๋ฐ์ํฉ๋๋ค.
3) presentationLogic์ด ๋์ด๋๋ฉด ViewModel์ด ๋น๋ํด์ง๋๋ค.
(์ด๊ฑด ๋ชจ๋ ๋์์ธ ํจํด์ด ๋ค ๊ทธ๋ด ๋ฏ)
4) Data Binding์ ๊ตฌํํ ๋, delegate pattern, closure๋ฑ ์๋นํ ๋ค์ํ ๋ฐฉ์์ด์๋ค๋ณด๋, ์ฌ๋๋ง๋ค MVVM์
๊ตฌํํ๋ ๋ฐฉ์์ด ๋ค ๋ค๋ฅด๋ค
(MVVM์ ์ฒ์ ์ ํ๋ ์ฌ๋์ ๋ง๋งํ๋ค)
์ด๋ ๊ฒ MVVM์ ๋ํ ์ ๋ฆฌ๋ฅผ ๊ฐ๋จํ๊ฒ ํด ๋ณด์์ต๋๋ค.
์ด์์ผ๋ก ๊ธ์ ๋ง์น๊ณ , ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!
[์ฐธ๊ณ ์๋ฃ]
https://velog.io/@ictechgy/MVVM-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4
https://okanghoon.medium.com/rxswift-4-mvvm-with-rxswift-17a9b6d43746
https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52