목차
RxJava empty: 값 없이 정상 완료
Flowable.empty()는 onNext를 호출하지 않고 바로 onComplete를 보낸다. “데이터가 없다”와 “작업이 실패했다”를 구분하는 정상 결과다. RxJava 2는 null 값을 onNext로 보낼 수 없으므로 빈 결과를 표현할 때 null을 통지하려고 하면 안 된다.

그림에는 값 원이 없고 마지막의 완료선만 있다. never에는 이 완료선이 없고 error에는 완료선 대신 X가 있다. 출력 값만 보면 셋 모두 비어 보일 수 있지만, 다음 연산자로 흐름이 이어질 수 있는지는 다르다.
구독 시 일어나는 일
import io.reactivex.Flowable;
public class EmptyExample {
public static void main(String[] args) {
System.out.println("구독 전");
Flowable.<String>empty().subscribe(
value -> System.out.println("값: " + value),
error -> System.out.println("오류: " + error),
() -> System.out.println("완료")
);
System.out.println("구독 후");
}
}
별도 스케줄러가 없으므로 예상 순서는 구독 전 → 완료 → 구독 후다. subscribe가 동기적으로 완료 신호를 전달하고 돌아온다. empty() 자체는 백그라운드 작업이나 대기를 만들지 않는다. 같은 코드에서 never()를 쓰면 가운데 완료가 사라진다.
concat과 대체값에서의 역할
Flowable.concat(
Flowable.just("A"),
Flowable.<String>empty(),
Flowable.just("B")
).subscribe(System.out::println); // A, B
Flowable.<String>empty()
.switchIfEmpty(Flowable.just("검색 결과 없음"))
.subscribe(System.out::println); // 검색 결과 없음
첫 예제에서 concat은 A 소스가 완료된 뒤 빈 소스를 구독한다. 빈 소스는 즉시 완료하므로 다음 B 소스로 이동한다. 두 번째 예제의 switchIfEmpty는 원본이 값을 하나도 내지 않고 완료한 경우에만 대체 소스를 구독한다. 원본이 오류를 내면 빈 결과가 아니므로 이 대체 경로로 가지 않는다. 한 개의 기본값만 필요하면 defaultIfEmpty("검색 결과 없음")도 선택할 수 있다.
입력 값 중 조건에 맞지 않는 것만 빼고 싶으면 flatMap 안에서 빈 내부 스트림을 반환할 수 있다.
Flowable.just("A", "", "B")
.flatMap(value -> value.isEmpty()
? Flowable.<String>empty()
: Flowable.just(value))
.subscribe(System.out::println); // A, B
다만 이 경우처럼 단순한 제외는 .filter(value -> !value.isEmpty())가 더 명확하다. empty()를 외부 시스템의 예외를 숨기기 위한 일반적인 대체로 쓰면 운영에서 장애를 정상적인 빈 결과로 착각할 수 있다. 오류가 정상적인 부재인지, 실패인지 먼저 정해야 한다.
즉시 완료하는 소스에는 오래 유지할 작업이 없지만, empty()를 포함한 상위의 비동기 조합은 구독 수명이 남을 수 있다. 전체 체인의 취소는 실제 작업을 하는 다른 소스까지 고려해 관리한다.
