ViewModel에 대해서

Assist·2023년 6월 6일
0

Android

목록 보기
21/21

회사 관사로 복귀 하는날이 왔습니다..... 일하러 가기 싫네요....

저번에 Android Repository 을 Naver 검색 api 에 적용을 해봤습니다.

그럼 이제 Repository에서 데이터를 끌어왔으니 이제 ViewModel에 대해서 공부를 하는 긓을 써 보려고 합니다.

ViewModel은 무엇인가?

안드로이드 폰 , 아이폰 이던 둘다 같이 있는 기능 화면 전환입니다.

안드로이드는 화면전환을 할때 view을 다시 그리는 역활을 했습니다.

그럼 아무것도 안했을 상태였을시는 아무 상관이 없을텐데
메모장 앱을 사용중에 글을 중간까지 썼다가 화면 전환이 되면 이전 text은 다 날아가게 됩니다.

그래서 해결법은

  • 안드로이드 화면전환 막기
  • savedInstanceState 사용 하는겁니다.,

그런데 saveInstanceState 데이터를 복원할때 key값 을 통해 데이터를 다시 복원하지만 String , Int 등의 데이터는 복원하는데 UI 가 빠르게 복원을 할수 있지만 비트맵 같은 이미지는 UI 복원하는데 시간이 조금 걸릴수 있습니다.

그래서 나온게 ViewModel입니다.

ViewModel은 데이터를 메모리에 저장하고 Acitivity, Fragment 의 생명주기와는 다르기때문에 독립적으로 데이터를 들고 있을수 있습니다.

그러니 따로 Key value 로 데이터를 복원할필요가 없고 그냥 ViewModel에 저장되있는 데이터를 가져오면 되는것입니다.

ViewModel에 생명주기 관리

버튼식으로 해서 데이터를 가져오고 항상 ViewModel 에 데이터를 확인해서 View에 전시한다면 상관은 없겠지만
어떤 화면에 들어올때마다 데이터를 요청하면 자칫 잘못하다간 중복된 데이터가 view에 쌓여서 이상한 버그가 나올수 있습니다
(저도 알고 싶지는..하...)
그럼 ViewModel은 생명주기는 어떻게 하는가?

제 Naver 검색 api 코드를 보겠습니다.

    private var _viewModel : SearchViewModel ? = null
    private val viewModel get() = _viewModel!!
    
    private fun initViewModel(){
        val factory = SearchViewModelFactory(RepositoryImpl())
        _viewModel = ViewModelProvider(this , factory)[SearchViewModel :: class.java]
    }
    
        override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "The Search View is Destory")
        _binding = null
        _viewModel = null
    }

위에서 부터 설명하자면 viewModel은 _viewModel 을 get해서 초기화를 하며
InitViewModel이라는 클래스에서 의존성을 주입합니다 .

onDestory 에서 view가 파괴되었을시 viewModel을 Null 처리 함으로서 메모리 누수를 방지합니다.

 viewModel.bookSearchLiveData.observe(viewLifecycleOwner , Observer {
            when(it.status){
                Resource.Status.LOADING ->{
                    Log.d(TAG , "Loading for search Book")
                }
                Resource.Status.SUCCESS ->{
                    Log.d(TAG , "Success to get Book")
                    val result = it.data!!
                    adapter.data = result
                    binding.recyclerviewBook.adapter = adapter
                }
                Resource.Status.ERROR ->{
                    Log.e(TAG , "Error to get Book")
                    Toast.makeText(requireContext() , getString(R.string.msg_check_network) , Toast.LENGTH_SHORT).show()
                }
                Resource.Status.FaIL ->{
                    Log.d(TAG , "Fail to get Book")
                    Toast.makeText(requireContext() , it.message , Toast.LENGTH_SHORT).show()
                }
            }
        })

이 코드를 보시면 viewModelProvider 로 인하여 viewModel 의 데이터는 fragmet 에서만 변환을 감지합니다.

근데 여기서 글을 쓰다가 재미있는것을 발견했습니다.

제가 위해서 ViewModelProvider을 쓸때 의존성 주입을 했다고 했습니다.

근데 google에서 이것을 별로 안좋게 생각을 했나봅니다
이유는

  • 의존성 주입을 위한 ViewModelFactory등의 클래스 증가
  • 그로인한 유지보수성의 어려움

음 충분히 그럴수 있다고 생각되는 글입니다.

그럼 해결법은?

의존성(DI) 주입

이것이 무엇인고 하고 보다보니 이전 F-LAB 멘토님께서 제 코드 리뷰를 하셨을때 잠깐 지나가신 말투로 "의존성 주입에 대해서 공부후 적용할지를 판단해보세요"
라고 한 기억이 있습니다 .

그래서 그게 무엇인고 보니

@HiltViewModel
class SearchViewModel @Inject constructor( private val repository : RepositoryImpl) : ViewModel() {

여기서 @로 되있는게 의존성 주입이라고 합니다
이것이 viewModelFactory등을 사용안하고 viewModel을 사용할수 있다고 합니다.

조금더 공부를 해봐야겠습니다.

그럼 다음글은 Android 의존성(DI)에 대해 공부를 해보겠씁니다.

읽어주셔서 감사합니다

-피드백와 비반은 언제나 환영입니다-

profile
안드로이드만 좋아하는 특이한 개발자

0개의 댓글