[아키텍처] MVVM 패턴

Hashswim·2024년 4월 17일
1

1. MVVM 이란?

MVVMMVC에서 파생된 모델로 Model-View-ViewModel의 약자이다.
2005년 Microsoft에서 View가 디자이너의 책임인 개발플랫폼의 진화에 맞춰 MVC를 변형시켜 만들어진 패턴이라고 한다..

  • MVVM 구조

  • Model: 모델은 데이터 모델뿐만 아니라 비즈니스 로직을 포함한 앱의 도메인 모델(추상화된 객체 모델)을 말한다

  • View: 애플리케이션의 유저 인터페이스를 나타내며 보여지는 동작(애니메이션 등 렌더링)만을 수행하는 객체로 모델을 알지 못해 데이터를 가지지도 조작하지도 못한다!
    (오직 뷰모델과의 데이터 바인딩을 통해 동작)

  • ViewModel: View가 데이터 바인딩을 통해 사용하는 속성과 명령을 구현하고 노출한다. 상태 변경이 발생하면 알림 이벤트 등을 통해 View에 알린다

즉, ViewModel은 서로 통신하지 않으며 View는 사용하는 속성과 메서드를 가지고 있는 View Model과만 통신하고, View ModelModel과만 통신하며 ViewModel사이의 통신을 중개한다.



MVVM의 장단점

Pros

  • 디자이너와 협업이 용이하다: View와 프로그래밍 로직이 분리되어 있어 디자이너와 개발자는 서로 다른 구성요소(디자인: View, 개발: ViewModel, Model)에 대해 동시에 작업할 수 있다!

  • UI 테스트가 용이하다: 마찬가지로 View가 완전히 독립적이므로 UI와 비즈니스 로직 각각 테스트할 수 있다.

  • 유지 보수, 기능 추가가 용이하다: 애플리케이션의 구성요소를 작은 단위까지 모듈화해 코드가 직관적이고 이해하기 쉽다.
    -> 유지 보수가 용이하며 새로운 기능을 어디에 구현하고 기존 코드와 어떻게 연결되는지 알기 쉽다.

Cons

  • 비용이 크다: 어플리케이션을 구현하는데 더 많은 작업을 필요로 해 모델을 설계하고 개발하는데 시간이 오래걸리며 복잡해진다.

  • 디버깅이 어렵다: 명령형 코드보다 선언형 코드가 더 디버깅하기 어렵다.. ViewView Model의 느슨한 결합은 비즈니스 로직과 UI간의 연결과 흐름을 파악하고 디버깅하기엔 더 어려울 수 있다.

    UIkit을 사용한 MVVM구조는 ViewController의 이름만 바꿔 ViewModel로 만들어 여전히 Massive하며 데이터 바인딩 과정이 더 복잡하게 만든다고도 한다..!



코드로 구현해 보기 - SwiftUI

MVVM의 핵심인 ViewView Model간의 데이터 바인딩을 하는 방법으로는 KVO, Delegate 패턴, RxSwiftCombine같은 반응형 프레임워크를 사용하는 방법들이 있겠다...

추후 UIkitMVC 구조로 구현된 프로젝트를 MVVM구조로 리팩토링하는 작업을 해볼거지만(포스팅 예정 ㅠ) MVVM의 구조만 구현해보기 위해 SwiftUI와 속성 래퍼를 사용해 간단하게 구현해보자..

생년월일로 나이 계산앱

  • Model
import Foundation

struct Person {
    var birthDay: Date
    var age: Int {
        return Calendar.current.dateComponents([.year], from: birthDay, to: Date()).year!
    }
}
  • View
import SwiftUI


struct ContentView: View {

    @StateObject var viewModel = ContentViewModel()

    var body: some View {
        VStack {
            Spacer()
            Text("나이 계산기")
                .fontWeight(.bold)
                .padding()
            Spacer()
            DatePicker("birthDay",
                       selection: $viewModel.birthDay,
                       displayedComponents: [.date])
                .datePickerStyle(.graphical)
                .labelsHidden()
                .padding()
            Text(viewModel.getAge())
            Spacer()
        }
        Spacer()
    }
}

#Preview {
    ContentView()
}
  • ViewModel
import Foundation

class ContentViewModel: ObservableObject {

    @Published var birthDay = Date()

    func getAge() -> String {
        let person = Person(birthDay: birthDay)
        return "만 \(person.age)세"
    }
}
  • 구현 화면

p.s. 작성하다보니 Date 타입에 대해서 정리를 한번 할 필요가 있는거 같다..(추후 포스팅!)

간단하게 작성하려 했지만 작성하다보니 점점 길어졌는데, computed property로 구현한 부분을 Model자체에 Service를 추가할 수도 있을거 같다.단순히 Age라는 모델이 제공할 기능인지를 명확하게 구분하기 힘들었다..
ViewModel을 알지 못하도록 수정,,


0개의 댓글