요즘 세상에 어느정도 기능이 있는 앱이라면, 서버와의 통신이 없을 수 없다.
클라이언트에 수많은 데이터를 저장해둔다던가, 고성능이 필요한 연산을 실행할 수는 없기 때문이다.
그래서 안드로이드 개발의 경우 보통 OkHttp + Retrofit를 사용해서 통신을 한다.
또한 보통 RxJava 또는 CoRoutine을 이용해서 비동기 처리를한다.
서버에 보내는 요청은 CRUD 모두 단건인 경우가 대부분이다.
혹시 단건이 아니더라도, 리퀘스트를 List
나 Array
에 담아보낼 수 있을 것이다.
그래서 (다중 요청 + 순서 유지)의 경우도 서버단에서 알아서 처리해주기도 한다.
하지만, 일이라는 것이 언제나 마음대로 되는게 아니다보니, 클라이언트에서 이 모든 것을 해결해야할 수도 있다.
그러니까 다중 요청을 복수의 단건 요청으로 처리하면서 순서유지까지 해야할 수도 있다.
그러면 이미지를 단건으로 나누어서 업로드 요청을 하고 그 결과의 순서를 유지하는 시나리오를 생각해보자.
List
로 만든 다음 재사용한다.RxJava를 사용해왔다면 이런 경우 속칭 국밥인 flatMap
을 생각할 수 있다.
그러나 flatMap
는 결과물의 순서를 보장하지 못한다.
그러므로 순서를 맞추려면 별도의 작업이 필요하다.
예를 들면, 아이템의 순서를 별도로 저장해뒀다가 나중에 정렬할 수 있을 것이다.
예를 들면, Pair<Item, Int>
로 RxChain을 타다가 필요할 때 Pair.second
를 기준으로 정렬한다.
하지만 그런식으로 구현할 경우, 가독성에도 안좋고, 별도로 정렬도 해야하므로 결코 좋다고 할 수 없다.
그래서 이번에는 concatMap
를 사용해보자.
쉽게 생각하면 concatMap
은 순서를 맞춰주는flatMap
이라고 생각하면 된다.
그러므로 아래와 같이 코딩하면 된다. (가볍게 의미만 읽어주면 된다.)
Observable.fromIterable(imageFiles)
.concatMapSingle {
api.uploadImage(it.toMultiPart())
}
.toList()
하지만 위처럼 구현하면 flatMap
에서 누릴 수 있던 단점 하나를 포기하게 된다.
concatMap
을 쓰면, 순서를 맞춰야 하는 점 때문인지 요청이 병렬적으로 가지 않는다.
즉 concatMap
을 쓰면 실질적으로는 Non-Blocking이 아니라 Blocking이 된다.
이러면 서버 응답을 기다리지 않고 요청을 보낼 수도 있다는 점에서 비효율이 발생할 수 있다.
그래서 이번엔 concatMapEager
를 사용해보자.
concatMapEager
는 concatMap
과 같은 결과를 내지만, 한번에 source를 전부 subscribe한다.
다른 점은 내부적으로 buffer를 이용, 순서를 맞춰서 결과를 배출한다.
참고로 concatMapEager
의 경우 concatMap
과 달리, concatMapEagerSingle
같은건 없다.
그러므로 Single
에 toObservable()
을 걸어주자.
Observable.fromIterable(imageFiles)
.concatMapEager {
api.uploadImage(it.toMultiPart()).toObservable()
}
.toList()