[SwiftUI] MVVM 아키텍처 패턴 (1탄 - 이해하기)
[SwiftUI] MVVM 아키텍처 패턴 (1탄 - 이해하기)
프로젝트를 할 때면 여러 아키텍처 패턴에 대해 고민을 하게 된다. 이번에 하는 프로젝트에서는 SwiftUI에 올바른 아키텍처 패턴이 무엇일지에 대해 고민하면서 SwiftUI 자체의 프레임워크 특성이 MVVM을 반영하고 있었기에 No 패턴을 고민하기도 했었고, 많이 상용되고 있는 MVVM패턴, 새로 알게 된 TCA 패턴 등 여러 고민을 했지만 결국에는 많이 사용되는 MVVM패턴을 적용하게 됐다.
이번 글에서는 MVVM 패턴에 대한 올바른 이해를 가져감과 동시에 SwiftUI를 이용하여 구현중인 프로젝트에 MVVM의 적합성을 판단하며 내용을 정리해보려 한다.
MVVM패턴이란?
MVVM(Model - View - ViewModel) 패턴은 SW 디자인 패턴중 하나로, 특히 GUI 프로그램 개발에서 많이 사용된다. Model, View, ViewModel 세 가지 요소로 구성되고, 각 요소는 서로의 역할을 분리하여 코드의 유지보수성과 재사용성을 높이는데 목적을 둔다.
Model
1
2
3
4
5
6
7
8
9
struct User: Identifiable {
var id: UUID
var name: String
var age: Int
mutating func increaseAge() {
age += 1
}
}
역할
- 어플리케이션의 데이터, 데이터 비즈니스 로직을 관리
책임
- 데이터의 구조 정의 및 저장
- 데이터의 CRUD(Create, Read, Update, Delete) 작업 처리
- 비즈니스 로직 구현(ex. 데이터 검증, 계산, …)
- DB 또는 네트워크와의 상호작용
특징
- struct 또는 class로 구현됨
- @Published 속성을 사용하여 데이터 변화를 알릴 수 있음
- UI에 대한 정보가 없고, UI와 독립적으로 동작
- 순수한 데이터 및 로직만 포함하여, 상태 관리나 데이터 처리 로직이 포함되어 있음
View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import SwiftUI
struct UserView: View {
@ObservedObject var viewModel: UserViewModel
var body: some View {
VStack {
Text("이름: \(viewModel.user.name)")
Text("나이: \(viewModel.user.age)")
Button("나이 먹기") {
viewModel.increaseAge()
}
}
}
}
역할
- 사용자 인터페이스(UI)를 나타내며, 사용자와의 상호작용을 담당
책임
- 사용자의 입력을 받아들임
- ViewModel로부터 데이터 바인딩 및 표시
- ViewModel에 사용자와의 상호작용한 이벤트를 전달
- UI와 관련된 비즈니스 로직을 처리
특징
- 논리에 대한 처리는 최대한 ViewModel로 위임하여 역할에 충실할 수 있게 구성
- @ObservedObject 또는 @StateObject를 사용하여 ViewModel과 바인딩
ViewModel
1
2
3
4
5
6
7
8
9
10
11
class UserViewModel: ObservableObject {
@Published var user: User
init(user: User) {
self.user = user
}
func increaseAge() {
user.increaseAge()
}
}
역할
- Model과 View 사이에서 중개자의 역할을 한다. 사용자의 입력을 받아 Model에 전달하고, View를 위한 데이터를 준비하기도 한다.
책임
- Model의 데이터를 View에 제공하기 위한 형식으로 변환
- 사용자 입력 처리 및 그 결과를 Model에 반영
- View와 Model 간의 데이터 동기화
특징
- ObservableObject 프로토콜(Observable 패턴)을 채택하여 데이터 변화를 알림
- @Published 속성을 사용하여 바인딩된 View에 알림
- 비즈니스 로직을 포함하지만, UI에 종속적이지 않도록 설계
- View의 생명주기와 독립적으로 존재
MVVM 패턴의 특징
1. 유지보수성 향상
- 각 컴포넌트가 명확하게 책임을 가지고, 코드를 더 쉽게 이해하거나 수정할 수 있음
- 코드가 더 구조화되고 모듈화 되어 있어 특정 기능을 수정하거나 확장하기 쉬움
2. 재사용성 증가
- View와 비즈니스 로직을 분리하여, 동일한 ViewModel을 다양한 View에서 재사용할 수 있음
- 동일한 비즈니스 로직을 여러 View에서 사용할 수 있음
3. 테스트 용이성
- 비즈니스 로직이 ViewModel에 집중되어 있어서 UI 테스트를 하지 않고도 로직 테스트를 할 수 있음
- 유닛 테스트 작성이 더 쉬워지고, 테스트 커버리지를 높일 수 있음
4. 데이터 바인딩
- View와 ViewModel 간의 데이터 바인딩을 통해, 데이터 변경 시 자동으로 UI가 업데이트 됨
- SwiftUI에서는 @Published, @StateObject, @ObservedObject 등을 통해 데이터 바인딩
5. UI 논리의 분리
- UI 관련 코드(View)와 비즈니스 로직(ViewModel)이 분리되어 있어, UI 변경이 로직에 영향을 미치지 않음
이어서…
MVVM 패턴은 이렇게만 보면 정말 좋은 패턴이지만 SwiftUI에서 사용하게 된다면 따라오게 되는 몇 가지의 딜레마가 있다 생각한다. 다음 편에서는 그에 대해서 다뤄보고 글을 정리하려 한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.