에러 핸들링에 대하여

timothy jeong·2021년 12월 30일
0

Android 개발 기록

목록 보기
8/8

21-03 새롭게 찾은 방법

기존에 방법은
(1) 각 viewModel 과 activity(혹은 fragment) 에서 반복되는 코드가 계속 등장하다는 점에서 불편했다.
(2) 또한 에러 dialog 에 대한 처리가 각 activity(혹은 fragment) 마다 흩어져있어서 일괄적인 관리가 힘들었다.

이러한 불편함을 해결할 수 있는 방법은 Resource<T> 패턴콰 상속을 이용하는 방법이다.

상속을 이용한 방법

  • AppCompatActivity() 를 확장한 클래스를 만든다.
  • 해당 클래스에서 errorCode 에 따라 에러 다이얼로그르 띄우는 함수를 만든다.
  • 이를 확장한 클래스에서는 에러를 모두 errorHandle(...) 함수를 통해 관리하도록 만든다.

이러한 방법은 LoadingDialog 를 띄우는 방법에도 이용할 수 있어서 굉장히 유용한 방법이다.

open class BaseActivity: AppCompatActivity() {
    lateinit var errorDialog: ErrorDialog
    
    fun errorHandle(errorCode: Int, msg: String) {
    	...
    }
}

상속이라는 개념은 잘 알고 있으면서 프로그래밍에서 잘 사용하지 않았는데, 이 레퍼런스를 찾고나서 반성하게 되었다.

Resource<T> 패턴

여러 레퍼런스를 찾다가 발견했는데, 에러 핸들링 뿐만 아니라 viewModel에서 사용되는 코드 자체를 크게 줄여줘서 만족도가 높았다. 내가 찾은 레퍼런스는 Room과 함께 사용하는 거였는데, Retorofit에 맞춰서 HttpCode 를 위한 변수를 추가했다.

data class Resource<out T>(var status: ResourceStatus,
                           val data: T?,
                           val message: String?,
                           val code: Int?) {

    companion object{

        fun <T> success(data: T?): Resource<T> {
            return Resource(ResourceStatus.SUCCESS, data, null, 200)
        }

        fun <T> error(msg: String, data: T?, code: Int): Resource<T> {
            return Resource(ResourceStatus.ERROR, data, msg, code)
        }

        fun <T> loading(data: T?): Resource<T> {
            return Resource(ResourceStatus.LOADING, data, null, null)
        }

        fun <T> reset(data: T?): Resource<T> {
            return Resource(ResourceStatus.RESET, data, null, null)
        }
    }
}

이러한 Resource 객체가 RetorofitAPI 코드를 통해 반환된 결과값을 갖도록 해주면 LiveData와 연계해서 사용하면 아주 코드가 깔끔해졌다.

class AuthRepo @Inject constructor(
    private val retrofitApi: AuthAPI
): AuthRepoInterface, ResourceUtil() {

    override suspend fun leave(): Resource<SimpleResponse> {
        return try {
            val response = retrofitApi.leave()
            val resultCode: Int? = if (response.isSuccessful) response.body()!!.resultCode else null
            generalResponseHandling(resultCode, response.body()?.message, response)
        } catch (e: Exception) {
            e.printStackTrace()
            val message = e.message ?: "null"
            Resource.error(message, null, -1)
        }
    }
}

open class ResourceUtil {
    protected fun <T> generalResponseHandling(resultCode: Int?,
                                              message: String?,
                                              response: Response<T>
    ): Resource<T> {
        return when {
            resultCode == 200 -> {
                Resource.success(response.body()!!)
            }
            resultCode != null -> {
                Resource.error(message!!, null, resultCode)
            }
            else -> {
                Resource.error("서버와 통신에 실패했습니다", null, -1)
            }
        }
    }
}

@HiltViewModel
class MyConfigViewModel @Inject constructor(
    private val authRepo: AuthRepoInterface
): ViewModel() {

    private val _leaveState = MutableLiveData<Resource<SimpleResponse>>()
    val leaveState: LiveData<Resource<SimpleResponse>> get() = _leaveState
    
    fun exit() {
          viewModelScope.launch(Dispatchers.IO) {
              _leaveState.postValue(authRepo.leave())
        }
    }
}

viewModel 의 코드에서 볼 수 있듯이, 그냥 적절한 liveData 에 postValue 해주기만 하면 된다. 그 이후의 처리는 ResourceSatate 의 결과값에 따라 달라진다. 아래와 같을 것이다.

        viewModel.leaveState.observe(this) {
            when (it.status) {
                ResourceStatus.SUCCESS -> {
                    ...
                    viewModel.resetLeaveState()
                }
                ResourceStatus.ERROR -> {
                   errorHandle(it.code!!, it.message!!)
                   viewModel.resetLeaveState()
                }
                ResourceStatus.LOADING -> {
                    ....
                }
                ResourceStatus.RESET -> {}
            }
        }

20-12 처음 에러 핸들링

viewModel 에서 errorOccur 라는 라이브데이터를 만들고, 이를 activity(혹은 fragment) 에서 observe 하며 에러 메시지를 출력하는 방법을 사용했다.

profile
개발자

0개의 댓글