-
Android) State 패턴을 통해서 UI 상태 관리하기Android 2021. 2. 26. 12:19
오늘은 제가 요즘 유용하게 사용하고 있는 State 패턴에 대해서 글을 작성해보려고 합니다.
State 패턴
- 객체의 특정 상태를 클래스로 선언하고, 클래스에서는 해당 상태에서 할 수 있는 행위들을 메서드로 정의합니다.
- 각 상태 클래스들을 캡슐화하여, 클라이언트에서 호출하는 방식
- UI 상태관리를 Readable하게 하기 위한 방법 중 하나
이런 특징들로 회사에 출근하기 위한 State 패턴을 예로 만들어보자면 아래와 같은 상태로 나눌 수 있지 않을까 싶습니다.
각 상황에 따라 대처할 행위를 명시해 두는 것이죠. 그래면 해당 상황에 직면했을 때 대비하는 것이 쉬워집니다.
when (상태) { is 출근.준비 -> { 씻기() 밥먹기() } is 출근.실패 -> { 늦잠() 회사에연락() } is 출근.성공 -> { 업무() 회의() } }
Usage
interface, enum class, sealed class 모두로 State 패턴을 구현할 수 있는데 sealed class로 구현하는 것을 추천드립니다.
sealed 클래스는 Child 클래스의 종류를 제한하는 특성이 있어, 정의된 하위 클래스 외에 다른 하위 클래스는 존재하지 않는다는 것을 컴파일러에게 알려주는 것과 같은 효과를 냅니다.
즉 when 문으로 모든 케이스에 대해서 처리가 되어야 하기 때문에 다른 클래스들은 else 구문이 들어가야 하지만,
sealed class는 컴파일 시점에 하위 클래스들이 정해져 있기 때문에 else 없이도 클래스들을 관리할 수 있습니다.
sealed class의 하위클래스는 class, data class, object 로 정의할 수 있습니다.
State Class
sealed class UiState { object Loading : UiState() object Empty : UiState() data class Success(val list: List<FavoriteItem>) : UiState() data class Error(val message: String?) : UiState() }
위와 같이 State 클래스를 선언했습니다.
ViewModel
private val _uiState = MutableStateFlow<UiState>(UiState.Loading) val uiState = _uiState.asStateFlow() private fun getSomething() { trendingRepository.getSomething() .onStart { _uiState.value = UiState.Loading delay(300) } .catch { exception -> UiState.Error(exception.localizedMessage) } .collect { _uiState.value = UiState.Success(it) } }
그런 후에 뷰모델에서 위와 같이 사용할 수 있습니다. 저는 코루틴의 flow와 함께 사용했습니다.
View
private fun showSomething() { viewModel.uiState.asLiveData().observe(viewLifecycleOwner, Observer { when (it) { is UiState.Loading -> { showLoadingView() hideRecyclerView() } is UiState.Empty -> { hideLoadingView() showEmptyText() } is UiState.Success -> { hideLoadingView() showRecyclerView() adapter.submitList(it) } is UiState.Error -> { hideLoadingView() showErrorText(it.message.toString()) } } }) }
그런 후에 View(Fragment)에서 State를 나누어 해당 상태에 맞는 코드를 작성하면 됩니다.
State 패턴을 사용한다면 굉장히 직관적이고 가독성 있는 코드를 작성할 수 있지 않을까요?
State 패턴은 UI 상태가 복잡해질수록 직관적이고, Side Effect 적은 방식이기 때문에 이런 경우에 유용한 패턴이라고 생각합니다.
반응형'Android' 카테고리의 다른 글
Android) Retrofit2 작동 원리 알아보기 (0) 2021.04.09 Android) Device 실제 사이즈 구하기 (feat - display, getRealMetrics) (0) 2021.04.08 Android) Room에서 List 또는 객체를 필드에 저장하는법 feat) @Embeded @TypeConverter (0) 2021.02.19 Android) asStateFlow() 메소드 알아보기 (0) 2021.02.17 Android) Jetpack Paging 3 라이브러리 알아보기 (0) 2021.02.16