FATAL EXCEPTION: main
Process: com.app.stronglife, PID: 23864
java.lang.RuntimeException: Cannot create an instance of class com.app.stronglife.viewmodel.SurveyViewModel
at androidx.lifecycle.viewmodel.internal.JvmViewModelProviders.createViewModel(JvmViewModelProviders.kt:40)
at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.android.kt:185)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.android.kt:309)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.android.kt:291)
새로운 ViewModel을 정의하고 앱을 실행시켰더니 크래시가 났다. viewModel과 앱의 크래시는 관련이 없다고 생각해서 다른 곳에서 에러를 찾았는데 로그를 보니 SurveyViewModel을 생성하는 곳에서 크래시가 발생하고 있었다...
문제 원인(GPT 왈,,)
SurveyViewModel은 생성자에 ApiService가 필요한데, viewModel()로 파라미터 없이 생성하려 해서 실패했습니다. 다른 ViewModel들(ProductViewModel, UserCodeViewModel)처럼 Factory를 사용하도록 변경했습니다.이제 앱이 정상적으로 실행됩니다. 테스트해보시고 문제가 있으면 알려주세요.
Android ViewModel 이란?
프론트 개발할 때 중요한 것 중 하나가 UI와 State Business Logic의 분리이다. 같은 상태를 공유하는 비지니스 로직과 UI 구현이 같은 파일에서 진행되면 타이밍이 꼬이거나 상태가 일관적이지 않을 수 있고 이는 잦은 에러와 유지보수의 악화를 발생시킨다. 이때 필수적으로 사용되는 것이 바로 viewModel 이다. UI는 그려주기에 집중하고 viewModel은 상태와 비지니스 로직을 관리하도록 분리할 수 있다.
Android ViewModel에서의 생성자(constructor)
코틀린에서 클래스를 만들 때
class SurveyViewModel : ViewModel() <- 이런 기본 생성자를 사용하여 만들 수 있는 viewModel이 있는가 하면,
class SurveyViewModel(private val apiService : ApiService) : ViewModel() <- 생성자에 파라미터가 있는 viewModel이 있다.
이때 기본 동작은 파라미터가 없는 기본 생성자로 만드는 것이기 때문에 생성이 불가하다. 예외가 발생하기 때문에 ViewModelFactory가 등장한다.
ApiService와 ViewModel의 관계?
ApiService는 Retrofit 통신에서 API 호출 함수를 정의한 인터페이스이다.
@POST("survey/kiosk")
suspend fun submitSurvey(
@Header("x-api-key") apiKey: String,
@Header("userCode") userCode: String?,
@Body request: SurveyRequest
): Response<Unit>
" SurveyViewModel은 생성자에 ApiService가 필요한데"
ㄴ 이 말은 ViewModel이 서버 데이터를 가져와야 할 때, ViewModel 내부에서 apiService 인스턴스가 주입되어야 하는 상황이다. ViewModel에서 상태/로직을 담당한다면, ApiService는 서버와의 실질적 통신 부분을 담당해 역할을 분리하고 ApiService에서 ViewModel로 의존성을 주입할 수 있는 것이다.
ViewModel vs ViewModelFactory
ViewModel로만 관리되는 상태는 파라미터 없이 생성 가능한 기본 ViewModel이자, 내부에서 그냥 초기화가 가능한 상태들이다. 화면 탭 선택값이나 로딩 여부, 텍스트 입력값 같은 상태들은 항상 같은 값으로 기본값을 지정할 수 있다. (== 초기화가 가능하다)
다만 나의 경우에는 SurveyViewModel에서 쓰이는 submitSurvey가 특정 초기화 값에 의존되어야 했다.
val response = api.submitSurvey(
apiKey = apiKey,
userCode = userCode,
request = request
)
ViewModel의 응답 데이터가 ApiService의 submitSurvey에 의존하고 있기 때문에 ViewModel에 ApiService 의존 객체가 필요했고 " viewModel()로 파라미터 없이 생성하려 해서 실패했습니다" -> 즉 기존 코드에 파라미터 없이 ViewModel을 생성하려고 했기 때문에 에러가 발생한 것이다. 이때, 파라미터에 값을 넣어줄 수 있는 게 바로 ViewModelFactory이다.
class SurveyViewModelFactory(
private val api: ApiService
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SurveyViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return SurveyViewModel(api) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
오류가 아니라, 앱 크래시가 발생하는 이유?
api 호출이 실패하거나 잘못된 값이 응답으로 오면 오류가 발생하는 것을 볼 수 있다. 다만 ViewModel 생성에서 오류에서 그치는 게 아니라 앱이 '크래시'되는 것은 ViewModel 생성에 실패하면 앱이 계속 실행될 수 없는 UI이기 때문이다. 생성에 실패하면 IllegalArgumentException/RuntimeException 같은 런타임 에러가 발생하는데, 이 예외에 대한 UI 스레드의 처리 코드가 없었기 때문에 프로세스가 종료되는 현상이 발생했다.
즉, ViewModel 생성에서 발생한 throw 에러가 상위의 스레드로 올라가 프로세스가 종료되는 결과를 일으킨 것이다...
=> 정리하자면, 처리되지 않은 예외가 메인 스레드에서 발생할 시 앱은 크래시가 나고, 처리된 에러인 경우에 에러에서 그칠 수 있다.
'Error Report > Android' 카테고리의 다른 글
| [Android] 안드로이드 스튜디오 에뮬레이터 조용히 꺼짐 문제 (0) | 2025.07.15 |
|---|