๐Ÿ‘จโ€๐ŸŽ“
Today I Learned
  • Today-I-Learend
  • ๐ŸŽWWDC
    • Developer Tools
      • Testing in Xcode
    • UIKit
      • UIDiffableDataSource
        • [WWDC 19] Advances in UI Data Sources
      • [WWDC2019] Advances in CollectionView Layout
  • ์ž๋ฃŒ๊ตฌ์กฐ
    • Heap ์ž๋ฃŒ๊ตฌ์กฐ
  • Clean code
    • ๋„ค์ด๋ฐ
    • ์ฃผ์„๊ณผ ํฌ๋งทํŒ…
    • ํ•จ์ˆ˜
    • ํด๋ž˜์Šค
    • ์—๋Ÿฌ ํ•ธ๋“ค๋ง
    • ๊ฐ€๋…์„ฑ ๋†’์ด๊ธฐ
    • ๊ฐ์ฒด์ง€ํ–ฅ
  • Network
    • RestAPI
  • Swift
    • DateType
    • ARC
    • Availablity
    • KeyPath
    • Network
    • Neverํƒ€์ž…
    • Result
    • Selector
    • ๊ฒ€์ฆํ•จ์ˆ˜
    • ๋ฉ”ํƒ€ํƒ€์ž…
    • ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
    • ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „
    • ์—๋Ÿฌ์ฒ˜๋ฆฌ
    • ์ ‘๊ทผ์ œ์–ด (Access Control)
    • ์ œ๋„ค๋ฆญ
    • ์ฃผ์š” ํ”„๋กœํ† ์ฝœ
  • ์•Œ๊ณ ๋ฆฌ์ฆ˜
    • ๊ทธ๋ž˜ํ”„
    • ๊ธฐ์ดˆ ์•Œ๊ณ ๋ฆฌ์ฆ˜
    • ๋ˆ„์ ํ•ฉ(Prefix)
    • ๋ณต์žก๋„
    • ๋น„ํŠธ๋งˆ์Šคํ‚น
  • ์šด์˜์ฒด์ œ
    • ์šด์˜์ฒด์ œ์˜ ๊ฐœ์š”
    • ํ”„๋กœ์„ธ์Šค์™€ ์Šค๋ ˆ๋“œ
    • CPU ์Šค์ผ€์ค„๋ง
    • ํ”„๋กœ์„ธ์Šค ๋™๊ธฐํ™”
    • ๊ต์ฐฉ์ƒํƒœ
    • 07. ๋ฉ”๋ชจ๋ฆฌ
    • 08.๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ
    • ์ž…์ถœ๋ ฅ ์žฅ์น˜
    • ํŒŒ์ผ ์‹œ์Šคํ…œ
  • UIKit
    • UITableView xib์œผ๋กœ ๋งŒ๋“ค์–ด๋ณด๊ธฐ
  • ๐Ÿ–Š๏ธ์ •๋ณด ๊ธฐ๋ก
    • ์ฝ”์ฝ”์•„ํŒŸ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•
  • iOS Project
    • ์ฑ„ํŒ… ์•ฑ ๋งŒ๋“ค๊ธฐ
      • Trouble shooting
      • 1. ๋””์ž์ธ
      • 2. AutoLayout
    • ๋‚ ์”จ ์กฐํšŒ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜
      • Figma๋ฅผ ์ด์šฉํ•œ UI ์„ค๊ณ„
      • TableView ์—ฐ๊ฒฐํ•˜๊ธฐ
      • Networking
    • MVC -> MVVM์œผ๋กœ ๊ตฌ์กฐ ๋ณ€๊ฒฝํ•ด๋ณด๊ธฐ
      • MVC
      • MVVM
    • OAuth Project
      • ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ๋ฅผ ์ด์šฉํ•œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ œ์ž‘
      • Github์˜ OAuth App ์„ค์ •
    • Rest API ํ”„๋กœ์ ํŠธ
      • UI์„ค๊ณ„ (with Figma)
      • Network Model
      • MVVM ๊ตฌ์กฐ ์ „ํ™˜
  • ๐Ÿ•ถ๏ธUIKit
    • Compositional Layout
Powered by GitBook
On this page
  • ๋“ค์–ด๊ฐ€๊ธฐ ์ „
  • ViewModel
  • ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด (Observable)
  • ์ฝ”๋“œ
  • ์ฝ”๋“œ (ViewModel)
  • View (ViewController.swift)
  • ์ฝ”๋“œ
  • ๊ฒฐ๋ก 
  1. iOS Project
  2. MVC -> MVVM์œผ๋กœ ๊ตฌ์กฐ ๋ณ€๊ฒฝํ•ด๋ณด๊ธฐ

MVVM

๋“ค์–ด๊ฐ€๊ธฐ ์ „

ViewModel์ด๋ž€?

์ด์ œ ๊ธฐ์กด MVC ํ”„๋กœ์ ํŠธ๋ฅผ MVVM์œผ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๊ธฐ ์ „์—, ViewModel์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ณด์ž.

ViewModel์€ View์—์„œ ์ผ์–ด๋‚œ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด์„œ Model์—๊ฒŒ ๊ทธ ๋ณ€๊ฒฝ์„ ์ „๋‹ฌํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์Œ์•…์„ ๊ฒ€์ƒ‰ํ•˜๋ฉด, ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ๋˜๋Š” ๊ฒ€์ƒ‰ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ ViewModel์ด ๊ทธ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ดํ›„์— Model์— ๊ทธ ๋ณ€๊ฒฝ์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด NetworkManager๋ผ๋Š” ๊ณ„์ธต์—๊ฒŒ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•˜๋Š” ์—ญํ• ์„ ๋‹ด๋‹นํ•œ๋‹ค.

๊ธฐ์กด MVC์˜ ViewController์™€ MVVM์˜ ViewController์˜ ์ฐจ์ด๋ฅผ ์‚ดํŽด๋ณด์ž.

์œ ์ €์˜ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์„ ViewModel์ด ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ๋„คํŠธ์›Œํฌ ์š”์ฒญ์˜ ์—ญํ• ๋„ ViewModel์ด ๋‹ด๋‹นํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ View(๊ธฐ์กด ViewController)๋Š” ๋ทฐ๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , TableView๋ฅผ reloadํ•˜๋Š” ์—ญํ• ๋งŒ ๋‚จ๊ฒŒ ๋˜์—ˆ๋‹ค.

MVVM์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์กฐ๊ฑด

ViewModel์€ ์–ด๋–ค ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•  ๋•Œ, ๊ทธ ๋ณ€ํ™”๋ฅผ ๋ทฐ๋ชจ๋ธ์ด ๊ฐ์ง€ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํ•˜์ง€๋งŒ UIKit์€ ๊ทธ๋Ÿฐ ๋ฐ”์ธ๋”ฉ ์—”์ง„์„ ์ œ๊ณตํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ UIKit์—์„œ MVVM์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์—”์ง„์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

(๋ฌผ๋ก  Combine์ด๋‚˜ RxSwift ๋“ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์ƒํƒœ์˜ ๋ณ€ํ™”๋ฅผ ๋ฐ”์ธ๋”ฉํ•  ์ˆœ ์žˆ๋‹ค. ์ฑ„์šฉ ๊ณต๊ณ ์— ๋‚˜์˜ค๋Š” MVVM ๋Œ€๋ถ€๋ถ„ Combine์ด๋‚˜ RxSwift๋ฅผ ์ด์šฉํ•ด์„œ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋”๋ผ. ํ•˜์ง€๋งŒ ๋‚˜๋Š” VM์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ์กฐ์ฐจ.. ๋ชจ๋ฅด๊ฒ ๋‹ค..)

๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์—”์ง„์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋งŽ์ง€๋งŒ, ๋‚˜๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์˜ Observable ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ–ˆ๋‹ค. (Boxing์ด๋ผ๋Š” ํ‚ค์›Œ๋“œ๋กœ ๊ฒ€์ƒ‰ํ•˜๋ฉด ๋งŽ์€ ์ž๋ฃŒ๊ฐ€ ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค)

[์ •๋ฆฌ] MVVM์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์กฐ๊ฑด

  1. ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด

  2. ๋ชจ๋ธ

  3. ๋ทฐ ๋ชจ๋ธ

  4. ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ (View)

ViewModel

๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด (Observable)

  • UIKit์€ ์‹œ์Šคํ…œ ์—”์ง„์—์„œ observableํ•œ ํ˜•ํƒœ๋ฅผ ์ œ๊ณตํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค.

  • ์ด์— UIKit์—์„œ MVVM์„ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ์ฒด๋ฅผ ๊ด€์ฐฐ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.

  • ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ, ๋‚˜๋Š” Observable ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๊ตฌํ˜„ํ–ˆ๋‹ค.

  • ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ Model์— ์ฃผ์ž…ํ•œ๋‹ค.

์ฝ”๋“œ

class Observable<T> {
    var value: T? {
        didSet {
            listener?(value)
        }
    }
    
    init(_ value: T?) {
        self.value = value
    }
    
    private var listener: ((T?) -> Void)?
    
    func bind(_ listener: @escaping (T?) -> Void) {
        listener(value)
        self.listener = listener
    }
}

Observable์ด๋ผ๋Š” ์ œ๋„ค๋ฆญ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ํƒ€์ž… T์— ํ•ด๋‹น๋˜๋Š” ๊ฐ’์— ๋Œ€ํ•ด ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค listener์—๊ฒŒ ์•Œ๋ ค์ฃผ๊ฒŒ ๋œ๋‹ค.

bind ํ•จ์ˆ˜๊ฐ€ ์ดํ•ด๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๊ฒฐ๋ก ์„ ๋จผ์ € ๋งํ•˜์ž๋ฉด, ๋“ฑ๋ก๋œ ๊ฐ’์ด ๋ณ€ํ•˜๊ณ , listner์— ๋“ฑ๋ก๋œ ํ•จ์ˆ˜๊ฐ€ ๋๋‚œ ํ›„์— view์—์„œ ํ•ด์•ผํ•  ์ผ์„ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋œ๋‹ค. (์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” tableview๋ฅผ reloadํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด๋œ๋‹ค.)

์ง๊ด€์ ์œผ๋กœ ์„ค๋ช…ํ•˜์ž๋ฉด listner ๋๋‚˜๊ณ  ํ•ด์•ผํ•  ์ผ์„ ์ž‘์„ฑํ•˜๋Š” ๊ณต๊ฐ„์ด๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค. ์ด bind๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, viewModel์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ (ViewModel)

struct UserListViewModel {
    
    init(){
        fetchMusicData()
    }
    
    let networkManager = NetworkManager.shared
    
    var users: Observable<[Music]> = Observable([])
    
    func fetchMusicData() {
        networkManager.requestData(term: "jazz") { result in
            switch result {
            case .success(let data):
                self.users.value = data
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }
}

(์ง€๊ธˆ์€ ๊ฒ€์ƒ‰์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ์ด ์—†์–ด์„œ ์œ ์ €์˜ ๊ฒ€์ƒ‰์–ด๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค)

NetworkManager์™€ ํ†ต์‹ ํ•˜๊ณ , Model์ธ users๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋‹ค.

์—ฌ๊ธฐ์—๋Š” ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์•˜์ง€๋งŒ, ์œ ์ €๊ฐ€ ๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•  ๋•Œ fetchMusicData๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

View (ViewController.swift)

  • ๋ทฐ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด์„œ tableview๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•œ๋‹ค.

์ฝ”๋“œ

final class ViewController: UIViewController {
    
    private let tableView: UITableView = {
        let table = UITableView()
        table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        return table
    }()
    
    private var viewModel = UserListViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        setTableView()
        setDataBinding()
    }
    
    private func setTableView() {
        view.addSubview(tableView)
        tableView.frame = view.bounds
        tableView.dataSource = self
    }
    
    private func setDataBinding() {
        viewModel.users.bind { _ in
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
}

extension ViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = viewModel.users.value?[indexPath.row].musicTitle
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        viewModel.users.value?.count ?? 0
    }
    
}

ViewController๋Š” ์ด์ œ View๋ฅผ ๊ทธ๋ฆฌ๋Š” ์—ญํ• ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ViewModel์˜ users ๋ฐ์ดํ„ฐ๋ฅผ bindํ•˜์—ฌ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•˜๊ณ , listner์˜ ๋™์ž‘์ด ๋๋‚˜๋ฉด tableView๋ฅผ reloadํ•ด์ฃผ๋Š” ์—ญํ• ์„ ๊ฐ–๊ฒŒ ๋๋‹ค.

๊ฒฐ๋ก 

๊ทธ๋ž˜์„œ ์™œ MVVM์„ ์‚ฌ์šฉํ•ด์•ผํ• ๊นŒ? ์–ธ์ œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„๊นŒ?

........ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์‚ฌ์‹ค Messive View Controller๋ผ๋Š” ์ด์•ผ๊ธฐ๊ฐ€ ๋งŽ์ง€๋งŒ, ๋ทฐ๋ฅผ ์ž˜ ๋ถ„๋ฆฌํ•˜๊ณ , NetworkModel์„ ์ž˜ ์„ค๊ณ„ํ•˜๋ฉด ๊ทธ ๋ฌธ์ œ๋Š” ์ถฉ๋ถ„ํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•œ๋‹ค..

๊ตฌ์กฐ๋ณ€ํ™˜์€ ํ•ด๋ดค์ง€๋งŒ ์žฅ์ ์€ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค

๊ฐ์ฒด๋ฅผ ๊ด€์ฐฐํ•˜๊ธฐ์œ„ํ•ด ๋˜ ๋‹ค๋ฅธ ๊ฐ์ฒด ์•ˆ์— ๋‹ด์•„ ๊ด€๋ฆฌํ•ด์•ผํ•˜๊ณ , ViewController๋งˆ๋‹ค ํ•˜๋‚˜์˜ ViewModel์ด ํ•„์š”ํ• ํ…๋ฐ(์•„๋‹Œ๊ฐ€..?) ์‚ฌ๋žŒ๋“ค์€ ์ด๊ฒŒ ์ง„์งœ ์ข‹์€๊ฐ€?

ํ…Œ์Šคํ„ฐ๋ธ”ํ•œ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค! ๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์€ ์กฐ๊ธˆ ๋” ๊ณ ๋ฏผํ•ด๋ณด๊ฒ ๋‹ค!

์•„์ง ๋งŽ์€ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•ด๋ณด์ง€ ์•Š์•„์„œ ์ด๋Ÿฐ ๊ณ ๋ฏผ์„ ํ•˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค

PreviousMVCNextOAuth Project

Last updated 2 years ago