MVVM 패턴에 대하여

Updated:

  • MVVM

    • 안드로이드에 대해 더 공부하고 개발하다 보니 현재 다양한 곳 에서 쓰이고 있는 MVVM 패턴에 대해서 정리해보고자 한다. 처음 접한 것은 코틀린으로 쇼핑몰 앱 만들기 라는 책에서 처음 접해봤으며 한 Fragment나 Activity에 모든 기능과 소스들을 때려박았던 것 보다 훨씬 유용하다고 생각했고 좀 더 자세히 공부해 보고 싶어서 글을 쓰게 되었다.

      아직 많이 미숙하지만 내가 여러 블로그와 구글링을 해서 얻어온 정보를 정리하고자 하는 것이니, 누군가 이걸 본다면 그냥 참고용으로만 봐 주었으면 좋겠다.

      앱 아키텍처는 나중에 다뤄 보고 일단은 MVVM에 대해서 정리해보겠다.

  • MVVM - Model-View-ViewModel의 줄임말, 하나의 소프트웨어를 최대한 작은단위로 나누어 유지보수가 용이하게 만든 구조.

    Input들은 View로 전달되어, ViewModel은 Input에 해당되는 Logic을 처리, View에 data를 전달하는 방식이고 ViewModel과 View는 독립적이며 ViewModel : View = 1:N 관계를 가진다. -> View가 자신이 사용할 ViewModel을 선택해 바인딩하여 받는 형태이다.

    나중에 Model이 상태 및 데이터들이 변경될 때 해당 ViewModel을 사용하는 View가 자동적으로 업데이트 됨.

    MVP와 마찬가지로 M-V사이에 의존성이 없고, V-VM이 독립적이다.

  • View - UI를 담당하는 Activity나 Fragment 등을 정의. 화면에 나타나는 것이고, 사용자와 상호작용한다.

    class ProductMainActivity : BaseActivity<ProductMainViewModel>() {
      
        override val viewModelType = ProductMainViewModel::class
        private val ui by lazy { ProductMainUI(getViewModel()) }
      
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            ui.setContentView(this)
            ui.viewPager.adapter =
                ProductListPagerAdapter(supportFragmentManager)
            ui.tablayout.setupWithViewPager(ui.viewPager)
            setupDrawerListener()
        }
      
        private fun setupDrawerListener() {
            val toggle = ActionBarDrawerToggle(
                this,
                ui.drawerLayout,
                ui.toolbar,
                R.string.open_drawer_description,
                R.string.close_drawer_description
            )
            ui.drawerLayout.addDrawerListener(toggle)
            toggle.syncState()
        }
    }
    
  • ViewModel - View에 연결된 데이터 및 관련 로직이 들어가고, 데이터를 잘 가공하여 View에 뿌리기 쉬운 Model로 바꾸는 역할을 하고, 변경알림 이벤트를 통해 상태 변경을 View에게 알림. 또한 View와 분리 되어있기 때문에 View가 Destroy 되어 다시 Create가 되어도 데이터를 여전히 가지고있는다.

    class ProductMainViewModel(app: Application) : BaseViewModel(app) {
      
        fun openSearchActivity(keyword:String?){
            keyword?.let{
                startActivity<ProductSearchActivity>{
                    putExtra(ProductSearchActivity.KEYWORD, keyword)
                }
            }?:toast("검색 키워드를 입력해주세요.")
        }
      
        fun openRegistrationActivity() {
            startActivity<ProductRegistrationActivity>() { flags = Intent.FLAG_ACTIVITY_SINGLE_TOP }
        }
    }
    
  • Model - 사용하려는 데이터를 가지는 클래스로 DTO, POJO, ENTITY 개체 등이 있다. 일반적으로 데이터를 액세스 하거나 캐싱이 필요한 Service or Repository와 함께 사용됨. (Source Code - Category, ViewModel)

    • 예시 : Category 의 categoryList를 ProductRegistrationViewModel에서 사용하는 예시
    class ProductRegistrationViewModel(app: Application) : BaseViewModel(app){
    //...
    	val categories = MutableLiveData(categoryList.map { it.name })
    	var categoryIdSelected: Int? = categoryList[0].id
      
    //...
      fun onCategorySelect(position: Int) {
            categoryIdSelected = categoryList[position].id
        }
    //...
    }
      
      
      
    //Catogory.kt
    data class Category(
        val id: Int,
        val name: String
    )
      
    val categoryList = listOf(
        Category(0, "여성의류"),
        Category(1, "남성의류"),
        Category(2, "가전제품"),
        Category(3, "생활용품"),
        Category(4, "도서"),
        Category(5, "반려동물용품"),
        Category(6, "식품")
    )
    
  • 결론

    • 장점 : 각 사이의 의존성이 없어서 유지 보수가 용이 한것이 사실이고, 변수들이 제대로 설정되어있는지 여부도 확인하기 용이하다. 또한 중복 코드를 모듈화 하여 효율적으로 코딩이 가능하다.
    • 단점 : ViewModel 설계가 어려운것 같다. 하지만 사실 내가 코딩 실력이 부족해서 어렵게 느껴지는 것 같다.
    • 앞으로 추가적인 안드로이드 디자인 패턴에 대해 공부를 해야겠지만 여러 디자인 패턴중에 이것이 완벽한 정답이다 라고 느껴지는 건 없는 것 같다. MVC, MVP 패턴을 앞으로 포스팅하며 잘 익혀봐야할 것 같고 MVVM 구조를 활용한 추가적인 예제 어플을 제작하기로 했다.