brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Jul 31. 2017

Reactive Extensions

ReactiveX #02 - Networking patterns

ReactiveX - Networking patterns


#01 Add RxJava2 to Networking module


Rx를 안드로이드 많은 부분에 활용할 수 있지만, 가장 많이 활용하는 부분은 네트워크 모듈입니다.


네트워크 모듈은 단순히 비동기 API를 이용하는 데 그치지 않고, 이를 기반으로 다른 API 호출 같은 비동기 동작을 호출하거나, 여러 비동기 API를 동시에 처리하는 등 복잡해지기 쉬운 부분입니다.

이를 직접 Java 코드로 처리할 수 도 있지만 신경 써야 할 부분이 많기에 Retrofit과 RxJava를 이용해 좀 더 수월하게 처리하는 방법에 대해 작성하겠습니다.




#01 Init Retrofit


Retrofit은 Square가 제공하는 안드로이드와 Java 애플리케이션을 위한 라이브러리로, 안전한 타입(type-safe) 방식의 HTTP 클라이언트입니다.


Retrofit에 대한 내용은 아래 링크들을 참조해 주시기 바랍니다.




#02 Init RxJava2 Adapter


RxJava2 설정까지 끝나면 Retrofit2와 RxJava2를 연동하기 위한 Adapter를 설정해야 합니다.

자세한 방법은 아래 링크를 참조해 주시기 바랍니다.

 




#03 Retrofit Interface


Retrofit을 이용한 일반적인 API 호출 Interface와 이를 Rx의 Observable 형태로 받는 Interface입니다.(Flowable, Single 모두 가능합니다.)


Observable, Flowable, Single의 개념은 공식 페이지를 참조해 주시기 바랍니다.

// Retrofit
public interface RetrofitAPI {
   @GET("vod")
   Call<Vod> getVod();

   @GET("playurl/{id}")
   Call<String> getPlayUrl(@Path("id") String id);

   @GET("live")
   Call<Live> getLive();
}
// Retrofit + Rx
public interface RxAPI {
   @GET("vod")
   Single<Vod> getVod();

   @GET("playurl/{id}")
   Single<String> getPlayUrl(@Path("id") String id);

   @GET("live")
   Observable<Live> getLive();

   @GET("movie")
   Flowable<Movie> getMovie();
}




#04 Call API


#02 Call API


Retrofit을 이용하면 onResponse(), onFailure()를 통해 복잡한 API response 처리를 간단하게 처리할 수 있습니다.

// Retrofit
Call<Vod> call = retrofitAPI.getVod();

call.enqueue(new Callback<Vod>) {
   @Override
   public void onResponse(Response<Vod> response, Retrofit retrofit) {
        // Display Vod
    }

   @Override
   public void onFailure(Throwable t) {
        // failure
   }
});


Retrofit에 Rx를 같이 이용하면 아래와 같이 처리할 수 있습니다.

코드도 늘어나고 더 복잡해 보입니다. 단순히 API를 요청하고 응답받는 로직에서는 굳이 Rx까지 이용할 필요는 없어 보입니다.

// Retrofit + Rx
Single<Vod> single = rxAPI.getVod();

single 
   .subscribeOn(Schedulers.io())
   .observeOn(AndroidSchedulers.mainThread())
   .subscribe(new DisposableSingleObserver<Vod>() {
   @Override
   public void onSuccess(Vod value) {
        // Display Vod
    }
   @Override
   public void onError(Throwable e) {
        // failure
   }
});




#05-1 Call API from another API


#03 Call API from another API


하나의 API response를 기반으로 다른 API를 호출하는 경우가 발생할 수 있습니다.

(메쉬업에서도 이용할 수 있습니다.)


1. Vod 영상 정보를 가져오기 위한 Vod API 호출

2. Vod API의 response(String VodId)를 기반으로 PlayUrl API 호출

3. PlayUrl API의 reponse(String url)을 이용해 Vod 영상 재생


이를 Retrofit 만으로 만든다면 아래와 같이 onResponse에서 PlayUrl API를 호출해야 합니다.

// Retrofit
Call<Vod> callVod = retrofitAPI.getVod();

callVod.enqueue(new Callback<Vod>) {
   @Override
   public void onResponse(Response<Vod> response, Retrofit retrofit) {
        // call play url
        Call<PlayUrl> callPlayUrl = retrofitAPI.getPlayUrl(response.getVodId);

        callPlayUrl .enqueue(new Callback<String>) {
           @Override
           public void onResponse(Response<String> response, Retrofit retrofit) {
                // Play Vod
            }

           @Override
           public void onFailure(Throwable t) {
                // failure
            }
        });
    }

    @Override
    public void onFailure(Throwable t) {
        // failure
    }
});


만약 이런 경우가 한 번이 아닌 여러 번 수행되어야 한다면, 다중 Callback 상황이 일어나게 됩니다.

안드로이드에서는 주요 컴포넌트(Actvitiy 등)의 생명주기와 관련된 처리가 필수입니다.

이러한 환경에서 다중 Callback은 NullPointException이나 메모리 누수가 발생하기 쉽기 때문에 신경 쓸 부분이 많게 됩니다.

Callback() {
    Callback() {
        Callback() {
            Callback() {
                ....
            }
        }
    }
}


Rx를 이용하면 아래와 같이 처리할 수 있습니다.

#04 Call API from another API with Rx
Single<Vod> singleVod = rxAPI.getVod();
Single<String> singlePlayUrl = singleVod.flatMap(Vod -> {
        return rxAPI.getUserTweets(Vod.getVodId());
    });


다중 Callback도 쉽게 처리할 수 있습니다.

#05 Multiple Call API from another API with Rx
Single<Vod> singleVod = rxAPI.getVod();
Single<SomeObject> something = singleVod.flatMap(Vod -> {
        return rxAPI.getUserTweets(Vod.getVodId());
    })
    .flatMap({
        return ...
    })
    .flatMap({
        return ...
    })
    .flatMap({
        return ...
    })
    .flatMap({
        return ...
    });





#05-2 Combine APIs


#06 Combine APIs


여러 API response 들을 결합하여 한번에 처리해야 하는 경우도 있습니다.


1. Vod와 Live 정보를 결합하여 한번에 보여주기 위해 Vod API와 Live API 호출

2. Vod API response와 Live API response가 올 때까지 대기 

3. 두 API response를 결합


이를 Retrofit 만으로 만든다면 아래와 같이 별도의 변수를 이용할 수 도 있습니다.

개발자마다 구현 방법은 다르겠지만 개인적으로 좋아하는 로직은 아닙니다.

// Retrofit
int responseCount = 0;

Call<Vod> callVod = retrofitAPI.getVod();
Call<Vod> callLive = retrofitAPI.getLive();

call.enqueue(new Callback<Vod>) {
    @Override
    public void onResponse(Response<Vod> response, Retrofit retrofit) {
        if (responseCount == 2) {
            // Display Vod & Live
        } else {
            responseCount++;
        } 
    }

    @Override
    public void onFailure(Throwable t) {
        // failure
    }
});


call.enqueue(new Callback<Live>) {
    @Override
    public void onResponse(Response<Live> response, Retrofit retrofit) {
        if (responseCount == 2) {
            // Display Vod & Live
        } else {
            responseCount++;
        }  
    }

    @Override
    public void onFailure(Throwable t) {
        // failure
    }
});


Rx를 이용하면 아래와 같이 처리할 수 있습니다.

#07 Combine APIs with Rx
Single<Vod> singleVod = rxAPI.getVod();
Single<Live> singleLive = rxAPI.getLive();

Single<VodAndLive> singleVodAndLive
    = Single.zip(singleVod, singleLive, SingeVodAndLive::new);





네트워킹 모듈이라는 주제를 잡고 글을 썼지만 사실 Callback 처리에 대한 내용들입니다.

소위 Callback 지옥에서 벗어나게 해주는 것만으로도 Rx를 이용할만한 가치가 있다고들 하는데, 저도 많이 느꼈습니다.

저도 아직 배워가는 단계라 이 두 가지의 예를 들었는데, 이 외에도 네트워킹 모듈에 적용하기 좋은 부분이 있으면 피드백 부탁드립니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari