MVVM 구조

ushin20
|2024. 10. 22. 15:24

MVVM = Model, View, View Model

 

1. Model

데이터 저장 구조체이다. 크로스핏 운동 어플을 만든다고 가정하면,

enum ExerciseType {
    case weighted(Double)  // 무게가 필요한 경우 (kg 단위 등)
    case bodyweight         // 무게가 필요 없는 경우
}

struct Exercise {
    let name: String
    let sets: Int
    let reps: Int
    let type: ExerciseType
}

struct WOD {
    let id: Int
    let name: String
    let exercises: [Exercise]
}

 

 

2. View Model

View에서 사용하기 위해 Model의 데이터를 처리하는 과정이다. WOD 단위의 데이터 로드를 하고 싶으면,

import Foundation
import Combine

class WODViewModel: ObservableObject {
    @Published var wodList: [WOD] = []

    init() {
        fetchWODs()
    }

    func fetchWODs() {
        // Mock data 예시
        let sampleWOD = WOD(
            id: 1,
            name: "Hero WOD",
            exercises: [
                Exercise(name: "Deadlift", sets: 5, reps: 10, type: .weighted(100)),
                Exercise(name: "Pull-ups", sets: 3, reps: 15, type: .bodyweight),
                Exercise(name: "Burpees", sets: 4, reps: 20, type: .bodyweight)
            ]
        )
        self.wodList = [sampleWOD]
    }
}

 

 

3. View

ViewModel에서 처리된 데이터를 기반으로 화면을 구성한다.

import SwiftUI

struct WODView: View {
    @ObservedObject var viewModel = WODViewModel()

    var body: some View {
        NavigationView {
            List(viewModel.wodList, id: \.id) { wod in
                VStack(alignment: .leading) {
                    Text(wod.name)
                        .font(.headline)
                    
                    ForEach(wod.exercises, id: \.name) { exercise in
                        HStack {
                            Text(exercise.name)
                            Spacer()
                            Text("\(exercise.sets) sets x \(exercise.reps) reps")
                                .font(.subheadline)
                                .foregroundColor(.gray)
                            
                            if case let .weighted(weight) = exercise.type {
                                Text("(\(weight, specifier: "%.1f") kg)")
                                    .font(.subheadline)
                                    .foregroundColor(.blue)
                            }
                        }
                    }
                }
                .padding(.vertical)
            }
            .navigationTitle("CrossFit WODs")
        }
    }
}

struct WODView_Previews: PreviewProvider {
    static var previews: some View {
        WODView()
    }
}

 

 

이렇게 구성하는 이유는 데이터, 비즈니스 로직, UI를 구별해서 구현 및 유지, 보수, 사용하기 위함이다.