android

Android UiState

Seon Dev Notes 2025. 3. 25. 14:32

오늘은 안드로이드 앱 개발에서 UI의 현재 상태를 나타내는 핵심 개념, UiState를 알아 보겠습니다.

UiState

UiState는 UI를 구성하는 데이터의 상태를 나타내는 개념
Compose와 함께 MVVM 패턴을 사용할 때, ViewModel이 관리하는 UI 상태를 의미하며, UI의 일관성을 유지하고 예측 가능한 방식으로 상태 변화를 처리하는 데 중요한 역할을 한다.

UiState가 필요한 이유

안드로이드에서 UI 상태를 직접 관리하면 생길 수 있는 문제들

  • 비동기 작업 관리의 복잡성: 네트워크 요청, 데이터베이스 작업 등에서 UI가 올바르게 업데이트되지 않음
  • 예측 불가능한 UI 변경: 여러 UI 요소가 동시에 갱신될 때 일관성을 유지하기 어려움

UiState의 기본 구조

UiState는 일반적으로 sealed interface 또는 sealed class를 사용하여 정의한다. sealed class를 사용하면 상태의 종류를 제한할 수 있어 코드의 안정성을 높일 수 있습니다.

sealed class

sealed class는 상속을 제한하는 클래스이다. sealed로 선언된 클래스는 해당 클래스의 하위 클래스를 같은 파일 내에서만 정의할 수 있다.

sealed class UiState

data class Loading(val message: String) : UiState()
data class Success(val data: String) : UiState()
data class Error(val exception: Throwable) : UiState()

UiState는 sealed class로 선언되어 있으며, 이 클래스를 상속한 Loading, Success, Error는 같은 파일 내에서만 정의될 수 있다. 그 외의 파일에서는 UiState를 상속할 수 없다.

sealed interface

sealed interface는 sealed class와 비슷하지만 인터페이스로 정의된다. sealed interface를 사용하면, 여러 클래스들이 이 인터페이스를 구현할 수 있다.

sealed interface UiState

data class Loading(val message: String) : UiState
data class Success(val data: String) : UiState
data class Error(val exception: Throwable) : UiState

sealed interface는 sealed class와 거의 동일하지만, 클래스가 아닌 인터페이스로 구현된다는 점에서 차이가 있다

ViewModel에서 UiState 관리하기

ViewModel을 사용하면 UI 상태를 한 곳에서 관리할 수 있어 유지보수성이 높아집니다.

class MyViewModel : ViewModel() {
    private val _uiState = MutableStateFlow<UiState>(UiState.Empty)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()

    fun fetchData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading("데이터를 불러오는 중...")
            delay(2000) // 네트워크 요청 시뮬레이션
            val result = fetchFromServer()
            _uiState.value = if (result.isEmpty()) UiState.Empty else UiState.Success(result)
        }
    }
}

Compose에서 UiState를 observe하여 UI 업데이트하기

collectAsState()를 활용하여 ViewModel의 UiState를 감지할 수 있다.

 @Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsState()

    when (uiState) {
        is UiState.Empty -> Text(text = "데이터가 없습니다.")
        is UiState.Loading -> Text(text = (uiState as UiState.Loading).message)
        is UiState.Success -> Text(text = (uiState as UiState.Success).data)
        is UiState.Error -> Text(text = "에러 발생: ${(uiState as UiState.Error).exception.message}")
    }
}

'android' 카테고리의 다른 글

Android Okhttp  (0) 2025.04.08
Android Interceptor  (0) 2025.04.01
Android ViewModel  (0) 2025.03.17
Android State Hoisting(상태 호이스팅)  (0) 2025.03.10
Android XML과 Compose의 차이점?  (0) 2025.02.23