API의 결과에 따라 다른 결과를 보여주고 싶어!

milkbottle·2024년 5월 20일
0

ApiState

MVVM 아키텍처에서는 ViewModel에서 Api통신을 한다.

그 결과에 따라 성공하면 페이지를 이동시키고, 실패하면 토스트 메시지를 보내는 이런 분기가 많다.

하지만, ViewModel 내부에서는 context를 사용할 수 없다. context는 @Composable 안에서만 사용할 수 있기 때문이다.

어떻게 이를 해결할 수 있을까?

방법

ApiState

Api에 대한 상태는 Empty, Loading, Success, Error로 크게 4가지로 나눌 수 있다.

  • Empty: 아직 Api를 호출하지 않은 상태
  • Loading: Api를 호출중인 상태
  • Success: Api의 응답결과가 잘 온 상태
  • Error: Api의 응답결과가 모종의 이유로 안된 상태

이를 코드로 나타내면?

sealed class ApiState<out T> {
    data class Success<T>(val data: T) : ApiState<T>()
    data class Error(val message: String) : ApiState<Nothing>()
    object Loading : ApiState<Nothing>()
    object Empty : ApiState<Nothing>()
}

제네릭으로 둔 이유는 경우에 따라 String이나, Int로 StatusCode를 메시지로 넣을 수 있도록 한 것이다.

ViewModel - Fetch 함수

ViewModel에서는 Api를 Fetch하는 함수가 있을 것이다.

api를 호출 하는 함수는 비동기적이어야 하므로 보통 viewModelScope.launch를 많이 사용한다.

class MyViewModel : ViewModel() {
    private val _apiState = mutableStateOf<ApiState<String>>(ApiState.Empty)
    val apiState: State<ApiState<String>> = _apiState

    fun fetchData() {
        viewModelScope.launch {
            _apiState.value = ApiResult.Loading
            try {
                val result = apiCall()
                _apiState.value = ApiResult.Success(result)
            } catch (e: Exception) {
                _apiState.value = ApiResult.Error(e.message())
            }
        }
    }
}

View - 분기처리

이제 Api에 대한 결과를 View에서 분기처리 해야한다.

LaunchedEffect를 통해 apiState의 값이 바뀌면 분기마다 처리할 수 있도록 한다.

@Composable
fun MyScreen(viewModel: MyViewModel, navController: NavController) {
    val context = LocalContext.current
    val apiState = viewModel.apiState.value

    LaunchedEffect(apiState) {
        when (apiState) {
            is ApiState.Loading -> {
                // 로딩 상태 처리
            }
            is ApiState.Success -> {
                // 성공 시 페이지 이동
                navController.navigate("nextPage")
            }
            is ApiState.Failure -> {
                // 실패 시 토스트 메시지 띄우기
                Toast.makeText(context, (apiResult as ApiResult.Failure).error, Toast.LENGTH_SHORT).show()
            }
            is ApiState.Empty -> {
            	// 아무것도 안함
            }
        }
    }

    // 화면 구성
    // Column() {}
}

Compose에 대해 공부하며 LaunchedEffect, DisposableEffect 등 많았는데, 이를 좀 정리해서 포스팅하도록 하겠다.

0개의 댓글