예제를 통해 좀 더 operator에 대해서 설명하겠습니다.
아래와 같은 매소드가 있다고 가정합니다.
1 2 | // text에 기반한 검색 된 website URL 리스트를 반환 Observable<List<String>> query(String text); | cs |
text 검색을 통한 결과를 보여주는 시스템을 만들려고 합니다.
이전에 본 매소드를 이용하면 다음과 같은 결과가 나올 수 있습니다.
1 2 3 4 5 6 | query("Hello, world!") .subscribe(urls -> { for (String url : urls) { System.out.println(url); } }); | cs |
이러한 구조는 데이터 스트림을 변환하는 기능이 없어지므로 매우 불만스러울 수 있습니다. 만약 각각의 URL에 대해서 수정을 하고 싶다면 이것을 Subscriber 에서 진행하여야 합니다.
map()에서 urls -> urls을 만들 수 있었지만 map() 은 안에서 for-each 루프를 생성합니다.
from
1 2 | Observable.from("url1", "url2", "url3") .subscribe(url -> System.out.println(url)); | cs |
이것이 어떤식으로 좀 더 나은지 보겠습니다.
1 2 3 4 5 | query("Hello, world!") .subscribe(urls -> { Observable.from(urls) .subscribe(url -> System.out.println(url)); }); | cs |
for-each 루프는 없지만 코드는 보기 좋지 않을 뿐더러 수정하기도 힘듭니다.
더 나은 방법
Observable.flatMap() 은 Observable의 결과를 다른 스트림으로 방출한다.
1 2 3 4 5 6 7 8 | query("Hello, world!") .flatMap(new Func1<List<String>, Observable<String>>() { @Override public Observable<String> call(List<String> urls) { return Observable.from(urls); } }) .subscribe(url -> System.out.println(url)); | cs |
위에 코드를 람다로 표현하게 되면 아래와 같습니다.
1 2 3 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .subscribe(url -> System.out.println(url)); | cs |
왜 다른 Observable로 돌려주는건가?
여기서 중요한 핵심 개념은 새로운 Observable을 Subscriber가 보는 것입니다.
이것은 List<String>을 받는 것이 아니라 일렬의 개인적인 String을 돌려 받습니다.
이 부분은 이해하기가 어려워 설명이 이상한점 양해 바랍니다.
조금더 예를 들어 보겠습니다.
아래와 같은 메소드가 있다고 가정하겠습니다.
1 2 | // 해당 URL에 대한 title을 반환한다. 없다면 null를 반환 Observable<String> getTitle(String URL); | cs |
이 메소드를 통해 각 웹사이트의 제목을 출력하고 싶습니다. 하지만 이 메소드는 한번에 한 URL에 대해서만 작동하며
String을 반환하지 않고 Observable이 String을 반환합니다.
flatMap() 을 사용하면 이 문제를 쉽게 해결 할 수 있습니다. URL의 목록을 개개의 아이템으로 나눈후 flatMap() 안에서 getTitle()을
하면 됩니다.
1 2 3 4 5 6 7 8 9 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(new Func1<String, Observable<String>>() { @Override public Observable<String> call(String url) { return getTitle(url); } }) .subscribe(title -> System.out.println(title)); | cs |
한번더 람다를 통해 단순화.
1 2 3 4 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .subscribe(title -> System.out.println(title)); | cs |
이외의 Operators
1 2 3 4 5 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .subscribe(title -> System.out.println(title)); | cs |
filter() 은 부울 체크가 된 아이템에 대해서만 방출합니다.
다음으로 오직 5개의 결과만 보여주고 싶습니다.
이 경우에는 다음과 같이 작성하면 됩니다.
1 2 3 4 5 6 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .take(5) .subscribe(title -> System.out.println(title)); | cs |
각각의 Title에 대해 디스크에 저장하고 싶은 경우
1 2 3 4 5 6 7 | query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .take(5) .doOnNext(title -> saveTitle(title)) .subscribe(title -> System.out.println(title)); | cs |
doOnNext() 은 항목이 방출 될 때마다 추가 동작을 추가 할 수 있습니다.
Key idea #3: Operator는 데이터 스트림에 대해 무엇이든 할 수 있다.
'Language > Java' 카테고리의 다른 글
JVM 구조 (0) | 2019.03.07 |
---|---|
RxJava란? -4 Scheduler (0) | 2019.01.26 |
RxJava란 - 2 Operators (0) | 2019.01.26 |
RxJava란? - 기본 구조 (5) | 2019.01.26 |
JDBC- MariaDB와 Java연동 (0) | 2019.01.02 |