ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MVVM 패턴
    디자인 패턴 2022. 4. 21. 16:46

    패턴 개요

    Model(M), View(V), ViewModel(VM)으로 어플리케이션을 구성하는 디자인 패턴이다.

    구조

    전체적인 구조는 다음과 같다.


    Model

    데이터와 비즈니스 로직 등을 담는 역할을 한다. View, ViewModel은 신경쓰지 않으며, 데이터가 보여지는 방식에 대한 고려는 하지 않는다. 이는 MVC에서의 Model과 비슷하다.


    View

    화면 구성에 집중한다. Model을 직접 알고 있지 않으며, ViewModel로부터 데이터를 가져와서 화면에 표시한다. 사용자와의 상호작용을 수신하여 ViewModel에 이에 대한 처리를 요청한다.


    MVC와의 차이점

    MVC에서의 View의 경우 화면에 보일 내용 등을 Controller가 담당하지만, MVVM에서의 View는 보이는 부분을 자기가 직접 설정한다.


    ViewModel

    View로부터 전달받은 요청을 처리(Model을 업데이트)하고, Model에 변화가 생기면 View에게 알린다. View와 Model 사이를 중재하여 presentation logic(view가 데이터를 더 쉽게 보여줄 수 있도록 데이터 형식을 다시 지정)을 처리한다.


    동작 흐름

    a. View가 ViewModel에게 이벤트를 알려주면, ViewModel이 Model을 업데이트한다.
    b. Model이 변화하면, ViewModel에게 알려지고, ViewModel과 바인딩된 View가 업데이트된다.


    MVC와의 차이점

    구성요소 간의 관계

    MVC의 경우, View와 Model이 다른 구성요소를 알지 못하고, Controller가 나머지 두 구성요소를 모두 알고 있는 형태이다. MVVM의 경우, View가 ViewModel을 소유하여 알고 있고, ViewModel은 Model만을 알고 있다.


    데이터를 보여주는 방식

    MVC의 경우, Model에 View를 Observer로 등록하는 방식이나, Controller가 Model의 변경을 감지하여 View에 데이터를 설정하는 방식 중 하나를 사용한다.

    반면 MVVM의 경우, View가 ViewModel과의 바인딩을 통해 스스로 데이터를 보여준다. 데이터를 화면에 보이기 위한 책임을 View가 직접 가진다는 뜻이다.


    사용자 이벤트 처리

    MVC에서는 View가 Controller를 모르기 때문에 처리를 부탁하려면 delegate pattern이나 target-action을 사용해야 한다. MVVM의 경우, View가 ViewModel을 가지고 있기 때문에 메서드를 직접 호출하여 이벤트 처리가 가능하다.


    예시

    Model: CurrentDate

    struct CurrentDate: Decodable, Identifiable {
        let id = UUID()
        let date: String
    
        private enum CodingKeys: String, CodingKey {
            case date = "date"
        }
    }

    ViewModel: CurrentDateViewModel, CurrentDatesListViewModel

    @MainActor
    class CurrentDatesListViewModel: ObservableObject {
        @Published var currentDates: [CurrentDateViewModel] = []
    
        func populateDates() async {
            do {
                let currentDate = try await Webservice().getDate()
                if let currentDate = currentDate {
                    let currentDateViewModel = CurrentDateViewModel(currentDate: currentDate)
                    self.currentDates.append(currentDateViewModel)
                }
            } catch {
                print(error)
            }
        }
    }
    
    struct CurrentDateViewModel {
        let currentDate: CurrentDate
    
        var id: UUID {
            currentDate.id
        }
    
        var date: String {
            currentDate.date
        }
    }

    View: ContentView

    struct ContentView: View {
        @StateObject private var currentDateListVM = CurrentDatesListViewModel()
        @State private var currentDates: [CurrentDate] = []
    
        var body: some View {
            NavigationView {
                List(currentDateListVM.currentDates, id: \.id) { currentDate in
                    Text(currentDate.date)
                }.listStyle(.plain)
    
                .navigationTitle("Dates")
                .navigationBarItems(trailing: Button(action: {
                    Task {
                        await currentDateListVM.populateDates()
                    }
                }, label: {
                    Image(systemName: "arrow.clockwise.circle")
                }))
                .task {
                    await currentDateListVM.populateDates()
                }
            }
        }
    }

    Reference

    https://velog.io/@ictechgy/MVVM-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4#event-%EC%B2%98%EB%A6%AC%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%94%EC%9D%B8%EB%94%A9
    https://www.udemy.com/course/asyncawait-and-actors-concurrency-in-swift/

    '디자인 패턴' 카테고리의 다른 글

    MVC Pattern  (0) 2022.03.12

    댓글

Designed by Tistory.