지난 번, Firebase에 저장한 데이터를 View로 불러오는 것을 해봤습니다.
실제 개발을 진행한다면 앞선 방식보다는 정형화된 방법으로 데이터를 저장하고 다루는것이 공동개발과 유지보수에도 좋을 겁니다. 그렇다면 아키텍쳐 디자인 패턴인 MVVM 패턴에 대해 공부해보겠습니다.
마이크로소프트에서는 MVVM 패턴에 대해 다음과 같이 정의하고 있습니다.
MVVM(Model-View-ViewModel)은 UI 및 비 UI 코드를 분리하기 위한 UI 아키텍처 디자인 패턴입니다. MVVM을 사용하여 UI를 XAML로 선언적으로 정의하고 데이터 바인딩 태그를 사용하여 데이터 및 명령을 포함하는 다른 계층에 연결합니다. 데이터 바인딩 인프라는 UI 및 연결된 데이터를 동기화된 상태로 유지하고, 사용자 입력을 적절한 명령으로 라우팅하는 느슨한 결합을 제공합니다.
느슨한 결합을 제공하기 때문에, 데이터 바인딩을 사용하면 서로 다른 종류의 코드 간에 하드 종속성이 줄어듭니다. 이렇게 하면 다른 단원에서 의도치 않은 부작용을 발생시키지 않고도 개별 코드 단위(메서드, 클래스, 컨트롤 등)를 보다 쉽게 변경할 수 있습니다. 이러한 분리는 여러 디자인 패턴에서 중요한 개념인 문제의 분리 예입니다.
즉,
1) UI와 UI가 아닌 코드를 분리하여 정의한다.
2) 데이터 바인딩 태그를 사용하여 다른 계층에 연결하고 UI 및 연결된 데이터를 동기화된 상태로 유지한다.
3) 느슨한 결합을 제공하여 하드 종속성이 줄어 의도치 않은 부작용을 발생시키지 않고 개별 코드 단위를 보다 쉽게 변경할 수 있다.
그렇다면 MVVM 패턴을 사용하는 이유는 무엇일까?
다음 동영상을 참고하여 SwiftUI에서 MVVM 패턴을 사용해보겠습니다.
기본적으로 생성되는 뷰(ContentView)
파일과 함께 추가로 모델(Person)
, 뷰 모델(PersonViewModel)
파일을 생성해줍니다.
// Person
import SwiftUI
struct Person: Identifiable {
var id = UUID()
var name: String
var email: String
var phoneNumber: String
}
// ContentView
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// PersonViewModel
import SwiftUI
class PersonViewModel: ObservableObject {
@Published var people: [Person] = []
}
뷰 모델에는 ObservableObject
프로토콜을 부여하여 후에 뷰가 뷰 모델의 변경사항을 감시할 수 있게 됩니다.
추가로 @Published
로 표시된 속성이 있는 오브젝트가 변경될 때마다 해당 오브젝트를 사용하는 모든 뷰가 다시 로드되어 해당 변경 사항을 반영합니다.
다음 포스팅에는 Firebase로부터 가져온 데이터를 저장하고 활용하겠지만 지금은 데이터를 임의로 추가해줍니다.
// PersonViewModel
import SwiftUI
class PersonViewModel: ObservableObject {
@Published var people: [Person] = []
init() {
addPeople()
}
func addPeople() {
people = peopleData
}
func shuffleOrder() {
people.shuffle()
}
func reverseOrder() {
people.reverse()
}
func removeLastPerson() {
people.removeLast()
}
}
let peopleData = [
Person(name: "Paul Atreides", email: "Paul@naver.com", phoneNumber: "111-1111"),
Person(name: "Lady Jessica", email: "Jessica@naver.com", phoneNumber: "222-2222"),
Person(name: "Duke Atreides", email: "Leto@naver.com", phoneNumber: "333-3333"),
Person(name: "Baron Harkonnen", email: "Harkonnen@naver.com", phoneNumber: "444-4444"),
Person(name: "Duncan Idaho", email: "Duncan@naver.com", phoneNumber: "555-5555")
]
데이터를 생성해주고 init()
을 사용하여 최초 실행 시 people
변수에 데이터를 추가해 줍니다.
// ContentView
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel = PersonViewModel()
var body: some View {
VStack(alignment: .leading) {
ForEach(viewModel.people) { person in
HStack {
Text(person.name)
.font(.title)
Text(person.email)
}
}
Button {
viewModel.reverseOrder()
} label: {
Text("Reverse")
}
Button {
viewModel.removeLastPerson()
} label: {
Text("RemoveLast")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@ObservedObject
는 관찰중인 객체의 변경에 반응해서 화면을 업데이트할 수 있게 해주는 프로퍼티 래퍼입니다. ObservableObject
속성을 사용하는 PersonViewModel
의 변경사항을 감시하게 됩니다.
PersonViewModel
의 인스턴스를 생성하고 프로퍼티를 사용하여 저장된 데이터를 불러오고 사용할 수 있습니다.
마지막으로 MVVM 패턴의 불필요성에 대한 글이 있어 함께 공유합니다.