MVVM(Model - View - ViewModel)
- 사용자 인터페이스(UI) 코드와 비즈니스 로직을 분리하여 코드의 유지 보수성과 재사용성을 높이는 데 목적
1. Model
- 역할
- 데이터와 비즈니스 로직을 처리
- 앱의 상태 및 데이터를 관리 → 데이터를 가져오고, 비즈니스 로직을 적용하는 역할
- 앱의 데이터와 비즈니스 로직을 캡슐화함
- ViewModel에서 요청한 데이터를 반환하거나, ViewModel이 전달한 데이터를 저장
- 구성요소 : 데이터 구조, 데이터베이스, 네트워크 요청, API 호출 등
- 상호작용 : ViewModel과 상호작용하여 데이터를 제공하거나 업데이트를 받음
struct User {
var name: String
var email: String
}
2. ViewModel
- 역할
- Model과 View 사이의 중개자 역할
- 로직을 정의하고 View에는 데이터 제공, Model에는 업데이트 요청
- 구성요소 : 데이터 상태 관리, 비즈니스 로직, 입력 처리
- 상호작용
- Model로부터 데이터를 받아서 처리하고 View에 필요한 형태로 변환
- View의 이벤트(예. 버튼 클릭 등)를 처리하고, 필요한 경우 Model을 업데이트 함
- View와의 데이터 바인딩을 통해 UI를 업데이트
* Model, View와 달리 Class를 사용(Model, View는 Struct 사용)
* 데이터의 값이 변경될 때마다 추적하기 위해선 @ObservedObject 를 따르는 객체가 필요
→ 객체의 값이 변경되기 전에 알려주는 Publisher, SwiftUI View에 다시 그리는 것을 가능하게 함
@ObservedObject | @StateObject |
- 객체화를 만들어 내부에서 일어나는 변화를 기반으로 어떻게 화면을 다시 그리는 지 보여줌 | - 하나의 객체로 만들어지고, View가 얼마나 초기화되는지 상관없이 별개의 객체 관리 |
- 새로 고침되면 일부 애니메이션이 있기 때문에 값이 새로고침 됨 | - View와 별개의 메모리 공간에 저장해 데이터를 안전하게 보관 |
→ 이미 객체화된 것을 넘겨 받고 나서 사용 - SubView, 다른 View에서 사용 |
→ 객체를 처음 초기화할 때 사용 |
import Foundation
import Combine
// 사용자 정보를 업데이트하는 함수
class UserViewModel: ObservableObject {
@Published var user: User = User(name: "", email: "")
@Published var errorMessage: String?
// 이름과 이메일이 비어있는지 확인. 비어 있으면 에러 메시지 설정 후 함수 종료
func updateUser(name: String, email: String) {
guard !name.isEmpty, !email.isEmpty else {
errorMessage = "Name and email cannot be empty."
return
}
// 이메일이 유효하지 않으면 에러 메시지를 설정하고 함수 종료
if !isValidEmail(email) {
errorMessage = "Invalid email address."
return
}
// 이름과 이메일이 유효하면 사용자 정보 업데이트 후 에러 메시지 초기화
user.name = name
user.email = email
errorMessage = nil
}
// 이메일 유효성을 검증하는 private 함수
// _ : 외부 매개변수를 사용하지 않겠다는 의미
private func isValidEmail(_ email: String) -> Bool {
// 이메일 정규 표현식
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
// 정규 표현식을 사용하여 이메일 형식 검사
let emailPred = NSPredicate(format: "SELF MATCHES %@", emailRegEx)
return emailPred.evaluate(with: email)
}
}
3. View
- 역할 : 사용자 인터페이스(UI, 사용자가 상호작용하는 모든 시각적 요소를 포함)
- 구성요소 : 텍스트 필드, 버튼, 레이블 등
- 상호작용
- ViewModel과 데이터 바인딩을 통해 상태를 표시
→ View는 모델의 데이터를 직접 사용하거나 수정할 수 없고 ViewModel에 접근하여 그 값을 참조하는 형태
- 사용자 입력 및 이벤트를 ViewModel에 전달
- 구현
- SwiftUI의 경우 Text, Button, List 등의 뷰 컴포넌트를 사용하여 구현
import SwiftUI
struct UserView: View {
@StateObject private var viewModel = UserViewModel()
var body: some View {
VStack(spacing: 20) {
TextField("Name", text: $viewModel.user.name)
.padding()
.background(Color(.systemGray6))
.clipShape(RoundedRectangle(cornerRadius: 8))
TextField("Email", text: $viewModel.user.email)
.padding()
.background(Color(.systemGray6))
.clipShape(RoundedRectangle(cornerRadius: 8))
.keyboardType(.emailAddress)
.textInputAutocapitalization(.none)
if let errorMessage = viewModel.errorMessage {
Text(errorMessage)
.foregroundStyle(.red)
}
Button(action: {
viewModel.updateUser(name: viewModel.user.name, email: viewModel.user.email)
}, label: {
Text("Update User")
.foregroundStyle(.white)
.padding()
.frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/)
.background(.blue)
.clipShape(RoundedRectangle(cornerRadius: 8))
})
}
.padding()
.navigationTitle("User Information")
}
}
#Preview {
UserView()
}
4. MVVM 패턴에서의 상호작용
- View와 ViewModel
- View는 사용자의 입력을 ViewModel에 전달, ViewModel은 데이터 변경사항을 View에 알림
→ @StateObject, @ObservedObject, @Binding 등을 사용하여 View와 ViewModel 간의 데이터 바인딩
- ViewModel과 Model
- ViewModel은 Model에서 데이터를 가져오고, Model에 데이터를 저장
- ViewModel은 Model의 비즈니스 로직을 호출하여 필요한 작업 수행
[Dev_Pathway] Customize views with properties : Structures and Classes (0) | 2024.07.03 |
---|---|
[Dev_Pathway] Hello, SwiftUI (0) | 2024.07.03 |
[Framework] WidgetKit (0) | 2024.06.16 |
[Day18] WeSplit (1) (0) | 2024.06.03 |
[Day17] searchable (0) | 2024.05.21 |
댓글 영역