MVVM Pattern

안드로이드 개발을 하면서 가장 많이 접했던 MVVM 패턴에 대하여 기록하였습니다.

</aside>

MVVM

  • MVVM은 안드로이드 개발에서 많이 사용되는 아키텍처 패턴 중 하나로, Model, View, ViewModel로 구성되어 있습니다.
  • 이 패턴은 코드의 재사용성, 테스트 용이성, 유지보수성을 높이기 위해 설계되었으며, UI 로직과 비즈니스 로직을 분리하여 복잡한 프로젝트에서도 모듈화가 가능하게 해줍니다.

MVC와 MVP에서 View와 Model간의 의존성은 없었지만, Controller나 Presenter와 View 사이에 의존성은 여전히 존재합니다. MVVM에서 ViewModel을 도입하여 ViewModel이 View를 참조하지 않으므로 의존성을 제거할 수 있습니다. 비즈니스 로직과 프레젠테이션 로직을 UI로 부터 분리하는 목표를 위해 설계되었습니다.

  • ViewModel은 독립적인 존재라는 뜻으로, ViewModel과 View는 1: n 관계입니다.

의존성 분리

  • 역할만 보면 다른 아키텍처 패턴의 Controller나 Presenter와 유사할 수 있지만, LiveData와 DataBinding을 활용한 View와의 의존성 분리 이점을 얻을 수 있습니다.

Model

  • 데이터 계층을 관리하며, 데이터베이스, 네트워크 통신, 비즈니스 로직 등이 포함됩니다.
  • ViewModel이 필요로 하는 데이터를 제공하며, 데이터를 저장하고 갱신하는 역할을 합니다.
  • 데이터 소스는 로컬 데이터베이스, 웹 서비스 또는 캐시일 수 있습니다.

View

  • 사용자와 상호작용하는 UI 계층 레이어입니다.
  • Activity, Fragment, XML 레이아웃이 포함되며 ViewModel의 상태 변화에 따라 UI를 갱신합니다.

ViewModel

  • View와 Model 간의 중재자 역할을 합니다.
  • Model에서 데이터를 가져와 UI에 필요한 형태로 변환하고, LiveData나 StateFlow와 같은 데이터 구조를 사용해 View와 데이터를 연결합니다.
  • UI 상태를 유지하며, 생명 주기에 맞게 데이터를 관리합니다.

AAC ViewModel vs MVVM ViewModel

  • AAC : Android Architecture Components
  • 두 개념 모두 안드로이드에서 MVVM 패턴을 구현할 때 사용하는 ViewModel 개념입니다.

AAC ViewModel은 안드로이드 아키텍처 컴포넌트에서 제공하는 ViewModel 클래스이고, MVVM ViewModel은 MVVM 패턴에서 View와 Model 간의 중재자 역할을 합니다.

생명주기

  • AAC ViewModel은 Activity나 Fragment의 생명 주기와 무관하게 데이터를 유지하고, LiveData와 함께 UI를 자동으로 갱신합니다.
  • MVVM ViewModel은 MVVM 패턴에 맞춰 설계된 비즈니스 로직과 데이터를 처리합니다.

안드로이드 종속성?

  • AAC ViewModel은 안드로이드 아키텍처 컴포넌트의 일부분으로 안드로이드에 종속되어 있습니다.
  • 반면에 MVVM ViewModel은 특정 플랫폼에 종속되지 않으며, 안드로이드와 무관합니다.

유연성

  • AAC ViewModel은 안드로이드에 최적화된 생명 주기 관리를 제공하며, ViewModel 객체가 생명 주기와 독립적으로 데이터를 관리합니다.
  • 반면에 MVVM ViewModel은 AAC ViewModel 외에도 다양한 형태로 구현이 가능하며, 생명 주기 관리는 수동으로 처리해야할 수도 있습니다.

MVVM 패턴의 동작 흐름

  • View는 사용자의 입력을 받고, 그 입력을 ViewModel에게 전달합니다.
  • ViewModel은 Model에 요청하여 데이터를 가져오거나 업데이트 합니다.
  • Model은 네트워크, 로컬 데이터베이스 또는 다른 데이터를 처리한 후 ViewModel에 결과를 반환합니다.
  • ViewModel은 LiveData, StateFlow 등을 통해 View에 변경된 데이터를 알림으로써 UI를 갱신합니다.

Model(Data Layer)

data class User(val id: Int, val name: String)

class UserRepository {
    fun getUserData(): LiveData<User> {
        // 예시로 로컬 데이터베이스 또는 네트워크 호출을 통해 데이터를 가져옴
        val user = MutableLiveData<User>()
        user.value = User(1, "John Doe")
        return user
    }
}

ViewModel

class UserViewModel(private val userRepository: UserRepository) : ViewModel() {
    
    val user: LiveData<User> = userRepository.getUserData()
    
    // 추가 로직: 데이터 처리, 유효성 검사, UI에 맞춘 데이터 가공 등
}

View

class UserActivity : AppCompatActivity() {
    
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        // ViewModel 초기화
        viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        
        // ViewModel의 데이터를 관찰하여 UI 갱신
        viewModel.user.observe(this, Observer { user ->
            // UI 업데이트
            findViewById<TextView>(R.id.textViewUserName).text = user.name
        })
    }
}

UI와 로직을 분리하여 유지보수가 쉽고 UI와 데이터 로직을 독립적으로 관리할 수 있게 되었습니다. 여러 View에서 같은 ViewModel을 재사용할 수 있으며, 로직 분리로 인해서 테스트 용이성도 증가하였습니다. 또한 ViewModel이 Activity나 Fragment가 재생성되어도 데이터를 유지하여 데이터 손실을 방지하였습니다.

정리

  • MVVM 패턴은 안드로이드 애플리케이션에서 UI와 비즈니스 로직을 명확히 분리하여, 유지보수와 테스트를 더 쉽게 해줄 수 있게 합니다.
  • 반면에 구현이 복잡하고, 앱이 커질수록 양방향 DataBinding이 복잡해질 수 있습니다.
  • 앱에 많은 model - to - view 변화가 필요한 경우에 좋은 디자인 패턴이 될 수 있지만, 과해질수도 있으므로 적절한 아키텍처 패턴을 설계해야 합니다.

참고

https://www.kodeco.com/34-design-patterns-by-tutorials-mvvm