티몬 안드로이드 앱에서 한 화면에 여러 API 응답을 순서대로 배치해야 하는 문제를 RxAndroid로 정리한 사례다. 플래그를 세워 모든 응답을 기다리던 코드가 API가 늘 때마다 복잡해지자 zip 연산자로 바꿨다. Observable 생성부터 스케줄러 지정, 구독 해제까지 단계별 코드가 붙는다.
핵심 포인트- 화면에 노출할 정보가 늘면서 비동기로 호출할 API가 많아졌고 뷰는 정해진 순서로 배치해야 했다.
- 처음에는 응답마다 플래그를 세우고 매번 전체 플래그를 확인하는 방식이라 중복 코드가 쌓였다.
- zip은 모든 Observable이 최소 한 번씩 아이템을 발행할 때마다 결합 함수를 실행한다.
- 그래서 API 응답이 실패해도 빈 데이터를 담아 onNext를 호출해야 결합이 진행된다.
- RxJava 2에서는 onNext에 null을 넘기면 예외가 나므로 빈 데이터임을 알 수 있는 아이템을 발행해야 한다.
- subscribeOn은 구독이 실행되는 스레드를, observeOn은 그 뒤에 오는 연산자와 구독자를 바꾼다.
상세 정리- 배경: 초기에는 한 화면에서 같은 패턴의 목록과 배너 몇 개를 보여주는 단순한 구조였다.
- 변화: 이벤트 배너와 가격 목록, 기획전 목록처럼 뷰가 다양해지면서 호출할 API가 늘었다.
- 제약: 모든 API는 비동기로 호출하되 각 뷰는 정해진 순서에 맞게 배치돼야 했다.
- 첫 구현: 모든 API를 호출하고 응답마다 플래그를 세운 뒤, 플래그가 바뀔 때마다 전체를 확인해 다 모였으면 순서대로 어댑터에 넣었다.
- 첫 구현의 문제: 동작에는 문제가 없었지만 중복 코드가 많아지고 API가 추가될 때마다 신경 쓸 부분이 늘었다.
- 1단계 Observable 만들기: 각 API 호출을 감싸 응답을 받으면 onNext와 onComplete를 부르는 Observable을 만들어 목록에 담는다.
- 실패 처리: 응답을 정상적으로 받지 못한 경우에도 빈 데이터를 발행해 스트림이 끊기지 않게 한다.
- 버전 차이: RxJava 1에서는 onNext에 null이 허용되지만 2에서는 NullPointerException이 나므로 빈 데이터 객체를 따로 두어야 한다.
- 2단계 결합: Observable 목록을 zip으로 묶고 결합 함수에서 배열로 들어온 아이템들을 하나의 데이터 집합으로 가공해 구독자에게 넘긴다.
- zip의 동작: 각 스트림이 한 번씩 발행할 때마다 결합 함수가 돌며 그 결과가 하나의 아이템으로 나간다.
- 3단계 스케줄러: 구독 로직에 API 호출이 있으므로 subscribeOn에는 I/O에 적합한 스케줄러를, 이후 UI 작업이 있으므로 observeOn에는 메인 스레드를 준다.
- 적용 범위의 차이: subscribeOn은 호출 순서와 무관하게 최상단 Observable에 적용되고, observeOn은 바로 다음에 오는 연산자부터 적용된다.
- 클라이언트별 주의: 예제에 쓴 HTTP 클라이언트는 콜백을 메인 스레드에서 돌려주지만 클라이언트마다 다르므로 명시하는 편이 안전하다고 덧붙인다.
- 4단계 구독: subscribe로 시작하고 onNext에서 받은 데이터로 어댑터를 세팅해 화면에 그린다.
- 자원 정리: onSubscribe에서 받은 Disposable을 CompositeDisposable에 모아두고 화면이 소멸될 때 한꺼번에 비워 메모리 누수를 막는다.
- 최종 형태: zip으로 묶고 스케줄러를 지정한 뒤 구독하는 네 줄의 체이닝으로 정리된다.
- 도입 소회: 학습 곡선이 가파르고 참고 자료가 많지 않아 고생했으니 작은 부분부터 단계적으로 적용하기를 권한다.
왜 읽나여러 API 응답을 다 모아야 화면을 그릴 수 있는 구조에서 플래그 관리로 버티고 있다면 zip과 스케줄러 조합으로 정리하는 최소 형태를 볼 수 있다.