[배달의 민족] 따라만들기 8-2편 (Repository, ViewModel, MVVM)
안녕하세요 허접샴푸입니다~!
오랜만에 배달의 민족 따라 만들기 8편입니다!
이번 시간에 구현할 것은 안드로이드 앱 아키텍처 패턴의 일부인 Repository와 ViewModel을 작성해보겠습니다.
Guide to app architecture
https://developer.android.com/jetpack/docs/guide#common-principles
위 문서를 보다 보면 제일 중요하게 봐야 할 것이 아래 그림입니다.
Activity나 Fragment는 ViewModel에 의존하고
ViewModel은 Repository에 의존하고
Repository는 Room(Sqlite database library)이나 Retrofit 클래스와 같은 여러 다른 클래스에 의존합니다.
이게 무슨 말이냐면, 반대로 생각해보면 ViewModel은 Activity나 Fragment의 코드를 알 수 없습니다.
즉 UI를 관리하는 건 Activity나 Fragment가 하는 것이라면 ViewModel은 UI를 관리할 수 없다는 것입니다.
저 그래프의 예를 들면
Retrofit을 이용하여 서버에서 Member 데이터를 가져와 화면에 표시한다고 가정해 봅시다.
그럼 Activity나 Fragment에서는
1) ViewModel에 있는 함수를 호출합니다. (예: viewModel.getMemberInfo())
2) ViewModel에 있는 getMemberInfo 함수 안에서는 Repository에 있는 함수를 호출합니다. (예: repository.getMemberInfo())
3) Repostiroy에 있는 getMemberInfo 함수 안에서는 Retrofit 서버 통신 관련 함수를 호출합니다. (예: retrofit.getMemberInfo())
4) 서버로부터 값을 받아오면 repository에서는 그 값을 viewModel로 반환해주고 (예: return memberInfo)
5) ViewModel에서는 livedata 변수에 그 값을 세팅해줍니다. (예: _liveData.value = memberInfo)
6) 그리고 ViewModel의 livedata를 observe 하는 Activity나 Fragment에서는 해당 값을 이용하여 화면에 표시해주면 끝입니다.
무언가 설명이 길어졌는데 (저도 사실 허접이라 아직 미숙합니다. 계속 공부하도록 하겠습니다)
[START]
1) 먼저 package들을 아래와 같이 생성해줍니다.
일단 저희는 웹 서버 통신을 하는 앱이 아니기 때문에 Room, Retrofit을 사용하지 않습니다.
그러나 Fake 데이터가 있기 때문에 model이라는 패키지를 생성해줍니다.
더불어 그 Fake 데이터를 불러와야 하기 때문에 repository 패키지 또한 만들어줍니다.
Activity, Fragment, ViewModel은 모두 ui 패키지에 들어갑니다.
(현재까지 완성된 모습)
일단 ui 패키지를 보면 BottomNavigationView에 5개의 Tab 아이템이 있으며, 그 5개에 맞게
a_home (홈)
b_eatwhat (뭐먹지)
c_favorite (찜한가게)
d_order (주문내역)
e_profile (My배민)
로 나눈 것입니다.
아직 a_home 즉 홈 화면 말고 다른 뭐 먹지부터 My배민 화면까지 구현한 것이 없어서 a_home을 제외한 다른 패키지 내부가 비어 보이지만 추후에 만들어나가며 채워나갈 것이니 걱정하지 않으셔도 됩니다.
2) HomeViewModel.kt 작성
예전 (전편)에서 이미 ViewModel을 작성했었는데 이번에도 다른 것은 별로 없습니다.
3) HomeFragment.kt 작성
예전과 바뀐 점은
[1] autoScrollViewPager()
이제는 while(viewLifecycleOwner.lifecycleScope.isActive)를 통해 Loop를 돌리고 있습니다.
viewLifecycleOwner는 fragment의 lifecycle을 관여하는 FragmentViewLifecycleOwner 클래스 객체입니다.
Activity였으면 this.lifecycleScope.isActive였을 것입니다.
Google에서 Fragment에서는 viewLifecycleOwner을 사용하라고 권장하고 있습니다.
아무튼 본론으로 돌아와서, lifecycleScope은 말 그대로 lifecycle 즉 현재 class의 생명이 살아있을 때만 작동하는 코 루틴이기 때문에 isActive 확인을 통해 현재 fragment의 lifecycleScope이 살아있을 때만 while 루프가 true가 되어 돌게 됩니다. 즉 현재 Fragment가 destroy 돼버리면 while 루프는 false가 되어 돌지 않게 되죠.
그리하여 true 일 때만 delay(3000) 3초 딜레이를 준 다음, homeViewModel.setCurrentPosition을 해줍니다.
그럼 currentPosition이 3초마다 바뀌게 될 것이며 이를 아래와 같이 observe 하고 있다가 viewPager2의 현재 Item을 currentPosition으로 세팅합니다.
[2] 위 코드에서도 알 수 있듯이 Fragment에서는 viewLifecycleOwner을 사용합니다.
기존 Activity에서는
liveData.observe(this, Observer {})
형태였다면, Fragment에서는
liveData.observe(viewLifecycleOwner, Observer {})
를 사용합니다. viewLifecycleOwner는 Fragment에서! 이 부분에 대해서는 시간이 될 때 더 자세히 알아보고 따로 포스팅하도록 하겠습니다.
4) Repository 작성
[1] HomeRepository Interface 작성
[2] HomeRepositoryImpl.kt 작성
HomeRepository interface와 HomeRepositoryImpl 클래스로 나눈 이유는
1) 가독성을 높여준다. Repository가 어떻게 작성되어 있는지 interface만 봐도 대충 알 수 있게 된다.
2) Android Testing을 할 때 용이하다. (Mock 할 때 편하다고 합니다. 저도 이 부분은 더 알아봐야 할 것 같습니다.)
아무튼 HomeRepositoryImpl은 object로 작성하여 싱글턴 클래스로 만들어줍니다. 매번 해당 클래스를 생성해서 함수를 호출하는 것은 비효율적이기 때문입니다.
suspend가 붙는 이유는 저희는 coroutine을 사용하기 때문입니다.
override가 붙는 이유는 HomeRepository Interface를 상속받았기 때문입니다.
각 함수는 fakeBannerItemList와 fakeGridItemList를 return 합니다.
(HomeViewModel)
위 HomeViewModel에서는 HomeRepositoryImpl.getBannerItems()로 fakeBannerItemList를 받아오고 있습니다. 물론 suspend fun이기 때문에 coroutineScope 안에서 실행되어야 하며 viewModel 내부이기 때문에 viewModelScope을 사용합니다.
HomeRepository에서 가져온 fakeBannerItemList를 MutableLiveData인 _bannerItemList에 할당해줍니다.
참고로 withContext(Main) 스코프 안에서 값을 할당해주는 이유는 "setValue(T)" 메쏘드는 main thread에서 호출되어야 한다고 구글 Livedata 문서에 나와있기 때문입니다.
(HomeFragment)
HomeViewModel에서 값을 할당하면 bannerItemList liveData를 관찰하고 있는 HomeFragment.kt에서는 해당 리스트 값을 viewPagerAdapter로 넘겨주어 자동으로 스크롤되는 배너가 완성되는 것입니다.
[결과 화면]
[Youtube] 이해를 돕기 위해 직접 보며 따라 할 수 있는 영상 또한 준비하였습니다.
https://www.youtube.com/watch?v=XEybIfwSxWo&t=46s
[Github] 코드를 다운로드하여서 실행해보시기 바랍니다.
https://github.com/DJDrama/BaeminPractice/tree/Migrating-To-Navigation-Part2
사실 MVVM의 꽃은 DataBinding이라고 하더군요. 그래서 위는 Android MVVM이라 할 수 없지만 일단 맛보기라고 보면 될 것 같습니다.
IOS도 개발해보고 Android도 개발해봤지만 Android가 훨씬 어렵고 공부해야 할 것이 많은 것 같습니다.
아무튼 공부할 때 모르는 것이 있으면 물어볼 곳도 없었는데, 저와 같은 사람이 혹시 있을까 봐 블로그에 글을 남기며 많은 도움이 되면 좋겠습니다.
다음 편부터는 나머지 뭐먹지 ~ My 배민까지 완성하도록 하겠습니다.
그럼 다음 편에서 봐요~!
이 포스트가 좋으면 공감(하트) 부탁드립니다.
모든 댓글과 질문은 늘 환영합니다!!