Android, iOS

[MVVM 정복] 7. 완성한 코드로 MVVM 분석해보기

미스터머글 2020. 5. 18. 17:52
728x90
반응형

완성한 코드는 다음과 같다. (내가 드디어 해냈다. 캄.동.)

 

https://github.com/conquerex/mvvm-template

 

conquerex/mvvm-template

MVVM 학습과 앞으로 활용을 위한 템플릿. Contribute to conquerex/mvvm-template development by creating an account on GitHub.

github.com

앞으로 개발을 한다면 다음과 같은 시나리오일 것이다. (일종의 복습)

 

class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
	
    // 생략
    
    override val viewModel: MainViewModel by viewModel()
    
    // 생략
    
    override fun initDataBinding() {
        viewModel.imageSearchResponseLiveData.observe(this, Observer {
            it.documents.forEach {document ->
                mainSearchRecyclerViewAdapter.addImageItem(document.image_url, document.doc_url)
            }
            mainSearchRecyclerViewAdapter.notifyDataSetChanged()
            main_activity_search_button.isClickable = true
        })
    }
    
    // 생략

}

MainActivity를 다시 만든다고 해보자. 만들어 둔 BaseActivity를 상속받을 것이다. MainActivity는 MainViewModel에 종속적이다. ViewModel이 가지고 있는 LiveData로 변화를 감지해 리사이클러뷰를 갱신한다.

 

class MainViewModel(private val model: DataModel) : BaseViewModel() {
	
    // 생략
	
    fun getImageSearch(query: String, page: Int, size: Int) {
        addDisposable(model.getData(query, KakaoSearchSortEnum.Accuracy, page, size)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                it.run {
                    if (documents.size > 0) {
                        Log.d(TAG, "documents : $documents")
                        _imageSearchResponseLiveData.postValue(this)
                    }
                    Log.d(TAG, "meta : $meta")
                }
            }, {
                Log.d(TAG, "response error, message : ${it.message}")
            })
        )
    }
}

MainViewModel은 Model에 종속적이다. 생성자의 인자에 DataModel을 넣은 것은 의존성 주입을 위한 것이다. 이렇게 해서 ViewModel과 Model 그리고 View, 각각의 독립성을 최대한 보장한다.

 

  • subscribeOn : 구독(subscribe)에서 사용할 스레드를 지정
  • observeOn : Observable이(이 경우에는 Single) 다음 처리를 진행할때 사용할 스레드를 지정
  • subscribe : 동작시키기 원하는 것을 사전에 정의해 둔 다음 실제 그것이 실행되는 시점을 조절. 실제 데이터 발행 (이 경우엔 ImageSearchResponse)

ImageSearchResponse가 들어오면 LiveData인 _imageSearchResponseLiveData에 값이 들어오게 되고 뷰에서는 이를 감지하여 화면을 갱신하게 된다.

 

BaseViewModel를 보면, addDisposable로 구독을 해 주면 액티비티가 Finished되는 시점에 ViewModel도 onCleared()되면서 한번에 구독을 취소하게 된다. 아래 이미지를 보면 이해하기 더 쉬울 것이다.

Model은 ViewModel이 가지고 갈 수 있는 데이터를 발행한다. (발행 맞나??) 그런데 MainViewModel의 model.getData(...)에서 model은 Interface다. 실제 동작은 DataModelImpl에서 하는데 말이다. 이때도 역시 DI가 제역할을 하기에 가능한 구조이다.

 

/**
 * MyModule.kt
 */
 
var modelPart = module {
    factory<DataModel> {
        DataModelImpl(get())
    }
}

var retrofitPart = module {
    single<KakaoSearchService> {
        Retrofit.Builder()
            .baseUrl("https://dapi.kakao.com")
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(KakaoSearchService::class.java)
    }
}

 

MyModule에서 의존성을 세팅해 두었다.  DataModel에 의존성이 주입되는 순간마다 DataModelImple이 생성된다. 그런 의존성 주입이 되는 순간이 언제일까? MainViewModel을 다시 보자.

 

class MainViewModel(private val model: DataModel) : BaseViewModel() {...}

생성자에 DataModel이 존재한다. 여기에 접근할때마다 MyModule(위쪽 MyModule 참조)에서 DataModelImpl이 get()을 호출하면 타입추론으로 KakaoSearchService 객체를 주입받아 DataModelImpl을 생성하게 된다. 그러면 KakaoSearchService는 어떻게 생성될까? DataModelImpl 클래스의 생성자에 KakaoSearchService가 존재하고 다시 MyModule에서 의존성을 주입받아 네트워크 모듈을 생성하게 된다. (여기 상당히 헷갈린다. 혹시 내가 잘못 설명했다면 언제든 태클 플리즈)

 

이렇게 하면 서버로부터 데이터를 받아오고 데이터를 받으면 Model에 담기고, Model은 ImageSearchResponse를 리턴하여 ViewModel이 이를 LiveData에 담아 View가 감지할 수 있도록 한다. View는 ViewModel로 부터 발행된 데이터를 감지하여 Databinding을 통해 레이아웃을 갱신한다.

 

* * * * *

 

Rx, 데이터바인딩 등등. 아직 더 공부를 해야하지만 적어도 최소한의 MVVM 구조를 한바퀴 돌려본 느낌이다. 이제 부족한 부분을 더 심도깊게 살펴보고자 한다. (좀 쉬었다가... 아이고 지친다.) 

 

MVVM을 공부하기 위해서 참고한 코드
- https://github.com/Nexters/MNT_Android
- https://github.com/5seunghoon/Kotlin-MVVM-Sample (⭐️추천)
- https://github.com/skydoves/TheMovies2
- https://github.com/iam1492/mvvmsample
- https://github.com/hongbeomi/HarryPotter
추가로 공부해야 하는 것들
- 리액티브 프로그래밍 (Rx)
- 바인딩 어댑터
- Repository 패턴
- Observer 패턴
참고한 블로그
- https://deque.tistory.com/109?category=984011
- https://poqw.github.io/about_mvvm/  (⭐️추천)
- https://medium.com/hongbeomi-dev/aac%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-mvvm-pattern%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0-1d6d73689bd0

 

728x90
반응형