간장 공장 공장장은 간공장장이고 뷰모델 공장 공장장은 ...

김태영·2024년 8월 6일
0

TIL

목록 보기
63/70
post-thumbnail

오늘 공부한 것

- 알고리즘 문제 풀이
- 앱개발 심화 주차 특강 듣고 정리
- 컴포즈 녹강 보고 정리

ViewModel에 매개변수를 전달해야 할 때

기존에 나는 다음과 같이 매개변수의 초기값을 지정해주는 방식으로 사용해왔었다.

// 냅다 레포지토리 객체를 만들어서 초기값으로 대입해주었다. 
class SearchViewModel(private val repository: SearchRepositoryImpl = SearchRepositoryImpl()) : ViewModel()

그 이유는 ViewModel 객체를 생성할 때 매개변수를 못 전달하기 때문이다. ViewModel은 액티비티나 프래그먼트가 죽어도 데이터를 유지하고 관리하기 위해 계속 살아있는 특성을 가진다. 따라서 액티비티가 죽었을 때, 뷰모델을 토대로 다시 액티비티를 만들 수 있게 하기 위해서 빈 생성자를 갖고 있어야 한다고 한다.

// O
private val searchViewModel: SearchViewModel by viewModels<SearchViewModel>

// X 
// 넘겨주지 못한다...
private val searchViewModel: SearchViewModel by viewModels<SearchViewModel>(SearchRepositoryImpl())

ViewModel 객체는 어떻게 얻을 수 있을까?

ViewModel 객체를 얻는 방식에 대해 알아보려고 했는데 문서에 너무 예쁘게 잘 나와있어서 올려본다. 내가 위에서 사용한 건 Fragment의 by viewModels 이다.

그리고 정말 친절하게도 IDE에서 마우스 오버를 하면 팝업으로 매우매우 잘 알려주고 있다. (물론 영어임)

Returns a property delegate to access ViewModel by default scoped to this Fragment:

class MyFragment : Fragment() {     
		val viewmodel: MyViewModel by viewModels() 
}

Custom ViewModelProvider. Factory can be defined via factoryProducer parameter, factory returned by it will be used to create ViewModel:

class MyFragment : Fragment() {      
		val viewmodel: MyViewModel by viewModels { myFactory } 
}

Default scope may be overridden with parameter ownerProducer:

class MyFragment : Fragment() {     
		val viewmodel: MyViewModel by viewModels ({requireParentFragment()}) 
}

This property can be accessed only after this Fragment is attached i. e., after Fragment. onAttach(), and access prior to that will result in IllegalArgumentException.

뭐 대충 Fragment의 ViewModel을 사용하고 싶으면 다음과 같이 사용해라~~ 정도의 의미같다. 여기서 봐둬야 할 건 두 번째의 Factory이다. 내가 아는 Factory는 공장이란 뜻 밖에는 모르는데.. 도대체 뭐하는 자식일까?

ViewModelFactory

나는 아직 MVVM에 통달한 사람이 아니기 때문에 정말 간단히만 알아보았다. 우선, 위에서도 계속 나왔듯이 ViewModel을 생성할 땐 매개변수를 전달할 수 없다. 그렇기 때문에 Factory를 사용해서 매개변수를 ViewModel에 전달하는 것이다!

매개변수 전달 외에도 다양한 초기 상태나 구성을 가진 ViewModel 인스턴스를 여러 개 생성해야 할 때도 사용할 수 있다고 한다.

그럼 직접 매개변수를 전달하는 것과는 어떤 차이점이 있을까?

안드로이드는 ViewModelFactory를 사용해 ViewModel을 생성하게 된다. 이렇게 생성된 ViewModel은 안드로이드가 알아서 캐싱하고 관리한다고 한다. 따라서 액티비티나 프래그먼트가 죽었던 살았던 다시 만들어졌던 새로운 ViewModel 인스턴스를 생성하는 게 아니라 이전에 생성한 인스턴스를 유지할 수 있게 된다.

정말 말 그대로 ViewModel의 인스턴스를 만드는 공장 역할을 하는 것이다!

사용해보자

ViewModelProvider의 Factory 클래스를 상속받아 구현하면 된다.

class SearchViewModelFactory : ViewModelProvider.Factory {
    private val repository = SearchRepositoryImpl(NetworkClient.kakaoNetwork)

    override fun <T : ViewModel> create(
        modelClass: Class<T>,
        extras: CreationExtras
    ): T = SearchViewModel(
        repository
    ) as T
}

👀 ViewModelProvider?

ViewModelProvider를 통해 ViewModel 인스턴스를 요청하고, ViewModelProvider는 ViewModelStoreOwner를 참조해 ViewModelStore를 가져온다.

이미 생성된 ViewModel 인스턴스가 있다면 해당 인스턴스를 요청하고, 아니라면 Factory를 통해 인스턴스를 생성한다. — 참고

사용할 때는 다음처럼 사용하면 된다.

private val searchViewModel: SearchViewModel by viewModels<SearchViewModel> {
    SearchViewModelFactory()
}
profile
화이팅

0개의 댓글