관리 메뉴

나만을 위한 블로그

[Rxjava] Single, Maybe, Completable이란? 본문

개인 공부/Rxjava

[Rxjava] Single, Maybe, Completable이란?

참깨빵위에참깨빵 2021. 11. 17. 20:20
728x90
반응형

제목의 3가지는 모두 Observable의 변형이다. 즉, Observer가 데이터를 처리할 수 있도록 데이터를 내보내는 역할을 하는 생성기라는 것이다. Observable을 아직 모른다면 예전에 작성한 포스팅이 있으니 참고하고 먼저 보고 오는 걸 추천한다.

 

https://onlyfor-me-blog.tistory.com/326

 

[Rxjava] Observable이란? - 1 -

이제 코틀린과 같이 Rxjava를 공부할 건데, 어디서부터 공부할지가 막막해서 구글을 돌아다니다가 발견한 아래와 같은 순서로 한번 시도해보려고 한다. https://zzandoli.tistory.com/24 [RxJava] RxJava 학습

onlyfor-me-blog.tistory.com

 

Observable과 Single은 Rxjava 1버전부터 존재했던 것이고, Maybe와 Complete는 Rxjava 2부터 사용할 수 있게 됐다.

이 3가지는 왜 만들어졌을까? Observable만으로 충분하지 않았던 건지, 또는 무슨 문제가 있던 것인가? 3가지의 특징을 확인하면서 생각해보자.

 

Single

 

Single은 "단 하나의, 단일의" 라는 사전적 정의를 갖고 있으며 Rxjava 2가 나오면서 처음부터 다시 설계됐다. 이 말의 근거는 아래의 Rxjava 공식 깃허브 링크에서 Single을 문단을 확인해보면 알 수 있다.

 

https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#single

 

GitHub - ReactiveX/RxJava: RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based p

RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM. - GitHub - ReactiveX/RxJava: RxJava – Reactive E...

github.com

 

Single이 Observable의 변형이라는 걸 감안하면, 데이터를 1개만 방출하기 때문에 이 이름이 붙은 게 아닌가 하고 상상해 볼 수 있다.

먼저 ReactiveX 공식 홈페이지의 Single 문서부터 확인하고 가자.

 

http://reactivex.io/documentation/ko/single.html

 

ReactiveX - Single

 

reactivex.io

Rxjava는 Observable과 유사한 Single을 제공한다. Single은 Observable의 한 형태지만, Observable처럼 존재하지 않는 곳에서부터 무한대까지의 임의의 연속된 값들을 배출하는 것과 달리 항상 한 가지 값 또는 오류 알림 둘 중 하나만 배출한다. 이런 이유 때문에 Single을 구독할 때는 Observable을 구독할 때 쓰는 3가지 메서드(onNext, onError, onComplete) 대신 아래 2가지 메서드만 쓸 수 있다.

- onSuccess() : Single은 자신이 배출하는 하나의 값을 이 메서드를 통해 전달한다
- onError() : Single은 아이템을 배출할 수 없을 때 이 메서드를 통해 Throwable 객체를 전달한다.

Single은 이 메서드 중 하나만 단 한 번 호출한다. 메서드가 호출되면 Single의 생명주기는 끝나고 구독도 종료된다.

 

데이터를 1개만 방출하거나 오류 1개만을 방출하기 때문에 Single이란 이름이 붙은 것 같다.

데이터가 전달된다면 onSuccess() 안에 넣어 보내고, 데이터를 전달할 수 없다면 onError() 안에 Throwable 객체를 매개변수로 전달한다. 또한 메서드가 호출됐다면 그 즉시 Single은 종료된다. 간단한 예제로 확인해보자.

에뮬레이터나 핸드폰에 연결해 결과를 확인하기 귀찮기 때문에 안드로이드 스튜디오에서 유닛 테스트를 수행하는 파일에서 진행했다.

 

import org.junit.Test;

import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleEmitter;
import io.reactivex.rxjava3.core.SingleOnSubscribe;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Single.just(1)
                .subscribe(System.out::println);
    }
}

 

반복해서 말하지만 Single은 단 하나의 아이템만 발행하기 때문에, just()는 하나의 인자만 가질 수 있다.

위 메서드를 실행하면 1이 출력되는 걸 볼 수 있다. 그럼 create()를 쓴다면 어떨까?

 

import org.junit.Test;

import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleEmitter;
import io.reactivex.rxjava3.core.SingleOnSubscribe;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Single.create(emitter -> emitter.onSuccess("안녕"))
                .subscribe(System.out::println);
    }
}

 

create()를 쓰는 경우 Emitter를 써서 데이터를 발행한다. 또한 데이터는 1번만 발행되므로 onSuccess()로 onNext(), onComplete()를 대체한다.

왜 Emitter를 써서 발행하냐면, 위의 create() 예시의 원형은 아래와 같기 때문이다.

 

import org.junit.Test;

import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleEmitter;
import io.reactivex.rxjava3.core.SingleOnSubscribe;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Single<Integer> single = Single.create(new SingleOnSubscribe<Integer>() {
            @Override
            public void subscribe(@NonNull SingleEmitter<Integer> emitter) throws Throwable {
                emitter.onSuccess(1);
            }
        });
        single.subscribe(System.out::println);
    }
}

 

이 코드를 줄여서 단 2줄로 간결하게 표현한 것이다. subscribe() 전에 개행문자를 넣는 습관이 있어 2줄이 됐지만 따지고 보면 1줄이다.

콜백으로 호출되는 subscribe() 안의 매개변수를 잘 보면 SingleEmitter<Integer> emitter라는 게 보인다. 그래서 람다식으로 간결하게 표현할 경우 emitter를 사용한다. 물론 다른 이름을 써도 상관없다.

여기서 "emitter -> emitter."까지 입력하면 아래와 같은 자동완성 리스트를 볼 수 있다.

 

 

그러나 때에 따라선 Single도 Observable로 바꿔 써야 할 때가 있을 수도 있다. 그 때는 toObservable()을 써서 Observable로 바꿔주면 된다.

 

import org.junit.Test;

import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Single<Integer> single = Single.just(1);
        Observable<Integer> observable = single.toObservable();
    }
}

 

observable 변수에 중단점을 찍고 디버깅해보면 1이 들어가 있는 걸 알 수 있다.

 

 

값을 보내거나 말거나 둘 중 하나만 하는 클래스니까 HTTP 요청과 응답 이벤트를 처리할 때 쓰면 유용할 듯 하다.

 

Maybe

 

그런데 사람들이 Single로는 만족을 못하고 Maybe라는 걸 만들었다.

Maybe는 "어쩌면, 아마, 혹시" 라는 사전적 정의가 있는데 이것만으론 왜 Maybe란 이름이 붙었는지 모르겠다. 공식 홈페이지와 다른 곳의 글들을 확인해보자.

 

http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Maybe.html

 

Maybe (RxJava Javadoc 3.1.2)

Converts a Future into a Maybe, with a timeout on the Future. The operator calls Future.get(long, TimeUnit), which is a blocking method, on the subscription thread. It is recommended applying subscribeOn(Scheduler) to move this blocking wait to a backgroun

reactivex.io

Maybe 클래스는 하나의 값의 지연된 계산과 방출(값이 없거나 예외)를 나타낸다. MaybeSource base 인터페이스를 구현하며 이 클래스가 상호작용하는 기본 소비자(Consumer) 유형은 subscribe(MaybeObserver)를 통한 MaybeObserver다

 

https://www.baeldung.com/rxjava-maybe

 

RxJava Maybe | Baeldung

A quick and practical introduction to Maybe in RxJava.

www.baeldung.com

Maybe는 0 또는 1개의 항목만 방출할 수 있는 Observable이며 특정 지점에서 계산 실패 시 오류를 보고한다. 이 점에서 Single과 Completable의 결합과 같다. Maybe를 비롯한 모든 축소 유형은 Flowable 연산자의 하위 집합을 제공한다. 이는 작업이 0 또는 1인 항목에 대해 의미있는 한 Flowable처럼 Maybe로 작업할 수 있음을 의미한다. 하나의 값만 방출할 수 있기 때문에 Flowable처럼 배압을 처리하진 않는다

 

https://jeongupark-study-house.tistory.com/41

 

RxJava, RxKotlin - Observable (2) - Single , Maybe

[출처 - RxJava 프로그래밍 : 리액티브 프로그래밍 기초부터 안드로이드 까지 한번에] 본 글은 'RxJava 프로그래밍 : 리액티브 프로그래밍 기초부터 안드로이드 까지 한번에' 를 학습하면서 정리한

jeongupark-study-house.tistory.com

Maybe 클래스는 Single 클래스와 비슷하다. 최대 하나의 데이터를 가질 수 있기 때문이다. 그러나 데이터 발행 없이 바로 데이터 발행을 완료할 수 있다. 즉, Maybe는 Single에 onComplete()가 추가된 형태다
Maybe 객체는 보통 Observable의 특정 연산자를 통해 생성할 때가 많다.

 

https://irontech.tistory.com/23

 

RxJava - 또 다른 생산자 Single, Maybe와 Completable

목표 지난 포스팅에서 Flowable과 Observable을 사용해왔습니다. 이번 포스팅에서는 또 다른 생산자인 Single, Maybe 그리고 Completable을 사용해보며 익숙해지도록 합니다. 또한 어떠한 차이점이 있는지

irontech.tistory.com

Maybe
- 데이터를 1개만 통지하거나 통지하지 않고 완료 또는 에러를 통지한다
- 데이터 통지 자체가 완료를 의미하기 때문에 완료 통지는 하지 않는다
- 데이터를 통지하지 않고 처리가 종료되면 완료 통지를 한다
- Maybe의 대표적인 소비자(Consumer)는 Observer 인터페이스를 구현한 MaybeObserver다

 

Single과 비슷한데 발행하지 않을 수도 있다는 점에서 Single과 차이가 있다.

Maybe는 아이템을 발행할 때는 onSuccess()를 호출하고 발행하지 않을 때는 onComplete()를 호출하기 때문에 onSuccess() 호출 시에 onComplete()를 호출할 필요는 없다. 아래는 예제 코드다.

 

import org.junit.Test;

import io.reactivex.rxjava3.core.Maybe;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Maybe.create(emitter -> {
            emitter.onSuccess(100);
            emitter.onComplete();   // 무시됨
        }).doOnSuccess(item -> System.out.println("doOnSuccess1"))
                .doOnComplete(() -> System.out.println("doOnComplete1"))
                .subscribe(System.out::println);

        Maybe.create(emitter -> emitter.onComplete())
                .doOnSuccess(item -> System.out.println("doOnSuccess2"))
                .doOnComplete(() -> System.out.println("doOnComplete2"))
                .subscribe(System.out::println);
    }
}

 

아래는 공식 홈페이지에 올라와 있는 예제다.

import org.junit.Test;

import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.observers.DisposableMaybeObserver;
import io.reactivex.rxjava3.schedulers.Schedulers;

public class ExampleUnitTest {
    @Test
    public void testRxjava() throws InterruptedException {
        Disposable d = Maybe.just("Hello World")
                .delay(10, TimeUnit.SECONDS, Schedulers.io())
                .subscribeWith(new DisposableMaybeObserver<String>() {
                    @Override
                    public void onStart() {
                        System.out.println("Started");
                    }

                    @Override
                    public void onSuccess(String value) {
                        System.out.println("Success: " + value);
                    }

                    @Override
                    public void onError(Throwable error) {
                        error.printStackTrace();
                    }

                    @Override
                    public void onComplete() {
                        System.out.println("Done!");
                    }
                });
        Thread.sleep(1000);
        d.dispose();
    }
}

 

마지막으로 Completable에 대해 확인해봤다. 이것은 "전부 갖출 수 있는, 완성할 수 있는"이란 뜻이 있는 단어다.

 

http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Completable.html

 

Completable (RxJava Javadoc 3.1.2)

Returns a Completable which given a Publisher and when this Completable emits an error, delivers that error through a Flowable and the Publisher should signal a value indicating a retry in response or a terminal event indicating a termination. Note that th

reactivex.io

Completable 클래스는 값이 없고 완료 or 예외 표시만 있는 지연된 계산을 나타낸다. Completable은 완료 or 오류 신호만 내보낼 수 있다는 걸 제외하고 Observable과 비슷하게 작동한다. 다른 반응 유형과 마찬가지로 onNext, onSuccess가 없다. Completable 클래스는 CompletableSource base interface를 구현하고 상호작용하는 소비자 유형은 subscribe(CompletableObserver)를 통한 CompletableObserver다.

 

https://4z7l.github.io/2020/12/07/rxjava-3.html

 

[RxJava] RxJava 이해하기 - 3. Single, Maybe, Completable - HERSTORY

Single 개념 Single은 Observable의 변형된 형태이다. Observable과 비슷하지만, 여러 개의 데이터를 발행할 수 있는 Observable과 달리 Single은 한 개의 데이터(혹은 에러)만을 발행한다. 이전 포스트에서 말

4z7l.github.io

Completable은 데이터를 발행하는 Observable, Single, Maybe와 달리 데이터 발행의 완료 / 에러 신호만 보내는 특수 형태다. 따라서 데이터 발행 완료를 알리는 onComplete()와 에러 발생을 알리는 onError() 2가지 알림을 내보낸다

 

Completable은 아이템 발행은 관심없고 실행이 정상 종료됐는지에만 관심있다. 그래서 onNext, onSuccess가 없고 onComplete(), onError()만 있다.

 

import org.junit.Test;

import io.reactivex.rxjava3.core.Completable;

public class ExampleUnitTest {
    @Test
    public void testRxjava() {
        Completable.create(emitter -> {
            System.out.println("emitter 박스 안");
            emitter.onComplete();
        }).subscribe(() -> System.out.println("completed1"));

        Completable.fromRunnable(() -> {
            System.out.println("fromRunnable() 박스 안");
        }).subscribe(() -> System.out.println("completed2"));
    }
}

// >> emitter 박스 안
// >> completed1
// >> fromRunnable() 박스 안
// >> completed2

 

3가지 생성자의 결론은 아래와 같다.

 

이름 특징
Single 데이터를 발행하거나 오류를 뿜거나 둘 중 하나만 한다
Maybe 데이터가 발행되지 않더라도 완료(종료)될 수 있다
Completable 데이터 발행 따위 관심없고 정상 종료됐는지에만 관심있다

 

Single은 안드로이드 개발하는 입장에서 좀 쓸만해 보이지만 Maybe와 Completable은 당최 어디에 써먹으면 좋을지 모르겠다. 아직 공부가 부족해서 그런 것일테니 계속 공부해봐야겠다.

반응형
Comments