ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Retrofit / RxJava] 네트워크 요청 결과를 RxJava로 처리하자 -2-
    개발/Android 2019. 4. 4. 20:47
    반응형

    이전글

    2019/03/13 - [개발/Android] - [Retrofit / RxJava] 네트워크 요청 결과를 RxJava로 처리하자 -1-

    다음글

    2019/04/06 - [개발/Android] - [Retrofit / RxJava] 네트워크 요청 결과를 RxJava로 처리하자 -3-

    2019/04/09 - [개발/Android] - [Retrofit / RxJava] 네트워크 요청 결과를 RxJava로 처리하자 -4-

     

     앞서서 Retrofit과 RxJava를 이용한 비동기 네트워크 요청 및 처리 방법을 구현했습니다. 아주 단순처리일 경우는 기본 구현으로도 충분 할 수 있지만 Http Response code를 적극적으로 사용하는 경우는 앞선 기본 구현으로는 처리가 어렵습니다. Retrofit의 Response클래스로 감싸서 결과값을 받아봅시다.

     

     앞서 구현된 요청 인터페이스에서 결과값을 수정해 보도록 하겠습니다.

    class GithubRepository {
       
       ...
        
        val getFollowers: Single<Response<List<GithubUser>>> = githubApi
            .getFollowers("octocat")
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
    }

     기존의 반환값을 Response로 한번 감싸주기만 하면 되기 때문에 어렵지 않습니다. 이렇게 되면 결과값이 Response<T> 형태로 반환되기 때문에 결과를 꺼내는 작업을 추가해야 합니다. 

     

    class MainViewModel : BaseViewModel() {
    
    	...
    
        fun clickRequestFollowers() {
            showProgress.value = true
            addDisposable(repository?.getFollowers?.subscribe(
                {response ->
                    if (response.isSuccessful) {
                        adapter.setItems(response.body())
                    }
                    showProgress.value = false
                }, { throwable ->
                    showProgress.value = false
                    showError.value = ErrorMessage("서버요청중에 오류가 발생했습니다.\n${throwable.message}")
                })
            )
        }
    }

     subscribe()를 통해 전달받은 Response객체를 isSuccessful() 메소드를 이용하여 성공여부를 판별하여 Response.body()를 통해 원래 의도한 결과 객체를 꺼냅니다.

     여기서 눈치를 챌수 있지만 Response.isSuccessful()를 한다는 건 성공이 아닌 결과값도 subscribe() 메소드의 onSuccess callback으로 전달된다는 의미 입니다. 앞서 기본형태로 구현한 방식의 경우 성공만 onSuccess callback으로 전달이 되고 그외의 실패의 상황에서는 onError callback으로 전달받는 형태 였는데 onSuccess callback에서도 error를 확인해야 합니다.

     실제로 어떤 결과가 전달되는지 테스트를 해보기 위해 실패하는 요청을 추가해 보도록 하겠습니다. 

    class GithubRepository {
       
       ...
        
        val getFollowersSuccess: Single<Response<List<GithubUser>>> = requestFollowers("octocat")
        val getFollowersFail: Single<Response<List<GithubUser>>> = requestFollowers("qqwdqwsdsd")
        
        private fun requestFollowers(username: String) :  Single<Response<List<GithubUser>>> {
            return githubApi.getFollowers(username).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

      동일한 요청이지만 성공 / 실패하도록 구성하였습니다. 실제로 요청을 하면 어떻게 될지 확인해 보겠습니다. 결과값을 Response로 감싸기 전에는 실패할 경우 onError callback으로 실패가 반환되었습니다. 간단하게 Log를 찍어보도록 하겠습니다.

    class MainViewModel : BaseViewModel() {
    
    	...
    
        fun clickRequestSuccess() {
            showProgress.value = true
            addDisposable(repository?.getFollowersSuccess?.subscribe(
                { response ->
                    if (response.isSuccessful) {
                        adapter.setItems(response.body())
                        Log.i("TEST", "clickRequestSuccess success "+response.code())
                    } else {
                        Log.i("TEST", "clickRequestSuccess fail "+response.code())
                    }
                    showProgress.value = false
                }, { throwable ->
                    showProgress.value = false
                    showError.value = ErrorMessage("서버요청중에 오류가 발생했습니다.\n${throwable.message}")
                })
            )
        }
        
        fun clickRequestFail() {
        
            ...
            
        }
    }

     간단하게 확인하기 위해 onSuccess callback에서 isSuccessful()로 분기해서 Log를 출력했습니다. 

    I/TEST: clickRequestSuccess success 200
    I/TEST: clickRequestFail fail 404

     이렇게 둘다 onSuccess callback으로 넘어오게 됩니다. 물론 여기서 성공 / 실패 처리를 해주면 되기도 하겠지만 가급적이면 실패의 경우 onError callback으로 전달하는것이 의미상 더 부합하기 때문에 좀 더 예외처리를 해주도록 하겠습니다. RxJava에는 map()이라는 메소드가 있습니다. Java8과 kotlin Stream API와 동일한 명칭인 만큼 기능도 동일합니다. 서버에서 전달받은 결과객체를 map()을 통해 전처리를 해줍니다.

    class GithubRepository {
       
       ...
        
        private fun requestFollowers(username: String) :  Single<Response<List<GithubUser>>> {
            return githubApi.getFollowers(username).subscribeOn(Schedulers.io())
                .map { t -> if (t.isSuccessful) t else throw HttpException(t) }
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

     map()을 통해 전달받은 Response<T> 객체에서 isSuccessful()을 확인하여 true일 경우는 그대로 전달을 하고 false일 경우는 HttpException을 발생시키도록 하였습니다. HttpException의 경우 생성자로 Response 객체를 전달받게 됩니다.  이렇게 되는 경우 Http response code 가 2xx가 아닌 경우는 모두 HttpException이 발생하게 됩니다.

     이제 다시 Log를 출력해 보도록 하겠습니다. 이번에는 onError callback쪽에 Log를 하나 더 추가 하겠습니다. 

    class MainViewModel : BaseViewModel() {
    
    	...
    
        fun clickRequestSuccess() {
            
            ...
            
        }
        
        fun clickRequestFail() {
            showProgress.value = true
            addDisposable(repository?.getFollowersFail?.subscribe(
                { response ->
                    if (response.isSuccessful) {
                        adapter.setItems(response.body())
                        Log.i("TEST", "clickRequestFail success "+response.code())
                    } else {
                        Log.i("TEST", "clickRequestFail fail "+response.code())
                    }
                    showProgress.value = false
                }, { throwable ->
                    showProgress.value = false
                    showError.value = ErrorMessage("서버요청중에 오류가 발생했습니다.\n${throwable.message}")
                    if(throwable is HttpException) {
                        val exception = throwable
                        Log.i("TEST", "clickRequest httpexception code : ${exception.code()} exception.response.code : ${exception.response().code()}")
                    }
                })
            )
        }
    }

     onError callback에서 전달받은 Throwable 객체가 HttpException 객체일경우 Log를 출력하도록 했습니다. 

    I/TEST: clickRequestSuccess success 200
    I/TEST: clickRequest httpexception code : 404 exception.response.code : 404

     onError callback쪽에 추가한 Log가 출력이 되었습니다. 앞서 onSuccess callback쪽에서 출력되던 것과 다르게 의도한데로 error로 출력이 되었습니다. 오류 발생시 간단하게 출력하게 한 팝업도 정상적으로 표시가 되었습니다.

     서버 요청시 Http response code를 이용하여 결과값을 제어해야 하는 경우 Response 클래스로 결과값을 한번 감싸서 처리하는 방법과 발생할 수 있는 예외상황에 대해서 처리하는 방법을 확인했습니다. 이렇게만 구현해도 기본적인 서버요청에 대해서는 처리가 가능할 거라 생각합니다. 

     다음에는 네트워크 구현시 반드시 필요한 재요청에 대해서 추가해 보도록 하겠습니다.

    반응형
Designed by Tistory.