ABOUT ME

-

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

    이전글

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

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

    다음글

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

     

     앞에서 Retrofit과 RxJava를 이용하여 네트워크 요청 및 결과값 처리에 대해서 어떻게 구현할지에 대해서 고민을 해봤습니다. 이번에는 요청 실패시 재시도를 추가해 봅시다.

     

     RxJava에서 제공하는 retry() 메소드를 활용하여 재시도를 구현해 보겠습니다. 단순하게 재시도를 하는 경우는 retry()메소드에 재시도 횟수만 입력하여 요청 시점에 추가 하면 됩니다.

    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) }
                .retry(2)
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

     앞서 만들어놓은 follower를 가져오는 api에서 retry()메소드를 추가했습니다. 그리고 재시도 횟수를 2회로 지정합니다. 이렇게 되면 최초 요청 1회 + 재시도 2회 포함해서 최대 3회까지 요청이 됩니다.

     이렇게 해서 요청을 하면 UI 상에는 최종 실패한 결과만 전달되기 때문에 내가 입력한 횟수만큼 재시도를 했는지 확인이 어렵습니다. 그래서 Logger를 추가해보도록 하겠습니다.

     

     Square사의 OkHttp 라이브러리를 사용하는경우 interceptor를 추가하여 요청 중간에 본인이 원하는 형태로 네트워크 요청을 수정이 가능하게 됩니다. Retrofit은 Square사가 만든 라이브러리 이며 그래서 내부적으로 자사의 OkHttp 라이브러리를 사용하고 있어서 HttpClient를 생성할때 logger interceptor를 추가해주면 손쉽게 네트워크 로그를 확인할 수 있습니다. 

      dependency를 추가하도록 합시다 com.squareup.okhttp3:logging-interceptor를 추가합니다.

    android { 
    	...
    
    	dependencies {
    
    		...
    
    		//Retrofit 설정
    		implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    		implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    		implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
    		implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
    		
    
    		...
    
    	}
    
    ...
    
    }

     여기서는 logging-interceptor의 버전을 3.12.0으로 추가하였지만 버전은 OkHttp 배포사이트에서 확인후 입력하시면 됩니다. gradle에 추가가 완료 되었으면 Retrofit을 생성하는 코드를 수정해 주겠습니다. HttpClient를 생성하는 시점에 logging interceptor를 추가하면 됩니다.

    class GithubRepository {
        private val githubApi: GithubApi
    
        init {
            githubApi = createRetrofit("https://api.github.com").create(GithubApi::class.java)
        }
    
        private fun createRetrofit(baseUrl: String): Retrofit {
            return Retrofit.Builder()
                .client(
                    OkHttpClient.Builder()
                        .connectionPool(ConnectionPool(5, 20, TimeUnit.SECONDS))
                        .addInterceptor(HttpLoggingInterceptor(
                            HttpLoggingInterceptor.Logger {
                                Log.i("TEST", it)
                            }).apply {
                            level = if (BuildConfig.DEBUG)
                                HttpLoggingInterceptor.Level.BODY
                            else
                                HttpLoggingInterceptor.Level.NONE
                        })
                        .build()
                )
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(baseUrl)
                .build()
        }
    
        ...    
        
    }

     OkHttpClient를 생성하는 시점에 addInterceptor()메소드를 통해 HttpLoggingInterceptor를 추가합니다. 여기서는 생성자로 HttpLoggingInterceptor.Logger 구현체를 추가하였는데 이건 로그의 내용을 지정한 별도로 지정한 tagname으로 출력하려고 한것이고 로그의 내용을 별도로 가공하지 않아도 된다면 생성자 파라매터로 아무것도 넣지 않아도 됩니다. 그러면 'OkHttp'라는 tagname으로 logcat에 출력이 됩니다.

     생성시점에 setLevel() 메소드를 통해 출력되는 로그의 수준을 지정할 수 있습니다. 여기서는 DEBUG 빌드일때는 전체출력을 하고 RELEASE일때는 아무것도 출력하지 않도록 하였습니다. 이렇게 하고 404오류가 발생하는 요청을 해보도록 하겠습니다.

    I/TEST: --> GET https://api.github.com/users/qqwdqwsdsd/followers
    I/TEST: --> END GET
    I/TEST: <-- 404 Not Found https://api.github.com/users/qqwdqwsdsd/followers (1321ms)
    I/TEST: Date: Sat, 06 Apr 2019 07:28:27 GMT
    
    ...
    
    I/TEST: {"message":"Not Found","documentation_url":"https://developer.github.com/v3/users/followers/#list-followers-of-a-user"}
    I/TEST: <-- END HTTP (119-byte body)
    I/TEST: --> GET https://api.github.com/users/qqwdqwsdsd/followers
    I/TEST: --> END GET
    I/TEST: <-- 404 Not Found https://api.github.com/users/qqwdqwsdsd/followers (303ms)
    I/TEST: Date: Sat, 06 Apr 2019 07:28:28 GMT
    
    ...
    
    I/TEST: {"message":"Not Found","documentation_url":"https://developer.github.com/v3/users/followers/#list-followers-of-a-user"}
    I/TEST: <-- END HTTP (119-byte body)
    I/TEST: --> GET https://api.github.com/users/qqwdqwsdsd/followers
    I/TEST: --> END GET
    I/TEST: <-- 404 Not Found https://api.github.com/users/qqwdqwsdsd/followers (303ms)
    I/TEST: Date: Sat, 06 Apr 2019 07:28:28 GMT
    
    ...
    
    I/TEST: {"message":"Not Found","documentation_url":"https://developer.github.com/v3/users/followers/#list-followers-of-a-user"}
    I/TEST: <-- END HTTP (119-byte body)
    I/TEST: clickRequest httpexception code : 404 exception.response.code : 404

     출력된 로그를 보면 위에서 부터 3회 요청이 있었고 최종으로 onError callback으로 내려온것을 볼수 있습니다. 단순 재시도가 필요한 경우는 retry(retrytimes) 메소드만 추가하시면 됩니다. retry(Predicate), retry(retrytimes, Predicate) 메소드를 통해 재시도할 오류를 확인할 수도 있습니다. 

    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) }
                .retry() // 성공할때까지 재시도
                .retry(2) // 실패시 2회 재시도
                .retry { t -> t is HttpException } // 실패시 HttpException 일경우 성공할때까지 재시도
                .retry(2) { t -> t is HttpException } // 실패시 HttpException 일경우 2회 재시도
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

     이렇게 retry를 필요에 따라서 여러종류로 활용할 수 있습니다. Predicate를 통해 throwable을 전달받아 retry를 할지 여부를 선택할 수도 있어서 특정 오류일때만 재시도 하게 할수도 있습니다.

     

     이렇게해서 네트워크 요청시 필수적으로 설정해야 할법한 기능들을 추가했습니다. Retrofit과 RxJava를 엮어서 쓰는것은 Retrofit을 기본 형태로 활용하는것 보다 확장성이나 편의성에 있어서 월등하다고 생각하고 있어서 열심히 활용중입니다. 앞서서 사용했던 것처럼 map()을 이용해서 결과값을 전처리를 한다거나, retry()를 이용하여 재시도를 한다거나 하는 등의 소소하지만 편리하게 활용할 수 있습니다. 

     다음에는 retry()의 또다른 메소드 중 하나인 retryWhen()을 사용해서 조금 다른 형태의 재시도를 추가해 보도록 하겠습니다.

    반응형
Designed by Tistory.