본문 바로가기

Android + Kotlin

[Android Kotlin] sealed class로 Network Response (with Flow)를 구현해보자.

반응형

이번 시간엔 sealed class를 이용하여 서버에서 수신한 응답을 viewmodel로 전달하는 것을 구현하려고 합니다.

 

기존에 개발했던 아래 링크의 게시물 코드를 수정합니다.

 

Koin + MVVM + Coroutine + Flow 를 이용한 상세화면 만들기

앱개발에서 가장 기본이라 할수 있는 Rest API로 서버에서 데이터를 요청하고, 화면에 표시하는 일련의 과정을 정리해보려고 합니다. 요즘 Android 개발을 하면서 가장 많이 사용하고 있는(실제로도

heeeju4lov.tistory.com

 

위의 게시물에서는 Repository에서 exception 처리로 성공/실패을 응답했다면, 이번 시간엔 sealed class를 이용하여 성공/실패를 viewmodel로 전송하는 것을 구현하겠습니다.

 

그럼, 시작합니다.

 


 

1. 이번 개발의 핵심인 성공/실패 응답을 처리하는 sealed class와 함수를 구현합니다.

    ApiResult.kt 파일을 생성하고, 아래와 같이 구현합니다.

 

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import retrofit2.HttpException

sealed class ApiResult<out T> {
    data class Success<out T>(val value: T): ApiResult<T>()
    object Empty: ApiResult<Nothing>()
    data class Error(val code: Int? = null, val exception: Throwable? = null): ApiResult<Nothing>()
}

fun <T> safeFlow(apiFunc: suspend () -> T): Flow<ApiResult<T>> = flow {
    try {
        emit(ApiResult.Success(apiFunc.invoke()))
    } catch (e: NullPointerException) {
        emit(ApiResult.Empty)
    } catch (e: HttpException) {
        emit(ApiResult.Error(code = e.code(), exception = e))
    } catch (e: Exception) {
        emit(ApiResult.Error(exception = e))
    }
}

 

여러가지 서버 API 응답에 대응을 위해서, 재네릭 타입 T를 이용하여 구현합니다.

네트워크 응답은 성공 또는 실패인데, null 체크를 할겸 Empty 를 추가했습니다.

 

Success 에는 서버에서 전달받은 데이터가 들어가게 되고, Error 에는 에러코드와 메시지가 전달되도록 구현합니다.

 

 

2. retrofit 의 api service interface(LoremPicsumApiService.kt) 에서 기존에 Response 클래스로 응답을 받던 것을 데이터 클래스로 받도록 api를 추가해줍니다.

 

@GET("/id/{imageId}/info")
suspend fun imageInfoSafeFlow(@Path("imageId") imageId: String): ResImageInfo.Response

 

 

3. repository(LoremPicsumRepository.kt)에, 위의 inteface를 이용한 함수를 추가합니다.

 

fun getImageInfoSafeFlow(imageId: String): Flow<ApiResult<ImageInfo>> = safeFlow {
    service.imageInfoSafeFlow(imageId).mapper()
}

 

서버 요청 인터페이스를 실행하는 함수를 safeFlow()에 apiFunc 파라메터로 전달하여, safeFlow()  함수내에서 apiFunc 함수가 실행이 되도록 합니다.

safeFlow() 함수에서는 전달받은 apiFunc 을 호출하면서 try...catch()로 에러처리를 통해 ApiResult를 반환하게 됩니다.

 

 

4. viewmodel에서 ApiResult의 성공/실패/empty 에 대한 처리를 합니다.

 

fun getImageInfoSafeFlow(imageId: String) {
    viewModelScope.launch {
        repository.getImageInfoSafeFlow(imageId).collectLatest { result ->
            when (result) {
                is ApiResult.Success -> {
                    _imageInfo.postValue(result.value)
                }
                is ApiResult.Empty -> {
                    _errorMsg.postValue("data empty.")
                }
                is ApiResult.Error -> {
                    _errorMsg.postValue(result.exception?.message)
                }
            }
        }
    }
}

 

 

 

5. Activity에서도 위의 함수를 호출하여 실행합니다.

 

lifecycleScope.launchWhenStarted {
    intent?.getStringExtra(Constants.KEY_EXTRA_IMAGE_ID)?.let {

        /**
         * sealed class로 성공/실패를 처리하는 방식
         */
        viewModel.getImageInfoSafeFlow(it)

        /**
         * exception 으로 성공/실패를 처리하는 방식
         */
//                viewModel.getImageInfo(it)
    }
}

 

 

위의 개발 코드는 아래 Github 링크에서 확인해주세요.

 

 

GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.

https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함. - GitHub - rcbuilders/RemindSampleApp: https://heeeju4lov.tistory.com/ 블로그에서 Android + Kotlin 강좌에서 사용함.

github.com

 

반응형