์ผ๋ณธ์ด ๋ธ๋ก๊ทธ ์ํฐํด์ @young์ ๋ฒ์ญ๊ธ ์ ๋๋ค. ์์ญ์ด ํฌํจ๋์ด ์์ต๋๋ค.
- ใSwiftUIใงMVVMใๆก็จใใใฎใฏๆญขใใใใใจๆใ่ณใฃใ็็ฑ
- https://qiita.com/karamage/items/8a9c76caff187d3eb838
iOS ๊ฐ๋ฐ ํ์ฅ์์, ์ ์ธ์ UI๊ฐ ๋น์ฐํ๊ฒ ์ฌ์ฉ๋๋ ์๋๊ฐ ๋์์ต๋๋ค. SwiftUI๋ฅผ ์ฌ์ฉํด๋ณธ ๊ฒฝํ์ ๋ฉ์ก์ต๋๋ค. ์ต๊ณ ์ ๋๋ค.
ํ์ง๋ง ์ต๊ทผ, SwiftUI์์ ๋น์ฐํ๊ฒ "MVVM์ ์ฌ์ฉํ์" ๋ผ๊ณ ๋์์ ๋, "์ ๋ง ๊ทธ๊ฑธ๋ก ๊ด์ฐฎ์๊น?" ๋ผ๋ ์๋ฌธ์ด ๋ค๊ธฐ ์์ํ์ต๋๋ค.
์ ์๊ฐ์ ๊น์ด ํ๋ณด๋ฉด,
- ์ง๋ฌธ
- iOS๋ฅผ ๊ฐ๋ฐํ ๋, ์ ์ธ์ UI์์ MVVM์ ์ฌ์ฉํ๋ ๊ฒ์ ์ ๋ง๋ก ๊ด์ฐฎ์๊ฑธ๊น?
- ๊ฒฐ๋ก
- SwiftUI๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, MVVM์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ฉ์ถ์
- ์ด์
- ViewModel์ ์กด์ฌ๋ ์ ์ธ์ UI ์๋์๋ ํ์ ์๋ค ๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ
๋ผ๋ ๊ฒฐ๋ก ์ ๋ค๋ค๋๊ธฐ์ ์๊ฐํ ๊ฒ์ ์ด ์ํฐํด์ ์ ๋ฆฌํฉ๋๋ค.
iOS ๊ฐ๋ฐ ํ์ฅ์์, ๊น๊ฒ ์๊ฐํ์ง ์๊ณ ํ๋ก์ ํธ์ ์ํคํ ์ณ๋ก MVVM์ ์ ํํ๊ณ ์์ง ์๋์?
ํน์๋ผ๋ ๋น์ ์ด SwiftUI์์ MVVM ํจํด์ด ๋ฐฉํด๋๋ค๊ณ ์๊ฐํ๊ฑฐ๋, ์ค์ ๋ก ๊ตฌํํ ๋ ๊ทธ์ ๋๋ก ์ ์ฉํ์ง ์๋ค๊ณ ๋๊ผ๋ค๋ฉด, ๊ทธ๊ฒ์ MVVM ๋์์ธ ํจํด์ด SwiftUI์ ํน์ง๊ณผ ๋ง์ง ์๊ธฐ ๋๋ฌธ ์ด ์๋๊น์ ?
์ด์ ์ ์ (๊ธ์ด์ด)๋ ์๋์ ๊ฐ์ ๋ด์ฉ์ ํธ์ํฐ์ ๊ฒ์ํ์ต๋๋ค.
๋ชจ๋ฐ์ผ ์ดํ ๊ฐ๋ฐ์์ MVVM์์์ ViewModel์ ์๊ฐํ๋ ๊ฒ์ด ์ฃผ๋ฅ์ด๋ ์์ ์ด ์์๋ค. SwiftUI ๋ฑ์ ์ ์ธ์ UI๋ฅผ ์ฌ์ฉํ๋ ์ธ๊ณ์์๋ ์๊ฐํ๋ ๋ฒ์ ๋ฐ๊ฟ ํ์๊ฐ ์๋ค. ์๋ ViewModel์ด๋ผ๋ ๊ฒ์ ์ํ๋ฅผ View์ Bindingํด์ Reactive๋ก ๋ฐ์ํ๋ ๊ฒ์ด ๋ชฉ์ ์ผ๋ก ๋์ ๋์์ง๋ง, ์ ์ธ์ UI์ ๊ทธ ๊ธฐ๋ฅ์ด ๋ดํฌ๋์ด๋ฒ๋ ธ๊ธฐ์ ViewModel์ ๋ถํ์ํ๋ค.
Apple์ ๊ฐ๋ฐ์ ํฌ๋ผ์ด๋ ๋ ๋ง, ์ ํ๋ธ์์๋ "iOS๊ฐ๋ฐ(SwiftUI)์์ MVVM์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ฉ์ถ์" ์ ๋น์ทํ ์ฃผ์ ์ ๊ธ๋ค์ด ๋์ ๋๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
Stop using MVVM for SwiftUI https://developer.apple.com/forums/thread/699003
Is MVVM an anti-pattern in SwiftUI? https://www.reddit.com/r/swift/comments/m60pv7/is_mvvm_an_antipattern_in_swiftui/
STOP using MVVM for SwiftUI | Clean iOS Architecture https://www.youtube.com/watch?v=SOA0IT7sxvc
SwiftUI์์ MVVM์ ์ฌ์ฉํ๋ ๊ฒ์, "ํธ๋ฒ๋ณด๋๋ก ํ๋์ ๋ ์ ์๋๋ฐ๋, ์ผ๋ถ๋ฌ ๋ฐํด๋ฅผ ๋ฌ์์ ๋ฌ๋ฆฌ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ฒ์ด๋ค." ๋ผ๋ ๋น์๋ฅ๊ฑฐ๋ฆฌ๋ ๊ธ๋ ์์ต๋๋ค.
์ต๊ทผ, MVVM๋ก ๊ฐ๋ฐ์์ SwiftUI๋ฅผ ์ฌ์ฉํ๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค.
SwiftUI๋ฅผ ์ฌ์ฉํ ์ดํ๋ฆฌ์ผ์ด์ ์์, ์ํคํ ์ฒ๋ฅผ ์ด๋ป๊ฒ ํ ๊ฒ์ธ๊ฐ ํ๊ณ ๊ณ ๋ฏผํ๊ณ ์์ต๋๋ค. ๋ชจ๋ฐ์ผ ์ดํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ํ๋ค๋ณด๋ฉด, ์์ธ์ง ์ํคํ ์ฒ๋ MVVM๋ก ์ ํํด๋ฒ๋ฆฌ๊ณ ๋ง๋๋ค.
์ (๊ธ์ด์ด)๋ "์ด์งธ์?" ๋ผ๊ณ ์๋ฌธ์ ๊ฐ์ง๋๋ค.
์ด๊ฒ์ ๋ชจ๋ฐ์ผ ์ดํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์๋ ๋ถํฐ์ ์ ํต์ผ๋ก, 2016๋ ์ ๊ตฌ๊ธ์ด ์๋๋ก์ด๋ ๊ฐ๋ฐ์์ MVVM์ ์ ์ฐฝํ๋ ๊ฒ์ผ๋ก ์์๋๊ณ ์์ต๋๋ค. (์ด๋๊น์ง๋ ์ ์ฐฝํ ๊ฒ๋ฟ, ๋ฐ์์ ์์์ ๋ง์ดํฌ๋ก์ํํธ์ ๋๋ค)
์๋๋ ์๋๋ก์ด๋ ๊ฐ๋ฐ ์ค๋ฌด์์๋ถํฐ MVVM์ ์ฌ์ฉ์ด ์์๋์์ง๋ง, iOS ๊ฐ๋ฐ์๊น์ง ์ํฅ์ ๋ฐ๊ฒ ๋์ด์ ๋น์ฐํ ๊ฒ์ฒ๋ผ ํ์ฌ ์ฌ์ฉ์ด ํ์ฐ๋์ด ๋ฒ๋ ธ์ต๋๋ค.
ํ์ง๋ง, "๊ตฌ๊ธ์ด ๊ถ์ฅํ ์ํคํ ์ฒ๋ MVVM์ด๋ค" ๋ผ๋ ๋ง์ ์ค์ ๋ก ์กด์ฌํ์ง ์์ต๋๋ค. (๊ธ์ด์ด๊ฐ ์ฐพ์ ํ์๋)
ํ์ฌ, ๊ตฌ๊ธ ๊ณต์ ์์ผํฑ์ฒ ๊ฐ์ด๋์์ MVVM์ด๋ผ๋ ๋จ์ด๋ ์ง์์ ธ ์๋ ์ํ์ ๋๋ค.
- ViewModel ์ด๋ผ๋ ๋จ์ด๊ฐ ๋ฏธ๋ฌํ๊ฒ ๋จ์์์ง๋ง, ๊ธ์ด์ด๋ ์ ํํ ์๋ฏธ๋ฅผ ํ์ ํ์ง ๋ชปํ๊ณ ์์ต๋๋ค. https://developer.android.com/jetpack/guide#overview
ํ์ง๋ง MVVM์ ๋ฐ์์ ๋ง์ดํฌ๋ก์ํํธ, (๋ชจ๋ฐ์ผ์์๋ ์๋๋ก์ด๋) ์ ์ด์ ๋ณธ๊ฐ Apple์์๋ MVVM์ด๋ผ๋ ๋จ์ด๋ฅผ ์ฌ์ฉํ์ ์กฐ์ฐจ ์์ต๋๋ค.
๊ตฌ๊ธ๋ "์ต์ข ์ ์ผ๋ก Jetpack Compose์์๋ MVVM/ViewModel์ ์์ ๊ณ ์ถ๋ค." ๋ผ๋ ์ดํฌ๊ฐ ๋๊ปด์ง๊ธฐ ๋๋ฌธ์, ์๋๋ก์ด๋ ๊ฐ๋ฐ์์๋ MVVM์ด๋ผ๋ ๋จ์ด๊ฐ ์ฌ๋ผ์ ธ๊ฐ๋ ๋ถ์๊ธฐ๊ฐ ๋๊ปด์ง๊ณ ์์ต๋๋ค. (* ๊ธ์ด์ด์ ๊ฐ์ธ์ ์ธ ์๊ฐ์ ๋๋ค.)
์ ์ด์ iOS ๊ฐ๋ฐ์์ MVVM์, ์์ง SwiftUI๊ฐ ์๋ ์๋์ RxSwift ๋ฑ์ Reactive library๋ฅผ ์ฌ์ฉํด์, View์ ViewModel์ ๊ฐ์ ๋ฐ์ธ๋ฉ ํ๋ ์๋์ ์ฌ์ฉ๋๋ ๊ฒ์ ๋๋ค.
View <-> ViewModel <-> Model
Binding
class ViewController {
override func viewDidLoad() {
viewModel.username
.bind(to: label.rx.text)
.disposed(by: disposeBag)
}
}
ํ์ง๋ง, SwiftUI์ View์๋ ์๋๋ถํฐ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ๊ธฐ๋ฅ์ด ํฌํจ๋์ด ์์ต๋๋ค. ๋ฐ๊ฟ๋งํ๋ฉด, "UIKit.View MVVM" ์ผ๋ก ํ๋ ๊ฒ์ด "SwiftUI.View ํ๋"๋ก ๋๋ค, ๊ฐ ๋ฉ๋๋ค.
์ฆ, SwiftUI.View๋ผ๋ ๊ฒ์ ์ด๋ฏธ "View + ViewModel"์ ๊ธฐ๋ฅ์ ๊ฐ๊ณ ์์ด์, ์ง์ Model์ ๊ฐ์ Reactiveํ๊ฒ View์ ๋ฐ์ํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค๋ ๊ฒ ์ ๋๋ค.
SwiftUI.View <-> Model
Binding
SwiftUI์์ ViewModel์ ๋ฐ์ดํฐ๋ฐ์ธ๋ฉ์ด ํฌํจ๋์ด๋ฒ๋ฆฐ ์์ ์์, ViewModel์ ์กด์ฌ์ด์ ๋ฅผ ์๊ณ , 'ViewModel'์ด๋ผ๋ ๋จ์ด ์์ฒด๋ ์๋ฏธ๊ฐ ์ ๋งคํด์ ธ๋ฒ๋ฆฐ ๋จ์ด๊ฐ ๋์ด๋ฒ๋ ธ์ต๋๋ค.
SwiftUI์์ MVVM์ ์ฌ์ฉํด๋ฒ๋ฆฌ๋ฉด ViewModel์ด๋ผ๋ ๋ถํ์ํ ๋ ์ด์ด(๊ณ์ธต)์ ๋ผ์๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋ณต์กํด์ง๋๋ค.
View์ Model์ ์ฌ์ด์ ViewModel์ด๋ผ๋ ์ค๊ฐ ๋ ์ด์ด(๊ณ์ธต)๊ฐ ๋ผ์์ง ์๋ฐฉํฅ Data flow๊ฐ ๋์ด๋ฒ๋ฆฝ๋๋ค.
Apple ๊ณต์์์๋ ๋ณด์ฌ์ฃผ๋ ๋จ๋ฐฉํฅ Data flow๊ณผ๋ ๊ดด๋ฆฌ๊ฐ ์๊ฒจ๋ฒ๋ฆฝ๋๋ค.
https://developer.apple.com/documentation/swiftui/state-and-data-flow
์ ์ธ์ UI ์๋์์๋ ๋ฐ์ดํฐํ๋ก์ฐ๋ฅผ ๋จ๋ฐฉํฅ์ผ๋ก ํ๋ ๊ฒ์ด ์ฅ์ ์ด ์๋ค๊ณ ์๊ฐํจ๋๋ค. Single Source of Truth์ผ๋ก ํ๊ธฐ ์ํ ์ํ๊ด๋ฆฌ๋ฅผ ํ๋ ์ฅ์๋ Property Wrapper๋ฅผ ํ์ฉํด์ ๋ฐ์ดํฐํ๋ก์ฐ๋ฅผ ๊ณ ๋ฏผํ ํ์๊ฐ ์์ต๋๋ค.
@d_date ์ ํธ์ โใSwiftUI์์MVVM๋ฅผ ์ฌ์ฉํ๋๊ฑธ ๋ฉ์ถ์.ใ โญ๏ธใ์ ์ธ์ UI์ Property Wrapper์ ์ญํ ์ ์ดํดํด์ ๋ฐ์ดํฐํ๋ก์ฐ๋ฅผ ์ด๋ป๊ฒํ ๊น ๊ณ ๋ฏผํด๋ณด์ใ https://twitter.com/d_date/status/1502863294654464007?s=20&t=B9mI0uTKDSgMvEUuQyVBGg
SwiftUI.View <-> ViewModel <-> Model
Binding
MVVM on MVVM
"SwiftUI์์ MVVM๋ฅผ ์ฌ์ฉํด๋ฒ๋ฆฌ๋ ๊ฒ" ์ ๋ฐ๊ฟ๋งํ๋ฉด "SwiftUI + MVVM = MVVM on MVVM", ๋๊ฐ์๊ฑธ ๋ ๋ฒ ๋งํ๋ ๊ฒ์ด ๋์ด๋ฒ๋ฆฐ๋ค.
"MVVM ๋๋ถ์ MVVM์ด ๋์ด์ ๋คํ์ด๋ค"๋ผ๋ ์ํ๊ฐ ๋์ด์์ต๋๋ค.
์ด๊ฒ์ "๋์์ธ ํจํด์ ์์ฑํ๊ธฐ ์ํด์, ๋์์ธ ํจํด์ ๋ง๋๋ ๊ฒ"์ด ๋์ด์, ๋ชฉ์ ์ ์์๊ฒ ์๋๊น์? "MVVM on MVVM"์ด ๋์ง์๊ธฐ ์ํด์๋ ViewModel์ ๋ ์ด์ด(๊ณ์ธต)์ ์์ ๋ ํธ์ด ๋จ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋น์ทํ ์ ์ธ์ UI๋ฅผ ์ฌ์ฉํ๊ณ ์๋ React/Vue/Flutter์ ๋ถ์ผ์์๋, MVVM์ด๋ผ๋ ์ํคํ ์ฒ๋ ์ฌ์ฉ๋๊ณ ์์ง ์๊ณ (์ ์ธ์ UI์ ํฌํจ๋์ด ์์), SwiftUI์ ์์ด์๋ง MVVM๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ด์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
(from. @young | ๊ธ์ด์ด์ ์ฃผ๊ด์ ์ธ ์๊ฐ์ด ๊น์ ๋ฌธ๋จ)
- Flutter์์ MVVM๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์์ง๋ง, SwiftUI์์ ์ฌ์ฉํ๋ MVVM๊ณผ๋ ๋ค๋ฅด๋ค๊ณ ์๊ฐํฉ๋๋ค. Provider๋ผ๊ณ ํ๋ ์ํคํ ์ณ์ ํ์์ "Provider์์ ์ฎ๊ธฐ๊ณ ์๋ Notifier์ ์ด๋ฆ"์ ViewModel์ด๋ผ๋ ๊ฒ์ผ๋ก ์ด๋ฆ์ ๋ถ์๋ค๋ ์ธ์์ ๋๋ค. ์ํคํ ์ฒ๋ผ๋ ๊ฒ์ Provider์ด๋ฏ๋ก ์ (๊ธ์ด์ด)์ ์๊ฐ์ผ๋ก๋ MVVM๋ผ๊ณ ๋ ์๊ฐํ์ง ์์ต๋๋ค. Android๊ฐ๋ฐ์์์ MVVM์ ์ฐ์ฅ์ ์ผ๋ก Flutter์์๋ ViewModel๋ฅผ ๋ง๋๋ ๊ฒ์ ๊ฐ์ธ์ ์ผ๋ก๋ ์ข์ง๋ ์์ ํ๋จ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
"ViewModel" ์ด๋ผ๋ ๋จ์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ชจ๋ฐ์ผ ์ธ์์ ๋ถ๋ค์ด ๋๋ถ๋ถ์ด๋ฏ๋ก, ๋ค๋ฅธ ๋ถ์ผ์ ๊ฐ๋ฐ์๊ฐ ์ด์ผ๊ธฐํ๋ ViewModel์ด๋ผ๋ ๋จ์ด์ ์๋ฏธ๋ ๋ ๋ค๋ฅด๋ค๊ณ ๋๊ปด์ง ๋๋ ์์ต๋๋ค.
iOS์ ์์ค์ฝ๋๋ฅผ React ๊ฐ๋ฐ์์๊ฒ ๋ณด์ฌ์คฌ์ ๋, "์ด ViewModel์ด๋ผ๋ ๊ฒ์ ๋ฌด์จ ์๋ฏธ๊ฐ ์๋๊ฑด๊ฐ์?"๋ผ๋ ์ง๋ฌธ์ ๋ฐ์์ ๋ ์์ต๋๋ค.
MVVM์ ์ฌ์ฉํ์ง ์์์ ๋, "ViewModel์ด ์์ด์ง๋ฉด UI์ ๋ก์ง์ ๋ถ๋ฆฌ๋ ์ด๋ป๊ฒํ์ง?"๋ผ๋ ๋ฌธ์ ๊ฐ ์๊น๋๋ค.
๋ต์ Model ๋๋ Flux์ ์ธ Store๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ค์๊ท๋ชจ์ ์ดํ์ด๋ผ๋ฉด MV(Model๊ณผ View)๋ผ๋ฉด ์ถฉ๋ถํฉ๋๋ค.
๋จ์ํ View์ ๋ก์ง์ ๋ถ๋ฆฌํ๊ธฐ ์ํด์ ViewModel์ ๋ง๋๋ ๊ฒ์, ์ด๋ฆ๊ณผ ํด์ผํ๋ ์ผ์ด ๋ง์ง ์๊ธฐ ๋๋ฌธ์ ๋ ์ด์ ํ์ํ์ง ์๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ ์ธ์ UI์ ๋ฑ์ฅ์ผ๋ก, "๋ชจ๋ฐ์ผ ๊ฐ๋ฐ์ด๋ผ๋ฉด MVVM์ด์ง" ๋ผ๋ ์๋๋ ์ ๋ฌผ๊ณ , ํผ๋์ ์๋์ ๋์ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
ํ์ฌ, SwiftUI ๊ฐ๋ฐ์์ ๊ฐ์ฅ ํ์ค์ ์ธ ์ํคํ ์ณ๋ Best practice๋ผ๊ณ ๋ถ๋ ค์ค๋ ๊ฒ์ ์์ง ์์ต๋๋ค. ๋ชจ๋๊ฐ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ต๋๋ค. (ํน์ ์๋ค๋ฉด ๊ผญ ์๋ ค์ฃผ์ธ์ !)
์ ์ธ์ UI ์๋์ ๋๊ท๋ชจ ์ดํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์์ ์ฐ๋ฆฌ๊ฐ ์ ๋ง๋ก ์ํ๋ ๊ฒ์, MVI(๋จ๋ฐฉํฅ ๋ฐ์ดํฐํ๋ก์ฐ)๋ Flux(The composable architecture), Store/Provider ํจํด ๋ฑ์ด ์๋๊น ์๊ฐํฉ๋๋ค.
SwiftUI๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ง์ผ๋ก MVVM ์ ๋์ ๊ธฐ๋ฅ์ ๊ธฐ๋ณธ์ผ๋ก ๊ฐ๋ฅํ๊ฒ ๋์์ผ๋ฏ๋ก, ์ด์ ๋ ํ ๊ณ๋จ ์ ๋ ์ด์ด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ํคํ ์ฒ๊ฐ ํ์ํ ์๊ธฐ๊ฐ ๋์๋ค๊ณ ๋งํ ์ ์์ต๋๋ค. (from. @young | ์ ์ธ์ UI, SwiftUI์ ์ํด ๋ฐ์ํ๊ฒ ๋ ์ด์๋ค์ ์ด์ ๊ฐ๋ฐ์๋ค์ด ์ด๊ฒจ๋ด์ผํ ํ์ด๋ฐ์ด ์๋ค๊ณ ์ด์ผ๊ธฐํ๋ค์.)
๊ทธ ๋ฌธ์ ๋ฅผ ์๋ก๋ค๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
- "Component ์ค๊ณ๋ ์ด๋ป๊ฒํ ๊น?"
- "Component์ ์ํ์ ๋ก์ง์ View๋ก๋ถํฐ ๋ถ๋ฆฌํ๋ ๋ฒ"
- "๊ฐ Component๋ค๊ฐ์ ์ํ ๊ด๋ฆฌ๋ฅผ ์ ๋ฌํ๋ ๋ฒ"
- "๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ก์ฐ"
- ๋ฑ๋ฑ
MVVM ์ํคํ ์ฒ๋ Component ๊ฐ์ ์ฐ๊ฒฐ, ๋ฐ์ดํฐ ํ๋ก์ฐ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๊ฒ์ด ์๋๋๋ค.
์์ ๊ฐ์ ๋ฌธ์ ์ ํด๊ฒฐ๋ฐฉ๋ฒ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ด์ผ๊ธฐํ๋ค๋ฉด, React๋ผ๋ฉด Flux(Redux)๋ Hooks๊ฐ ๋๊ณ , Flutter๋ผ๋ฉด Providerํจํด์ด๋ Riverpod๊ฐ ๋ ์ ์์ต๋๋ค.
iOS ๊ฐ๋ฐ์์๋ ์ ์ธ์ UI๋ก๋ถํฐ ์ผ๊ธฐ๋๋, MVVM์ ์์ ๋ ์ด์ด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ํคํ ์ฒ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐ๋ฉ๋๋ค.
์ ์ด์ ์ํคํ ์ฒ์ ๋ฌธ์ ๊ฐ ์๋, "์๋ฒ๋ก๋ถํฐ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ fetchํด์, View์ ํ์ํ๊ณ ์ถ๋ค"๋ผ๋ ๋น๋๊ธฐ ๋๋ ์ํ๊ด๋ฆฌ์ ํด๊ฒฐ๋ฐฉ๋ฒ์ GraphQL ํด๋ผ์ด์ธํธ์ data fetch cache libarary๋ฅผ ์ฌ์ฉํ๋ฉด ํด๊ฒฐ๋ ์ ์์์์ง๋ ๋ชจ๋ฆ ๋๋ค.
ViewModel์ ์กด์ฌ๋ ์ ์ธ์ UI ์๋์ ํ์์๋ค๊ณ ์๊ฐ๋ฉ๋๋ค.
View์์ ๋ก์ง๊ณผ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ๋ถ๋ฆฌ์ํค๊ธฐ ์ํ ์ฅ์๋ก ViewModel์ด๋ผ๋ ๋จ์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ทธ๋งํ์ผ๋ฉด ํฉ๋๋ค.
์ด๊ฒ์ "๋๋ ์ด๋ ๊ฒ ์๊ฐํฉ๋๋ค"๋ผ๋ ์๋ฏธ์ด๊ณ , "์ด ์๊ฐ์ ๋ฌด์กฐ๊ฑด ์ณ๋ค"๋ผ๋ ์ฃผ์ฅ์ ์๋๋๋ค. ํ์์ ๋ณด๋ ์ฝ๋๋ฒ ์ด์ค๋ ๋ฐฑ๊ทธ๋ผ์ด๋, ์๊ฐํ๋ ๋ฐฉ๋ฒ, ์์ ๋ฑ์ ์ํด ๊ทธ ์ ํ์ด ์ณ์์ง ์๋์ง๋ ์ฌ๋๋ง๋ค ๋ค๋ฅด๊ธฐ ๋๋ฌธ์.
"๊ทธ๋ฌ๋ฉด, MVVM์ ์ฌ์ฉํ์ง ์๊ณ , @@์ํคํ ์ฒ๋ฅผ ์ฌ์ฉํ์"๋ผ๋ ๋ฅ์ ์ด๋ ํ ํน์ ์ํคํ ์ฒ๋ฅผ ์ถ์ฒํ๋ ๊ฒ์ด ์ด ๊ธ์ ๋ชฉ์ ์ ์๋๋๋ค.
TCA(The Composable Architecture)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ์ฌ์ค ๊ฒํ ํ์์ง๋ง, ๋๋ฌด ๊ณผ์ฅํ๋๊ฒ ์๋๊น ์ฐ๋ ค๋๊ธฐ๋ํฉ๋๋ค. (* ๋ด์ฉ์ถ๊ฐ: ๋ค์ ๊ฒํ ํด๋ณธ ๊ฒฐ๊ณผ, TCA๋ ์๊ธฐ์์กฐ๋ผ๋ ์๊ฒฌ์ด ๋ง์ง๋ง, ์ ๋ ํ ๋ฒ ์ฌ์ฉํ๊ณ ์ถ๋ค๊ณ ์๊ฐํฉ๋๋ค.)
The Composable Architecture https://github.com/pointfreeco/swift-composable-architecture
๊ฐ ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ ์ํคํ ์ฒ๋ฅผ "์ ๊ณ ๋ฏผํด์" ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค. ํน์๋ผ๋ ๊ณ ๋ฏผํ ๊ฒฐ๊ณผ๊ฐ "์ญ์ MVVM์ ์ฌ์ฉํด์ผ๊ฒ ๋ค."์ฌ๋, ๊ทธ๊ฒ์ ๊ทธ๊ฒ์ผ๋ก๋ ๊ฝค ์ข๋ค๊ณ ์๊ฐํ๊ณ , ๊ทธ ์๊ฐ์ ๋ถ์ ํ ์๊ฐ์ ์ผ๋ ์์ต๋๋ค.
์ด๋๊น์ง๋ ์ (๊ธ์ด์ด) ๊ฐ์ธ์ด SwiftUI๋ฅผ ์ฌ์ฉํ๋๋ฐ ์์ด์, "MVVM ์ฌ์ฉ์ ๋ฉ์ถ์"๋ผ๊ณ ์๊ฐ์ ์ด๋ฅด๋ ๊ธฐ ๋๋ฌธ์, ๊ทธ ์ด์ ์ ๋ํด์ ์์ฑํ ๊ธ์ด๋ฏ๋ก MVVM์ ๋์คํ ์๊ฐ๋ ์๊ณ , ํ์ฌ์ "๋น์ฐํ MVVM์ ์ฌ์ฉํ๋ค"๋ผ๋ ํ๋ฆ์ ์ข์ง ์๋ค๊ณ ์๊ฐํ๊ธฐ์ ์ด ๊ธ์ ์์ฑํ์ต๋๋ค.
๋ง์ง๋ง๊น์ง ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ํน์ ์๊ฒฌ์ด ์๋ค๋ฉด ๋๊ธ๋ก ์์ฑํด์ฃผ์ ๋ค๋ฉด ๊ฐ์ฌํฉ๋๋ค.
์คํด๋ฅผ ์ฐ ๋ด์ฉ์ด ์๋ค๊ณ ์๊ฐ๋์ด ์ถ๊ฐ๋ก ์์ฑํฉ๋๋ค.
์คํด๋ฅผ ์๋ค๊ณ ์๊ฐ๋ ํธ์ ๋ด์ฉ
- "ViewModel = ObservableObject"
- "๊ทธ๋ฌ๋ฉด ObservableObject์ ์ฌ์ฉํ์ง๋ง๋ผ๋ ๊ฑด๊ฐ?"
- "@State๋ง ์ฌ์ฉํ๋ผ๋๊ฑด๊ฐ?"
"SwiftUI์ ObservableObject๋ ViewModel์ผ"๋ผ๊ณ ํ๋ ๊ฒ์ ๋ง๋ ๋ง์ด๊ธฐ๋ ํ์ง๋ง ์๋๊ธฐ๋ ํฉ๋๋ค.
"ObservableObject๋ ViewModel์ ๋๋ค"๋ผ๋ ๋ง์ ๋์ฒด ๋๊ฐ ์ ํ๊ฑธ๊น์?
ObservableObject๋ ๋จ์ํ Reactํ Object๋ก ViewModel๋ ์๋๋ผ๊ณ ์๊ฐํ ์ ์์ต๋๋ค.
ObservableObject๋ Model/StateHolder๋ก ์๊ฐํ๋๊ฒ ์์ฐ์ค๋ฝ์ง ์์๊น์?
ํ์ง๋ง SwiftUI.ObservableObject๋ SwiftUI์ ์ผ๋ถ๋ก ์ฌ์ฉํ๋ฉด ์ฌ์ฉํ ์๋ก ์ข๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ด ์ํฐํด์ "ObservableObject๋ฅผ ์ฌ์ฉํ๋๊ฑธ ๋ฉ์ถ์"๋ผ๋ ์๋๊ฐ ์๋๋๋ค.
์ดํดํด์ฃผ์๊ธธ ๋ฐ๋๋๋ค.
์ฐธ๊ณ ๋ก, ๊ทธ ObservableObject๋ฅผ ViewModel๋ผ๊ณ ๋ช ๋ช ํ๋ ๊ฒ์ ๋ํด์๋ Model๋ช /Store๋ช /Domain๋ช ๋ฑ์ ๊ทธ๋๋ก ์ด๋ฆ์ ๋ถ์ด๋๊ฒ ์ข์ง ์์๊น ์๊ฐํฉ๋๋ค. (ViewModel๋ ์ด์ด(๊ณ์ธต) ์์ ๊ธฐ)
from.@young ๊ธ์ด์ด๋ ObservableObject์ ์ด๋ฆ์ @@ViewModel๋ก ํ๋ ๊ฒ๋ณด๋ค, @@Model, @@Store ๋ฑ๊ณผ ๊ฐ์ด ํ๋ ์ญํ ์ ๋ฐ๋ผ ๋ช ๋ช ํ๋ ๊ฒ์ ์ถ์ฒํ๋ค์ !
์๋ ํ์ธ์ ์๋.!
์ ๋ ํ๋ก์ ํธ๋ฅผ SwiftUI + MVVM ํจํด์ ์ฌ์ฉํด์ ๊ฐ๋ฐํด์ค๋ฉด์ ๋ถํ์ํ๊ฒ ViewModel์ธต์ด ์ปค์ง๋ ๊ฒฝํ๊ณผ
SwiftUI์ ์ ๋ง ์ข์ property wrapper๋ค (@FetchRequest, @AppStorage ๊ฐ์..)์ด ์กด์ฌํ๋๋ฐ ViewModel์์๋ ์ฌ์ฉํ๊ธฐ๊ฐ ์ ๋งคํ ๊ฒฝ์ฐ๊ฐ ๋ง์์ต๋๋ค.
์ ๋ Stop using MVVM for SwiftUI https://developer.apple.com/forums/thread/699003 ๋ผ๋ ๊ธ์ ์ฐ์ฐ์ฐฎ๊ฒ ์ ํ๋๋ฐ,
์ ๊ฐ MVVM์ ์ฌ์ฉํ๋ฉฐ ๊ฒฝํํ๋ฉฐ ๋ค์๋ ์๋ฌธ๋ค์ ์ ๋ต๋ณํด์ค ๊ฒ ๊ฐ์ ๊ธ์ ๋๋ค. ์๋์ ๊ธ์ ๋ณด๋ฉด์๋ MVVM์ ๋จ๋ฐํ๋ ์ ์ ๋ชจ์ต์ด ๋ณด์ ๋๋ค..ใ
์ข์ ๊ธ ๊ฐ์ฌํฉ๋๋ค.