Potato
์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐ์žก๋‹ˆ๋‹ค?๐Ÿฅ” ^___^ ๐Ÿ˜บ github ๋ฐ”๋กœ๊ฐ€๊ธฐ ๐Ÿ‘‰๐Ÿป

Computer/Design Pattern

[๋””์ž์ธ ํŒจํ„ด] (iOS) MVC ํŒจํ„ด์„ MVVMํŒจํ„ด์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๊ธฐ (feat. Combine)

๊ฐ์ž ๐Ÿฅ” 2023. 2. 7. 18:30
๋ฐ˜์‘ํ˜•

 

์ž, ์ด์ œ ์ •๋ง ๊ท€์ฐข์–ด์ง€๊ฒŒ ๋“ค์—ˆ๋˜ MVC์™€ MVVM ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ํฌ์ŠคํŒ…ํ•ด๋ณด์ž.
์ฃผ์ œ๋Š”, MVC ํŒจํ„ด์„ MVVMํŒจํ„ด์œผ๋กœ ๋ฐ”๊พธ๊ธฐ!!~ ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ฐจ์ด์ ์— ๋Œ€ํ•ด์„œ ์ง์ ‘ ๋Š๊ปด๋ณด์ž

๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ๋‹˜์˜ ์œ ํŠœ๋ธŒ ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด์„œ ํฌ์ŠคํŒ… ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
(https://www.youtube.com/watch?v=TLX7MjtOPd0&t=873)

 

MVC์™€ MVVM์ด ๋ญ”์ง€ ๋จผ์ € ์‚ดํŽด๋ณด๊ณ  ๋“œ๋ฃจ๊ฐ€์ž

 

โšซ๏ธ MVC์ด ๋ญ”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•ด๋ณด์ž.

Model, View, Controller๋กœ ๊ตฌ์„ฑ๋œ ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค. ๋ทฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ จํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ด€๋ จํ•œ ๊ฒƒ๋“ค์€ Controller๊ฐ€ ๋งก๋Š”๋‹ค. ๊ฐ ํ”„๋กœ์„ธ์Šค๋ณ„๋กœ ๊ตฌ๋ณ„๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ๊ฐ์˜ ๊ตฌ์„ฑ ์š”์†Œ์—๋งŒ ์ง‘์ค‘ํ•ด์„œ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ์žฅ์ ์ด๋‹ค. Controller์—์„œ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ๋“ค์„ ๊ฐฑ์‹ ํ•ด์„œ View๋กœ ๋ณด์—ฌ์ค€๋‹ค. View์—์„œ๋Š” ๋ทฐ๋ฅผ ๋ณด์—ฌ์ค„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์œ ์ €์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„๋“ค์ด๊ณ , ์ด ๋ฐ›์•„๋“ค์—ฌ์ง„ ์ด๋ฒคํŠธ๋Š” ๋˜ controller๊ฐ€ ์ฒ˜๋ฆฌํ•œ๋‹ค. Model์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ์ธ DB, ์ƒ์ˆ˜, ๋ณ€์ˆ˜๋“ฑ์„ ๋œปํ•œ๋‹ค. ์ด ๊ฐ’๋“ค์˜ ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธฐ๋ฉด, controller์—๊ฒŒ ์•Œ๋ ค์ฃผ๊ณ , Controller๊ฐ€ ์ด๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•ด์ค€๋‹ค.

์ด๋ ‡๊ฒŒ Controller๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์„ธ ๊ฐœ๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ์„ ํ†ตํ•ด์„œ ๋ทฐ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค!

Swift (UIKit) ์—์„œ๋Š” ViewController๊ฐ€ View์™€ Controller์—ญํ• ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•œ๋‹ค. (์ด๋ฆ„๋„ ๋ทฐ์ปจ ํํฌํฌ) ์ฆ‰, Swift์—์„œ๋Š” View์™€ Controller๊ฐ€ ํ•˜๋Š” ์ผ๋“ค์„ ๋ชจ๋‘ ViewContrller๋ผ๋Š” ํ•œ๋†ˆ์ด ๋ชจ๋“  ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ํ•œ๋†ˆ๋งŒ.. ๋ฐ”์˜๋‹ค... ๊ทธ๋Ÿฌ๋ฏ€๋กœ,,, ์•ฑ์ด ์ปค์ง€๋ฉด ์ปค์งˆ์ˆ˜๋ก VC์˜ ์—ญํ• ์ด ์—„์ฒญ ์ปค์ง€๊ฒŒ ๋˜๊ณ , ๋ชจ๋ธ๊ณผ ๋ทฐ์˜ ๊ด€๊ณ„๊ฐ€ ๊ต‰์žฅํžˆ ๋ณต์žกํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํž˜๋“ค์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

๐Ÿ”– Massive ViewController

https://medium.com/divar-mobile-engineering/what-is-massive-view-controller-and-how-to-avoid-it-in-swift-2fca3e7dc00

iOS์˜ MVC ํŒจํ„ด์€, Massiveํ•˜๋‹ค๋ผ๋Š” ๋†๋‹ด์ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค. ๋†๋‹ด? ํŠธ๋ฃจ์•„๋‹Œ๊ฐ€!
๊ทธ๋ž˜์„œ ์ด ๊ฑฐ๋Œ€ํ•ด์ง€๋Š” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—ญํ• ์„ ๋‚˜๋ˆ ์ฃผ์–ด์•ผํ•œ๋‹ค. ์Œ,, ์ฐพ์•„๋ณด๋‹ˆ๊นŒ DataSource๋ฅผ ํ™œ์šฉํ•ด์„œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ๋”ฐ๋กœ ๋นผ์„œ ๊ด€๋ฆฌํ•ด์ฃผ๊ฒŒ ๋งŒ๋“ค๊ฑฐ๋‚˜, Delegate๋‚˜ Cordinator ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด์„œ ์—ญํ• ์„ ์ž‘๊ฒŒ ์ชผ๊ฐœ์ฃผ๊ณ , VC๋Š” ๋˜๋„๋ก View์— ์ง‘์ค‘ํ•ด์„œ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ๋งŒ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•ด์ค€๋‹ค. 

๋‚˜๋„ ์ด์ „์—, VC๋กœ ๊ฐœ๋ฐœํ•˜๋‹ค๊ฐ€ Coordinator ํŒจํ„ด์„ ์ ์šฉํ•ด๋ณด๋Š” ํ”„๋กœ์ ํŠธ์— ์ฐธ์—ฌํ•ด๋ณธ์ ์ด ์žˆ๋‹ค. (๊ฐ“์šฐ๋””๋‹˜๊ป˜์„œ ์ž˜ ์„ค๋ช…์„ ๋‚จ๊ฒจ์ฃผ์…จ์—ˆ์ง€,,,) ์–ด์จŒ๋“ , ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ Massive VC ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. (์กฐ๊ธˆ ๋” ์„ฑ์žฅํ•˜๋ฉด ์•„๋ž˜ PR๊ณผ ์ด์Šˆ๋ฅผ ๋ชจ๋‘ ์ดํ•ดํ•ด๋ณด์ž)

https://github.com/DeveloperAcademy-POSTECH/MC3-Team7-MoTe/issues/49

 

[feature] ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด ์ ์šฉ · Issue #49 · DeveloperAcademy-POSTECH/MC3-Team7-MoTe

์ž‘์—… ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด ์ ์šฉ ์•„๋ž˜์— ์ ์šฉ ์ด์œ ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์ ์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Alarmi ์•ฑ ํ”Œ๋กœ์šฐ๋Š” ํฌ๊ฒŒ ๋‘๊ฐ€์ง€๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ ์•Œ๋žŒ์„ ๋“ฑ๋กํ•˜๋Š” ํ”Œ๋กœ์šฐ ๋ฉ”์ธํƒญ ํ”Œ๋กœ์šฐ ์—ฌ๊ธฐ์„œ ์ €๋Š” ์•Œ๋žŒ์„ ๋“ฑ

github.com

https://github.com/DeveloperAcademy-POSTECH/MC3-Team7-MoTe/pull/50

 

[D0] ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด ์ ์šฉ by wody-d · Pull Request #50 · DeveloperAcademy-POSTECH/MC3-Team7-MoTe

์ด์Šˆ๋ฒˆํ˜ธ #49 ์ž‘์—…์‚ฌํ•ญ ์ด์Šˆ์— ์ž‘์„ฑํ•œ ์ด์œ ์— ๋”ฐ๋ผ ์ฝ”๋””๋„ค์ดํ„ฐ ํŒจํ„ด์„ ์ ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ™”๋ฉด ๊ตฌ์กฐ์— ๊ด€ํ•œ ๊ทธ๋ฆผ ํ•˜๋‚˜ ์ฒจ๋ถ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐ์„ค๋ช… ํ™”๋ฉด ์‚ฌ์ด ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋‹จ์  ๊ตฌ์กฐ ์ฝ”๋””๋„ค์ดํ„ฐ์˜ ๋ชจ

github.com

 

โšซ๏ธ MVVM์ด ๋ญ”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•ด๋ณด์ž.

MVC์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ„ฐ์ค‘ ํ•˜๋‚˜๊ฐ€ MVVM ์ด๋‹ค. Model, View, ViewModel ์„ธ๊ฐ€์ง€ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋‹ค.

  • Model: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ์™€, ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.
  • View: ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ๋ถ€๋ถ„์ด๋‹ค.
  • ViewModel: View๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“  View๋ฅผ ์œ„ํ•œ Model์ด๋‹ค. View๋ฅผ ๋‚˜ํƒ€๋‚ด์ฃผ๊ธฐ ์œ„ํ•œ Data๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋“ค์ด ์—ฌ๊ธฐ์— ํฌํ•จ๋˜์–ด์žˆ๋‹ค.
View ์™€ ViewModel ์‚ฌ์ด๋Š” CommandํŒจํ„ด๊ณผ Data binding (๋ฐ์ดํ„ฐ๋ฐ”์ธ๋”ฉ) ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค. CommandํŒจํ„ด๊ณผ Data Binding์„ ์‚ฌ์šฉํ•ด์„œ View์™€ ViewModel์‚ฌ์ด์˜ ์˜์กด์„ฑ์„ ์—†์•ด๋‹ค๊ณ  ํ–ˆ๋‹ค. ViewModel๊ณผ View๋Š” 1:n ๊ด€๊ณ„์ด๋‹ค.  (์ญ? ์ดํ•ด๊ฐ€ ์•ˆ๊ฐ€.. ๋ฌด์Šจ๋ง์ผ์ง€ ์ข€ ๋” ๊ณ ๋ฏผํ•ด๋ณด๊ณ  ๋‹ค์‹œ ํฌ์ŠคํŒ…ํ•˜์ž. ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋ผ์„œ ๊ทธ๋Ÿฐ๊ฐ€?)

- Command(์ปค๋งจ๋“œ) : ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์š”์†Œ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋‚˜์˜ ์•ก์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๊ธฐ๋ฒ•
- Data Binding(๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ) : ํ™”๋ฉด์— ๋ณด์ด๋Š” ๋ฐ์ดํ„ฐ์™€ ์›น ๋ธŒ๋ผ์šฐ์ €์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์น˜์‹œํ‚ค๋Š” ๊ธฐ๋ฒ•, ๋ทฐ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ทฐ์— ๋ฐ”๋กœ ๋ณ€๊ฒฝ๋˜์–ด ๊ฐฑ์‹ ๋œ๋‹ค.

 

MVVM์˜ ๋™์ž‘ ์ˆœ์„œ๋ฅผ ๋ณด๋ฉด, ์œ„์—์„œ ํ•˜๋Š” ์—ญํ• ๋“ค์˜ ์ดํ•ด๊ฐ€ ์กฐ๊ธˆ ๋น ๋ฅผ ๊ฒƒ์ด๋‹ค.

  1. View๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„๋“ค์ธ๋‹ค.
  2. ๋ฐ›์€ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๋Š” Command ํŒจํ„ด์œผ๋กœ ViewModel์— ์ „๋‹ฌ๋œ๋‹ค.
  3. VIewModel์€ Model์— ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•œ๋‹ค.
  4. Model์€ ViewModel์—๊ฒŒ ์š”์ฒญ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ตํ•ด์„œ ๋ณด๋‚ด์ค€๋‹ค.
  5. ViewModel์€ ์‘๋‹ต๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ†ตํ•ด ์ด๋ฆฌ์ €๋ฆฌ ๊ฐ€๊ณตํ•ด์„œ ์ €์žฅํ•œ๋‹ค.
  6. View๋Š” ViewModel๊ณผ Data Binding ํ•˜์—ฌ ํ™”๋ฉด์— ๊ฐฑ์‹ ๋œ ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ธ๋‹ค.

MVVM์€ View์™€ Model ์‚ฌ์ด์˜ ์˜์กด์„ฑ์ด ์—†๊ณ , ์ปค๋งจ๋“œ์™€ ๋ฐ์ดํ„ฐ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด์„œ ๋ทฐ์™€ ๋ทฐ๋ชจ๋ธ์˜ ์˜์กด์„ฑ ๋˜ํ•œ ์—†๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ๊ฐ์ด ๋ถ€๋ถ„์€ ๋…๋ฆฝ์ ์ด๊ณ  ๋ชจ๋“ˆํ™” ๋˜์–ด ๊ฐœ๋ฐœ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ๊ฐ€์žฅ ํฐ ์žฅ์ ์ด๋‹ค.

 

โšซ๏ธ MVC๋กœ ๊ฐ„๋‹จํ•œ TableView๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค. 

์Œ,,, ๊ฐ„๋‹จํ•œ ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค. ํ…Œ์ด๋ธ”๋ทฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์€ ์ƒ๋žตํ•˜๊ณ , ๊ทธ๋ƒฅ ๊นƒํ—ˆ๋ธŒ ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ ค๋‘๊ฒ ๋‹ค. 
๋‚ด๊ฐ€ ๋งŒ๋“  ํ…Œ์ด๋ธ”๋ทฐ๋Š”, + ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ˆ„๋ฅผ์ˆ˜๋ก ํ•œ cell์”ฉ ์ฆ๊ฐ€ํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  reset๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ…Œ์ด๋ธ”๋ทฐ ์ „์ฒด ๋‚ด์—ญ์ด ์‚ฌ๋ผ์ง„๋‹ค.  

ํ˜„์žฌ๋Š”, ์ด ๋ฐ์ดํ„ฐ๋“ค๊ฐ€ ์ถ”๊ฐ€๋˜๊ณ  ์‚ญ์ œ๋˜๋Š” ๋กœ์ง๋“ค์ด ์ „๋ถ€ ViewController ๋‚ด๋ถ€์— ์กด์žฌํ•œ๋‹ค. (๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ ๊ฐ„๋‹จํ•ด์„œ Model ํŒŒ์ผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์ง€ ์•Š์•˜์ง€๋งŒ, ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์„œ ViewController๊ฐ€ ๋ชจ๋“  ๊ฒƒ์— ๊ด€์—ฌํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

https://github.com/deslog/awesome-swift/tree/main/DesignPattern/DesignPattern/Patterns/MVC

 

GitHub - deslog/awesome-swift: swift๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋“  ๊ณต๋ถ€๋ฅผ ๋ชจ์•„๋‘๋Š” ๊ฐ์ž์˜ ๋š๋”ฑ๋š๋”ฑ ์š”๋ฆฌ์‹คํ—˜์‹ค ๐Ÿฅ”๐Ÿณ

swift๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋“  ๊ณต๋ถ€๋ฅผ ๋ชจ์•„๋‘๋Š” ๊ฐ์ž์˜ ๋š๋”ฑ๋š๋”ฑ ์š”๋ฆฌ์‹คํ—˜์‹ค ๐Ÿฅ”๐Ÿณ๐Ÿ‘ฉ๐Ÿป‍๐Ÿณ. Contribute to deslog/awesome-swift development by creating an account on GitHub.

github.com

 

 

โšซ๏ธ MVVM ํŒจํ„ด์œผ๋กœ ๋ฐ”๊ฟ”๋ณด์ž! 

์šฐ์„ , ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Combine์„ ์‚ฌ์šฉํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. Combine์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์ž ์‹œ ๋‚˜์ค‘์œผ๋กœ ๋ฏธ๋ฃจ๊ณ , ์ผ๋‹จ Combnie์œผ๋กœ ๊ตฌํ˜„ํ•œ MVVM ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋Œ์•„๊ฐ€๋Š”์ง€ ์œ„์ฃผ๋กœ ์ฝ”๋“œ์„ค๋ช…์„ ํ•ด๋ณด์ž.

 

๐Ÿ”– ViewModel ์ƒ์„ฑ ๋ฐ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ๋กœ์ง ์˜ฎ๊ฒจ์ฃผ๊ธฐ

์šฐ์„   ๊ฐ€์žฅ ํฌ๊ฒŒ ๋ฐ”๋€Œ๋Š” ๊ฒƒ์€, ๊ธฐ์กด ์ฝ”๋“œ์—์„œ tableView์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  reset ํ•ด์ฃผ๋Š” ๋กœ์ง์ด ์ „๋ถ€ ViewModel์ด๋ผ๋Š” ํŒŒ์ผ๋กœ ์ด๋™ํ•œ๋‹ค.  ์šฐ์„  ์—†์—ˆ๋˜ ViewModel ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ฃผ๊ณ , ๊ทธ ์•ˆ์—๋‹ค๊ฐ€ ๊ธฐ์กด ViewController์— ์žˆ๋˜ @objc ํ•จ์ˆ˜๋“ค์„ ์˜ฎ๊ฒจ์ฃผ์—ˆ๋‹ค.

import Foundation
import Combine

class ViewModel: ObservableObject {

    // MARK: - property

    @Published var tempArr = [String]()
    var arrCnt = 0

    // MARK: - life cycle

    init() { }

    // MARK: - func

    @objc func addData(_ sender: Any) {
        arrCnt += 1
        tempArr.append("\(arrCnt)๋ฒˆ์งธ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€")
    }

    @objc func resetData(_ sender: Any) {
        arrCnt = 0
        tempArr = []
    }
}
  • @Published: ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ๊ฐ์ง€ํ• ? ๊ฒƒ์„ ๋„ฃ์–ด์ค€๋‹ค. 
  • arrCnt: ์ด์ „์— ViewController์— ์žˆ์—ˆ๋˜ ๋ณ€์ˆ˜, ๋ณ€๊ฒฝ๋˜๋Š” ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์™”๋‹ค.
  • addData / resetData: ๋‘ ๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

 

๐Ÿ”– ViewModel์„ ์ƒ์„ฑํ–ˆ์œผ๋ฉด, ViewController์—์„œ viewmodel์„ ํ†ตํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ๋ฐ›๊ฒŒ ๋งŒ๋“ค์ž.

1๏ธโƒฃ ์šฐ์„  Combine์„ import ํ•ด์ฃผ๊ณ , viewmodel์„ ์„ ์–ธํ•ด์ฃผ์ž.

import Combine

var disposalbleBag = Set<AnyCancellable>()
var viewModel: ViewModel = ViewModel()

diposalbleBag์€ combine์„ ํ• ๋•Œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ค์ •๋˜๋Š” ์•„์ด์ด๋‹ค. ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” combiine ์„ค๋ช…ํฌ์ŠคํŒ…์œผ๋กœ ๋‚˜์ค‘์— ๋‹ค์‹œ ์˜ฌ๋ฆฌ๊ฒ ๋‹ค! ์–ด์จŒ๋“  ์ด๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•ด์ค˜์•ผํ•œ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด, disposalbalbag์€ ๋ง๊ทธ๋Œ€๋กœ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋„˜๊ฒจ์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํƒœ์›Œ์ฃผ๊ธฐ ์œ„ํ•ด์„œ ์„ ์–ธํ•ด์ค€๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ subscribeํ•˜๊ณ  ๋‚จ์€ ๋ฐ์ดํ„ฐ๋“ค์„ ๋‹ด์•„์ค€๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. (bag! ๐Ÿ’ผ)

2๏ธโƒฃ Button์— ์„ค์ •๋œ ํƒ€๊ฒŸ๊ณผ action์„ Viewmodel์— ์†ํ•œ ๋ฉ”์„œ๋“œ๋“ค๋กœ ๋ฐ”๊ฟ”์ฃผ์ž.

private lazy var resetButton = UIBarButtonItem(title: "Reset",
                                               style: .plain,
                                               target: self.viewModel,
                                               action: #selector(viewModel.resetData(_ :)))
private lazy var addButton = UIBarButtonItem(image: UIImage(systemName: "plus.circle"),
                                                 style: .plain,
                                                 target: self.viewModel,
                                                 action: #selector(viewModel.addData(_ :)))

3๏ธโƒฃ ViewModel์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ž.

์ง€๊ธˆ ๋‚ด ์ฝ”๋“œ์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋Š” ์ฝ”๋“œ ํ•œ๋ฉ์–ด๋ฆฌ์™€, ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ function์„ ์‹คํ–‰์‹œ์ผœ์ค˜์•ผํ• ๋•Œ function์„ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋Š” ๋ฉ์–ด๋ฆฌ ๋‘๋ฉ์–ด๋ฆฌ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋‘์—ˆ๋‹ค.

fileprivate func setBindings() {
        // ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ
        viewModel.$tempArr.sink{ (updatedList: [String]) in
            self.tempArr = updatedList
//            self.tableView.reloadData()
        }.store(in: &disposalbleBag)

        // ์•ก์…˜์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ (๋ทฐ์™€ ๊ด€๋ จํ•œ ํ•จ์ˆ˜ ๋กœ์ง๋„ ์ „๋ถ€ ๋ทฐ๋ชจ๋ธ์— ์žˆ๊ณ , ๊ทธ ์•ก์…˜์„ ๋ฐ”์ธ๋”ฉํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•)
        viewModel.dataUpdateAction.sink{ (addingType: ViewModel.AddingType) in
            print("addingํƒ€์ž…์€ ์ด๊ฒƒ \(addingType)")
            switch addingType {
            case .add:
                self.tableView.reloadData()
            default:
                self.tableView.reloadData()
            }
        }.store(in: &disposalbleBag)
    }

- tempArr: Viewmodel์—์„œ published๋กœ ์„ ์–ธ๋˜์–ด์žˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋‹ฌ๋˜์–ด ์™“๋‹ค๊ฐ“๋‹ค ๋˜๋ฉด์„œ ๋ฐ”์ธ๋”ฉ ๋˜์–ด์งˆ ๊ฒƒ์ด๋‹ค.

- sink: ์„ค๋ช…์„ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์™€์žˆ๋‹ค. ๊ตฌ๋…ํ•œ๋‹ค๋ผ๊ณ  ํ”ํžˆ ์ด์•ผ๊ธฐํ•œ๋‹ค. ์–ด๋–ค published๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ์ค„๊ฑด์ง€ ์ •ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” tempArr์ด๋‹ค.

- store:  ๊ตฌ๋…ํ•˜๊ณ  ๋‚จ์€ ์•„์ด๋“ค์„ ์—ฌ๊ธฐ๋‹ค๊ฐ€ ๋‹ด์•„์ฃผ์ž! ์˜ ์˜๋ฏธ

์ฆ‰, ์„ค๋ช…ํ•˜์ž๋ฉด sink๋˜์–ด ๋“ค์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ tempArr์— ๋„ฃ์–ด์ฃผ๊ฒŸ๋‹ค๋Š” ์˜๋ฏธ์˜ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค. ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‚ด๋ถ€์— updatedList๋ฅผ tempArr์— ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค! 

dataUpdateActionํ•จ์ˆ˜๋Š”, ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ ํŠน์ € ์•ก์…˜์„ ๋„ฃ์–ด์ฃผ๊ณ  ์‹ถ์–ด์„œ ํ•œ๋ฒˆ ๋„ฃ์–ด๋ดค๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ๋Š”, AddingType์ด๋ผ๋Š” enumํƒ€์ž…์„ ์ „๋‹ฌ๋ฐ›๋Š”๋ฐ, addButton์ด ๋ˆŒ๋ฆฌ๋ฉด .add๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ํŠน์ •ํ•จ์ˆ˜ (์—ฌ๊ธฐ์„œ๋Š” ๊ทธ๋ƒฅ reloadData()๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค.)๋ฅผ ์‹คํ–‰์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค. reloadData๋Š” ์‚ฌ์‹ค tempArr๋ฅผ sink๋ฐ›์„ ๋•Œ ๋„ฃ์–ด๋„ ๋˜์ง€๋งŒ (์ฃผ์„์ฒ˜๋ฆฌ๋˜์–ด ์žˆ์Œ) ๋งŒ์•ฝ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ reload()๋ฅผ ํ•ด์ฃผ์–ด์•ผํ•˜๋Š” ์ƒํ™ฉ์ด ์˜จ๋‹ค๋ฉด, ์•„๋ž˜์ฒ˜๋Ÿผ action์— ๋Œ€ํ•œ binding๋„ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด์„œ ๋งŒ๋“ค์–ด์ค˜์•ผํ•œ๋‹ค!

 

์„ค๋ช…์ด ์กฐ๊ธˆ ์ž˜ ๋๋Š”์ง€,, ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ์ผ๋‹จ '๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ๋‹˜'์˜ ์˜์ƒ์„ ๋ณด๋ฉด ๋” ์ˆ˜์›”ํ•˜๊ฒŒ ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋” ์ดํ•ด๊ฐ€ ๋น ๋ฅผ ๊ฒƒ์ด๋‹ค!

๊ฐœ๋ฐœํ•˜๋Š” ์ •๋Œ€๋ฆฌ๋‹˜์˜ ์œ ํŠœ๋ธŒ ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด์„œ ํฌ์ŠคํŒ… ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
(https://www.youtube.com/watch?v=TLX7MjtOPd0&t=873)

https://github.com/deslog/awesome-swift/tree/main/DesignPattern/DesignPattern/Patterns/MVVM

 

GitHub - deslog/awesome-swift: swift๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋“  ๊ณต๋ถ€๋ฅผ ๋ชจ์•„๋‘๋Š” ๊ฐ์ž์˜ ๋š๋”ฑ๋š๋”ฑ ์š”๋ฆฌ์‹คํ—˜์‹ค ๐Ÿฅ”๐Ÿณ

swift๋ฅผ ํ™œ์šฉํ•œ ๋ชจ๋“  ๊ณต๋ถ€๋ฅผ ๋ชจ์•„๋‘๋Š” ๊ฐ์ž์˜ ๋š๋”ฑ๋š๋”ฑ ์š”๋ฆฌ์‹คํ—˜์‹ค ๐Ÿฅ”๐Ÿณ๐Ÿ‘ฉ๐Ÿป‍๐Ÿณ. Contribute to deslog/awesome-swift development by creating an account on GitHub.

github.com

 

 

์ด๋ ‡๊ฒŒ ์ง์ ‘ ๋‚ด๊ฐ€ MVVM์„ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค. ๋ฌผ๋ก , ์ง€๊ธˆ์€ ์ข‹์€ ํŠœํ† ๋ฆฌ์–ผ ๊ฐ•์˜์™€ ์•„์ฃผ ๊ฐ„๋‹จํ•œ ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ• ๋งŒํ•˜๋„ค! ๋ผ๋Š” ๋ง์ด ๋‚˜์™”๋˜๊ฒƒ ๊ฐ™๋‹ค. ๋‚ด ๋ชฉํ‘œ๋Š” ๋™์‹์ด ๊ฐœ๋ฐœ์ด ๋๋‚˜๋ฉด, ๋™์‹์ด ํ”„๋กœ์ ํŠธ ์ „๋ฐ˜์„ MVVM์œผ๋กœ ๋ฐ”๊ฟ”๋ณด๋Š”๊ฒƒ์ด๋‹ค! 

 


๐Ÿ“– Reference

https://beomy.tistory.com/43

 

[๋””์ž์ธํŒจํ„ด] MVC, MVP, MVVM ๋น„๊ต

์›น ๊ฐœ๋ฐœ์ž๋กœ ์ผ์„ ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ๋จผ์ € ์ ‘ํ•œ ๋””์ž์ธํŒจํ„ด์ด ๋ฐ”๋กœ MVC ํŒจํ„ด์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋งŒํผ ์œ ๋ช…ํ•˜๊ณ  ๋งŽ์ด ์“ฐ์ด๋Š” ๋””์ž์ธํŒจํ„ด์ธ MVC ํŒจํ„ด๊ณผ MVC ํŒจํ„ด์—์„œ ํŒŒ์ƒ๋˜์–ด์ ธ ๋‚˜์˜จ MVP ํŒจํ„ด๊ณผ MVVM ํŒจํ„ด์„ ์ด์•ผ

beomy.tistory.com

https://github.com/TuenTuenna/ios_uikit_dynamic_tableview/tree/fetch_more

 

GitHub - TuenTuenna/ios_uikit_dynamic_tableview

Contribute to TuenTuenna/ios_uikit_dynamic_tableview development by creating an account on GitHub.

github.com

 

๋ฐ˜์‘ํ˜•