최데브는 오늘도 프로그래밍을 한다.

MVVM 패턴 - 설명(1) (View) 본문

Android

MVVM 패턴 - 설명(1) (View)

최데브 2021. 2. 11. 01:52
반응형

MVVM 패턴은 사용하는 사람들마다 조금씩 만드는 방식이 달랐다.

여러 방식들을 보고 그 중에서 가장 개인적으로 마음에 드는 방식의 예제를 보고

따라하며 이해했다. 

 

 

이번 공부에는 LiveData , RxJava , Retrofit2, Databinding 이 사용되었다.

사전 지식이 없다면 이해하기 힘들 수 있다. 

 

먼저 View 파트부터 설명 해보겠다.

 

View 는 전에도 말했듯 유저가 클라이언트랑 소통하는 화면을 의미한다.

안드로이드에서는 Activity 를 말하겠다. MVVM 패턴에서의 View는 ViewModel에서 변경되는 값들을

Observer 패턴으로 관찰하며(이 프로젝트에서는 LiveData가 이를 쉽게 해결한다.) 변경되는 값들을 그대로 바꿔서 보여준다.

 

일단 나의 경우는 모든 Activity 에서 공통적으로 들어갈 요소들을 따로 빼내서 중복되는 코드를 줄이기 위해

BaseActivity.kt 라는 파일을 만들어줬다. 이 클래스는 모든 Activity 의 상위 클래스가 된다.

 

BaseActivity.kt 

abstract class BaseActivity<T : ViewDataBinding, R : BaseViewModel> : AppCompatActivity() {

    lateinit var viewDataBinding: T

    /**
     * setContentView로 호출할 Layout의 리소스 Id.
     * ex) R.layout.activity_sbs_main
     */
    //BaseKotlinActivity 상속받는 구현 액티비티마다 get 하는 방식으로 넣어주면 된다.
    //onCreate에서 레이아웃 리소스를 설정해줘야하는데, BaseKotlinActivity에서 onCreate를 override해버리니
    //BaseKotlinActivity를 구현한 액티비티는 레이아웃 리소스를 설정해 줄 곳이 없어서 이렇게 만들었음.
    //  override val layoutResourceId: Int
    //        get() = R.layout.activity_main 이런 느낌으로 사용하면 된다.
    abstract val layoutResourceId: Int

    /**
     * viewModel 로 쓰일 변수.
     */
    abstract val viewModel: R

    /**
     * 레이아웃을 띄운 직후 호출.
     * 뷰나 액티비티의 속성 등을 초기화.
     * ex) 리사이클러뷰, 툴바, 드로어뷰..
     */
    abstract fun initStartView()

    /**
     * 두번째로 호출.
     * 데이터 바인딩 및 rxjava 설정.
     * ex) rxjava observe, databinding observe..
     */
    abstract fun initDataBinding()

    /**
     * 바인딩 이후에 할 일을 여기에 구현.
     * 그 외에 설정할 것이 있으면 이곳에서 설정.
     * 클릭 리스너도 이곳에서 설정.
     */
    abstract fun initAfterBinding()

    private var isSetBackButtonValid = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //일반적인 setContentView 와는 다르지만 데이터바인딩에서는 이렇게하면 된다.

        viewDataBinding = DataBindingUtil.setContentView(this, layoutResourceId)

        initStartView()
        initDataBinding()
        initAfterBinding()
    }
}

이렇게 정리를 해두면 좀 더 코드를 줄일 수 있다는 장점이 있다. 액태비티디가 적은 경우에는 굳이 이렇게 하지 않아도 될거 같다.

 

 

class MainActivity : BaseKotlinActivity<ActivityMainBinding, MainViewModel>() {
    override val layoutResourceId: Int
        get() = R.layout.activity_main

    override val viewModel: MainViewModel by viewModel() // Koin 으로 의존성 주입
    private val mainSearchRecyclerViewAdapter: MainSearchRecyclerViewAdapter by inject()

    override fun initStartView() {
        main_activity_search_recycler_view.run {
            adapter = mainSearchRecyclerViewAdapter
            layoutManager = StaggeredGridLayoutManager(3, 1).apply {
                gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
                orientation = StaggeredGridLayoutManager.VERTICAL
            }
            setHasFixedSize(true)
        }
    }

    override fun initDataBinding() {
        //View에서 View 모델로는 아래처럼 의존성이 있다. 그런데 observe 를 사용하기 때문에 반대로는 의존성이 없어도 된다.
        viewModel.imageSearchResponseLiveData.observe(this, Observer {
            it.documents.forEach {document ->
                mainSearchRecyclerViewAdapter.addImageItem(document.image_url, document.doc_url)
            }
            mainSearchRecyclerViewAdapter.notifyDataSetChanged()
        })
    }

    override fun initAfterBinding() {
        main_activity_search_button.setOnClickListener {
            viewModel.getImageSearch(main_activity_search_text_view.text.toString(), 1, 80)
        }
    }

}

 

앞에서 작성한 Base액티비티를 상속하는 Main 액티비티 코드다. 

KOIN 라이브러리를 이용해서 MainViewModel 의존성을 주입받아서 쉽게 viewModel 로 접근한다. 

주석에도 적혀있듯 View에서 View 모델로는 의존성이 있다. observer로 관측하는 부분인데 

이게 viewModel에서는 liveData로 작동하기 때문에 데이터바인딩이 자동으로 되기때문에 역순으로의 의존성은 필요없어져서 자동으로 해결된다. 이게 MVVM 의 의존성 해결 방법이다.

 

initDataBinding 에서imageSearchResponseLiveData 를 observe 하고 그 결과를 어뎁터에 값이 변경될때마다 넣어주는 방식이다.  즉 계속 관찰하고 있는데 변경이 되면 livedata 이기 때문에 변경된것이 자동으로 데이터바인딩되어 view로 알려주고 그 변경값을 옵저버가 보고 있기 때문에 바로바로 반영이 가능해진다.  라는 식의 흐름이다.

 

다음 포스팅에서는 VIewModel 쪽 코드를 자세히 알아보자.

 

 

반응형
Comments