Skip to content

Instantly share code, notes, and snippets.

@unnnyong
Last active June 18, 2024 06:27
Show Gist options
  • Save unnnyong/439555659aa04bbbf78b2fcae9de7661 to your computer and use it in GitHub Desktop.
Save unnnyong/439555659aa04bbbf78b2fcae9de7661 to your computer and use it in GitHub Desktop.

์ผ๋ณธ์–ด ๋ธ”๋กœ๊ทธ ์•„ํ‹ฐํด์˜ @young์˜ ๋ฒˆ์—ญ๊ธ€ ์ž…๋‹ˆ๋‹ค. ์˜์—ญ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


"SwiftUI์—์„œ MVVM ์‚ฌ์šฉ์„ ๋ฉˆ์ถ”์ž"๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค์—ˆ๋˜ ์ด์œ 

์„ ์–ธ์ ์ธ UI์—์„œ, MVVM์ด ๋ถˆํ•„์š”ํ•œ ์ด์œ ๋Š”?

iOS ๊ฐœ๋ฐœ ํ˜„์žฅ์—์„œ, ์„ ์–ธ์  UI๊ฐ€ ๋‹น์—ฐํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ์‹œ๋Œ€๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. SwiftUI๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์€ ๋ฉ‹์กŒ์Šต๋‹ˆ๋‹ค. ์ตœ๊ณ ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ตœ๊ทผ, SwiftUI์—์„œ ๋‹น์—ฐํ•˜๊ฒŒ "MVVM์„ ์‚ฌ์šฉํ•˜์ž" ๋ผ๊ณ  ๋˜์—ˆ์„ ๋•Œ, "์ •๋ง ๊ทธ๊ฑธ๋กœ ๊ดœ์ฐฎ์„๊นŒ?" ๋ผ๋Š” ์˜๋ฌธ์ด ๋“ค๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ œ ์ƒ๊ฐ์„ ๊นŠ์ด ํŒŒ๋ณด๋ฉด,

  • ์งˆ๋ฌธ
    • iOS๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ, ์„ ์–ธ์  UI์—์„œ MVVM์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ •๋ง๋กœ ๊ดœ์ฐฎ์€๊ฑธ๊นŒ?
  • ๊ฒฐ๋ก 
    • SwiftUI๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, MVVM์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ฉˆ์ถ”์ž
  • ์ด์œ 
    • ViewModel์˜ ์กด์žฌ๋Š” ์„ ์–ธ์  UI ์‹œ๋Œ€์—๋Š” ํ•„์š” ์—†๋‹ค ๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ

๋ผ๋Š” ๊ฒฐ๋ก ์— ๋‹ค๋‹ค๋ž๊ธฐ์— ์ƒ๊ฐํ•œ ๊ฒƒ์„ ์ด ์•„ํ‹ฐํด์— ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

ViewModel์€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.

iOS ๊ฐœ๋ฐœ ํ˜„์žฅ์—์„œ, ๊นŠ๊ฒŒ ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ํ”„๋กœ์ ํŠธ์˜ ์•„ํ‚คํ…์ณ๋กœ MVVM์„ ์„ ํƒํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‚˜์š”?

ํ˜น์‹œ๋ผ๋„ ๋‹น์‹ ์ด SwiftUI์—์„œ MVVM ํŒจํ„ด์ด ๋ฐฉํ•ด๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ฑฐ๋‚˜, ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•  ๋•Œ ๊ทธ์ •๋„๋กœ ์œ ์šฉํ•˜์ง€ ์•Š๋‹ค๊ณ  ๋Š๊ผˆ๋‹ค๋ฉด, ๊ทธ๊ฒƒ์€ MVVM ๋””์ž์ธ ํŒจํ„ด์ด SwiftUI์˜ ํŠน์ง•๊ณผ ๋งž์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ ์ด ์•„๋‹๊นŒ์š” ?

์ด์ „์— ์ €(๊ธ€์“ด์ด)๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋‚ด์šฉ์€ ํŠธ์œ„ํ„ฐ์— ๊ฒŒ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋ฐ”์ผ ์–ดํ”Œ ๊ฐœ๋ฐœ์—์„œ MVVM์—์„œ์˜ ViewModel์„ ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ด ์ฃผ๋ฅ˜์ด๋˜ ์‹œ์ ˆ์ด ์žˆ์—ˆ๋‹ค. SwiftUI ๋“ฑ์˜ ์„ ์–ธ์  UI๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์„ธ๊ณ„์—์„œ๋Š” ์ƒ๊ฐํ•˜๋Š” ๋ฒ•์„ ๋ฐ”๊ฟ€ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. ์›๋ž˜ ViewModel์ด๋ผ๋Š” ๊ฒƒ์€ ์ƒํƒœ๋ฅผ View์— Bindingํ•ด์„œ Reactive๋กœ ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์œผ๋กœ ๋„์ž…๋˜์—ˆ์ง€๋งŒ, ์„ ์–ธ์  UI์— ๊ทธ ๊ธฐ๋Šฅ์ด ๋‚ดํฌ๋˜์–ด๋ฒ„๋ ธ๊ธฐ์— ViewModel์€ ๋ถˆํ•„์š”ํ•˜๋‹ค.

MVVM๋ถˆํ•„์š”์ด๋ก  ์€ ํ•ด์™ธ์—์„œ๋„ ์ข…์ข… ๋…ผ๋ž€์ด ๋œ๋‹ค

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์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€, "ํ˜ธ๋ฒ„๋ณด๋“œ๋กœ ํ•˜๋Š˜์„ ๋‚  ์ˆ˜ ์žˆ๋Š”๋ฐ๋„, ์ผ๋ถ€๋Ÿฌ ๋ฐ”ํ€ด๋ฅผ ๋‹ฌ์•„์„œ ๋‹ฌ๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค." ๋ผ๋Š” ๋น„์•„๋ƒฅ๊ฑฐ๋ฆฌ๋Š” ๊ธ€๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

image

๊ทธ๋Ÿฌ๋ฉด ์™œ ์šฐ๋ฆฌ๋Š” 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์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ์‚ฌ๋ผ์ ธ๊ฐ€๋Š” ๋ถ„์œ„๊ธฐ๊ฐ€ ๋Š๊ปด์ง€๊ณ  ์ž‡์Šต๋‹ˆ๋‹ค. (* ๊ธ€์“ด์ด์˜ ๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค.)

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์— ViewModel์ •๋„์˜ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, SwiftUI์˜ View์—๋Š” ์›๋ž˜๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์˜ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๊ฟ”๋งํ•˜๋ฉด, "UIKit.View MVVM" ์œผ๋กœ ํ–ˆ๋˜ ๊ฒƒ์ด "SwiftUI.View ํ•˜๋‚˜"๋กœ ๋œ๋‹ค, ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, SwiftUI.View๋ผ๋Š” ๊ฒƒ์€ ์ด๋ฏธ "View + ViewModel"์˜ ๊ธฐ๋Šฅ์„ ๊ฐ–๊ณ  ์žˆ์–ด์„œ, ์ง์ ‘ Model์˜ ๊ฐ’์„ Reactiveํ•˜๊ฒŒ View์— ๋ฐ˜์˜ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

SwiftUI.View  <->  Model
            Binding

image

์„ ์–ธ์  UI์˜ ๋“ฑ์žฅ์œผ๋กœ, ViewModel์€ ์กด์žฌ์ด์œ ๋ฅผ ์žƒ๊ณ  ์žˆ๋‹ค.

SwiftUI์—์„œ ViewModel์˜ ๋ฐ์ดํ„ฐ๋ฐ”์ธ๋”ฉ์ด ํฌํ•จ๋˜์–ด๋ฒ„๋ฆฐ ์‹œ์ ์—์„œ, ViewModel์€ ์กด์žฌ์ด์œ ๋ฅผ ์žƒ๊ณ , 'ViewModel'์ด๋ผ๋Š” ๋‹จ์–ด ์ž์ฒด๋„ ์˜๋ฏธ๊ฐ€ ์• ๋งคํ•ด์ ธ๋ฒ„๋ฆฐ ๋‹จ์–ด๊ฐ€ ๋˜์–ด๋ฒ„๋ ธ์Šต๋‹ˆ๋‹ค.

MVVM์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋ถˆํ•„์š”ํ•œ ๋ณต์žก์„ฑ์„ ๋งŒ๋“ค์–ด๋ฒ„๋ฆฐ๋‹ค.

SwiftUI์—์„œ MVVM์„ ์‚ฌ์šฉํ•ด๋ฒ„๋ฆฌ๋ฉด ViewModel์ด๋ผ๋Š” ๋ถˆํ•„์š”ํ•œ ๋ ˆ์ด์–ด(๊ณ„์ธต)์„ ๋ผ์›Œ๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.

View์™€ Model์˜ ์‚ฌ์ด์— ViewModel์ด๋ผ๋Š” ์ค‘๊ฐ„ ๋ ˆ์ด์–ด(๊ณ„์ธต)๊ฐ€ ๋ผ์›Œ์ง„ ์–‘๋ฐฉํ–ฅ Data flow๊ฐ€ ๋˜์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

Apple ๊ณต์‹์—์„œ๋„ ๋ณด์—ฌ์ฃผ๋Š” ๋‹จ๋ฐฉํ–ฅ Data flow๊ณผ๋Š” ๊ดด๋ฆฌ๊ฐ€ ์ƒ๊ฒจ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.

image 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์˜ ๋ ˆ์ด์–ด(๊ณ„์ธต)์„ ์—†์• ๋Š” ํŽธ์ด ๋‹จ์ˆœํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

React/Vue/Flutter ๊ฐœ๋ฐœ์—์„œ MVVM๋Š” ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค.

๋น„์Šทํ•œ ์„ ์–ธ์  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์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ป๊ฒŒํ•˜๋‚˜์š”?

UI์™€ ๋กœ์ง์˜ ๋ถ„๋ฆฌ

MVVM์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ, "ViewModel์ด ์—†์–ด์ง€๋ฉด UI์™€ ๋กœ์ง์˜ ๋ถ„๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒํ•˜์ง€?"๋ผ๋Š” ๋ฌธ์ œ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

๋‹ต์€ Model ๋˜๋Š” Flux์ ์ธ Store๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ค‘์†Œ๊ทœ๋ชจ์˜ ์–ดํ”Œ์ด๋ผ๋ฉด MV(Model๊ณผ View)๋ผ๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ View์™€ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ViewModel์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์€, ์ด๋ฆ„๊ณผ ํ•ด์•ผํ•˜๋Š” ์ผ์ด ๋งž์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์„ ์–ธ์  UI ์‹œ๋Œ€์— ํ•„์š”ํ•œ ์•„ํ‚คํ…์ฒ˜

์„ ์–ธ์  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 ๋“ฑ๊ณผ ๊ฐ™์ด ํ•˜๋Š” ์—ญํ• ์— ๋”ฐ๋ผ ๋ช…๋ช…ํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•˜๋„ค์š” !

@luminouxx
Copy link

์˜ ์•ˆ๋…•ํ•˜์„ธ์š”
์ด๋ ‡๊ฒŒ ๋งŒ๋‚˜๊ฒŒ ๋˜๋‹ˆ ์ •๋ง ๋ฐ˜๊ฐ‘๋„ค์š”
์ €๋„ ๋น„์Šทํ•œ ๊ณ ๋ฏผ์„ ํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์ •๋ง ๋งŽ์€ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~!~!

@Mercen-Lee
Copy link

ViewModel์˜ ํ•„์š”์„ฑ์— ๋Œ€ํ•ด ๊ณ„์† ์ƒ๊ฐํ•ด์™”๋Š”๋ฐ ์ €์™€ ๊ฐ™์€ ์ƒ๊ฐ์„ ํ•˜์‹œ๋Š” ๋ถ„๋“ค์ด ๊ณ„์…จ๋„ค์š”.
์œ ์ตํ•œ ์ •๋ณด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@zoop1259
Copy link

MVVMํŒจํ„ด์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•˜๋‹ค ์ด ๊ธ€์„ ๋ณด๊ฒŒ ๋˜์—ˆ๋„ค์š”.
ํ›„์— ์Šค์œ ์—์„œ์˜ ํŒจํ„ด์ด ์–ด๋–ป๊ฒŒ ๋ ์ง€ ๊ถ๊ธˆํ•ด์ง‘๋‹ˆ๋‹ค.

@yongbeomkwak
Copy link

์˜ ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค! 2๊ธฐ ๋Ÿฌ๋„ˆ ์˜ค์ „ ์„ธ์…˜ Kayle ์ž…๋‹ˆ๋‹ค.์ข‹์€ ๊ธ€ ๊ณต์œ ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.
MVVM ์‹ ๋ด‰์ž๋กœ์จ (์‚ฌ์‹ค ์•„๋Š” ํŒจํ„ด์ด MVVM๋ฟ์ด๋ผ๋Š”๊ฑด ๋น„๋ฐ€)
์˜์ด ๋งํ•œ ๊นŠ๊ฒŒ ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ํ”„๋กœ์ ํŠธ์˜ ์•„ํ‚คํ…์ณ๋กœ MVVM์„ ์„ ํƒํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ์ด๋ผ ๋’ท๋ชฉ์„ ์žก์•˜๋„ค์š”!
์˜๊ณผ ๋‹ค์–‘ํ•œ ๋””์ž์ธํŒจํ„ด์— ๋Œ€ํ•œ ๋Œ€ํ™”์ฐฝ์ด ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š” :)

@earlysummer0303
Copy link

์ข‹์€ ์ž๋ฃŒ ์ž˜ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค :) ๊ฐ์‚ฌํ•ด์š” ์˜!

@Oreo-Mcflurry
Copy link

์ข‹์€ ์ž๋ฃŒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@saroby
Copy link

saroby commented May 2, 2024

์ž˜ ๋ณด๊ณ  ๊ฐ‘๋‹ˆ๋‹ค. ๊ณ ๋ฏผํ•  ๊ฑฐ๋ฆฌ๊ฐ€ ์ƒ๊ฒผ๋„ค์š”

@seo-kh
Copy link

seo-kh commented Jun 18, 2024

์•ˆ๋…•ํ•˜์„ธ์š” ์˜๋‹˜.!

์ €๋„ ํ”„๋กœ์ ํŠธ๋ฅผ SwiftUI + MVVM ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์„œ ๊ฐœ๋ฐœํ•ด์˜ค๋ฉด์„œ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ViewModel์ธต์ด ์ปค์ง€๋Š” ๊ฒฝํ—˜๊ณผ
SwiftUI์˜ ์ •๋ง ์ข‹์€ property wrapper๋“ค (@FetchRequest, @AppStorage ๊ฐ™์€..)์ด ์กด์žฌํ•˜๋Š”๋ฐ ViewModel์—์„œ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ๊ฐ€ ์• ๋งคํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค.

์ €๋Š” Stop using MVVM for SwiftUI https://developer.apple.com/forums/thread/699003 ๋ผ๋Š” ๊ธ€์„ ์šฐ์—ฐ์ฐฎ๊ฒŒ ์ ‘ํ–ˆ๋Š”๋ฐ,
์ œ๊ฐ€ MVVM์„ ์‚ฌ์šฉํ•˜๋ฉฐ ๊ฒฝํ—˜ํ•˜๋ฉฐ ๋“ค์—ˆ๋˜ ์˜๋ฌธ๋“ค์„ ์ž˜ ๋‹ต๋ณ€ํ•ด์ค€ ๊ฒƒ ๊ฐ™์€ ๊ธ€์ž…๋‹ˆ๋‹ค. ์˜๋‹˜์˜ ๊ธ€์„ ๋ณด๋ฉด์„œ๋„ MVVM์„ ๋‚จ๋ฐœํ•˜๋˜ ์ €์˜ ๋ชจ์Šต์ด ๋ณด์ž…๋‹ˆ๋‹ค..ใ…Ž

์ข‹์€ ๊ธ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment