Android/StoreInfo

<정리> 챌린지반 과제 5

re트 2024. 1. 12. 16:03
728x90

오늘은 튜터님이 직접 오셨다.

그래서 진행도 말씀드리고 질문하고 나니까 이제는 지금 수준에서 마무리라고 해주셨다.

그래서 마지막으로 말해주시는 것만 반영했고 이를 정리해놓으려고 한다.

 

1. ViewModelFactory

뷰모델을 만들 때 파라미터를 넘겨줄 수 있는 뷰모델 팩토리를 만들었다.

이건 따로 파일을 만들어야했다.

ViewModelProvider.Factory를 상속받는 뷰모델 팩토리 클래스를 만들었다.

그리고 안에 create함수를 오버라이딩했다.

제너릭 타입이 사용되는 걸 보고 매우 낯설었다.

그래도 이해하기에는 문제가 없었다.

뷰모델 팩토리로 값을 넘기고 그 값을 파라미터로 하는 뷰모델 객체의 인스턴스를 만드는 것..!!

나는 context, entryType, userEntity를 넘겼다.

class SignUpViewModelFactory(private val context: Context, private val entryType: SignUpEntryType, private val userEntity: SignUpUserEntity?): ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(SignUpViewModel::class.java)) {
            return SignUpViewModel(context, entryType, userEntity ?: SignUpUserEntity("", "", "")) as T
        }
        throw IllegalArgumentException("Unknown viewModel Class")
    }
}

 

그리고 뷰모델 클래스에서 파라미터를 추가해주고 뷰에서 뷰모델을 초기화할 때 뷰모델 팩토리를 추가해줬다.

private val viewModel by lazy {
    ViewModelProvider(this@SignUpActivity, SignUpViewModelFactory(this, entryType, userEntity))[SignUpViewModel::class.java]
}

 

받아온 값들을 가지고 init에서 초기값 세팅을 하고 다른 뷰모델의 함수에서도 쓰려고 했는데... 안 된다?!

그래서 이걸 튜터님께 물어보니 private val을 붙이지 않았기 때문이라고 하셨다.

이걸 붙이지 않으면 생성자 내부에서만 쓰겠다는 의미가 되고 붙이면 코드 내부에서 사용가능하게 하겠다는 의미가 된단다.

아주 간단한 이유였다.

이를 통해 뷰모델 내의 함수들의 파라미터가 매우 간소화되었다.

이전에는 context 받으랴, entryType 받으랴, userEntity 받으랴 그외에도 받을 게 많았는데 확 줄었다.

// 이전에는 entryType을 받았었던 함수
fun onClickSignUp(name: String, email: String, password: String) {
    _userInfo.value = SignUpUserEntity(name, email, password)
    when (entryType) {
        SignUpEntryType.UPDATE -> editInfo(
            userInfo.value?.email,
            userInfo.value
        )

        else -> setInfo(userInfo.value)
    }
}

 

2. context

이 부분은 새롭게 들은 부분이다.

액티비티(뷰)의 컨텍스트와 뷰모델의 컨텍스트는 분리가 되어야한다고 하셨다.

이건 내가 뷰모델 내에서 getString을 하기 위해 context.getString을 사용한 걸 보고 말씀하신 거였다.

이에 대한 방법으로는 contextProvider가 있다는데 다음 주에 시간되면 수업 때 해주신다고 하셔서 그거까지 반영하지는 않았다.

그래도 최대한 context를 직접적으로 쓰는 경우를 줄이기 위해 String타입이던 에러 메세지 라이브 데이터를 Int 타입으로 바꿔 리소스 Id값을 저장하도록 했다.

private val _nameErrorMsg: MutableLiveData<Int> = MutableLiveData()
val nameErrorMsg: LiveData<Int> get() = _nameErrorMsg

 

그리고 옵저버도 이에 맞춰 수정했다.

nameErrorMsg.observe(this@SignUpActivity) {
    binding.tvSignupNameError.text = getString(it)
}

 

사실 가능하면 전부 다 안 쓰게 하고 싶었는데 내가 짠 코드 구조상 쉽게 건드릴 수 없는 부분이 있어 전부 다 교체하지는 못했다.

그래도 최대한 수정했다.

 

3. Spinner Adapter

사실 스피너의 어댑터를 서비스 제공자 리스트 때문에 뷰모델로 옮겨놨었는데 튜터님이 이걸 보시고 뷰의 구성요소에 포함되는 애가 여기 있으면 어떡하냐면서 뷰로 돌아가게 할 것을 말하셨다.

사실... 제대로 이해는 못 했는데 그렇다고 하시니 순종하고 변경했다.

필요한 리스트는 뷰모델에 선언한 서비스 제공자 리스트를 가지고 왔다.

// 스피너 어댑터 변수
val adapter = ArrayAdapter(this, R.layout.simple_spinner_item, viewModel.serviceProviderStringList)
// 어댑터 연결
binding.spinnerSignup.adapter = adapter
반응형

'Android > StoreInfo' 카테고리의 다른 글

<정리> 챌린지반 과제2 - 1  (0) 2024.01.23
Facade Pattern에 대한 이해  (0) 2024.01.22
<정리> 챌린지반 과제 4  (0) 2024.01.10
<정리> 챌린지반 과제 3  (1) 2024.01.09
<정리> 사과마켓 앱 구현 3  (1) 2024.01.08