상세 컨텐츠

본문 제목

[Swift] MVVM

Swift&SwiftUI

by (방울)도마토 2024. 6. 28. 16:25

본문

MVVM(Model - View - ViewModel)

- 사용자 인터페이스(UI) 코드와 비즈니스 로직을 분리하여 코드의 유지 보수성과 재사용성을 높이는 데 목적 

 

출처 : https://www.maray.ai/de/posts/swift-mvvm-architecture

 

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()
}

 

 

textField에 아무 값도 넣지 않은 경우

 

Email 형식을 지키지 않은 경우

 

두 형식을 모두 지킨 경우 > User Info가 업데이트

 

 

4. MVVM 패턴에서의 상호작용 

- View와 ViewModel 

    - View는 사용자의 입력을 ViewModel에 전달, ViewModel은 데이터 변경사항을 View에 알림 

    → @StateObject, @ObservedObject, @Binding 등을 사용하여 View와 ViewModel 간의 데이터 바인딩 

 

- ViewModel과 Model

    - ViewModel은 Model에서 데이터를 가져오고, Model에 데이터를 저장 

    - ViewModel은 Model의 비즈니스 로직을 호출하여 필요한 작업 수행 

'Swift&SwiftUI' 카테고리의 다른 글

[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

관련글 더보기

댓글 영역