카테고리 없음

RxJava에서 merge, zip, combineLatest 차이점과 사용법 총정리

Kyung_Development 2025. 6. 20. 12:23

 

RxJava를 사용하다 보면 여러 Observable(또는 Single 등)을 조합해서 처리해야 할 때가 많습니다.
그중에서 merge, zip, combineLatest 연산자는 자주 사용되는 조합 연산자입니다.
하지만 각각의 동작 방식과 특성이 달라 어떤 상황에서 어떤 것을 써야 할지 헷갈릴 수 있죠.

이번 글에서는 RxJava의 세 가지 주요 조합 연산자의 차이점과 사용 예, 그리고 언제 쓰면 좋은지 핵심만 딱 정리해드립니다.


1. merge — 여러 스트림을 한데 합쳐서 이벤트를 섞어버린다!

동작 원리

merge는 여러 Observable에서 발생하는 이벤트를 그대로 섞어서 방출합니다.
각 스트림에서 데이터가 나오는 시간 순서대로 섞여 나오며, 순서가 보장되지 않습니다.

특징

  • 여러 Observable이 독립적으로 동작하면서 동시에 이벤트를 뿜는 병렬 처리 가능
  • 이벤트 순서가 중요하지 않을 때 사용
  • 각 스트림 이벤트가 독립적일 때 효과적

언제 쓰는가?

  • 여러 API를 동시에 호출해서 응답이 도착하는 대로 처리하고 싶을 때
  • 실시간 알림, 로그 스트림 등 이벤트 순서가 중요하지 않은 경우

코드 예시

val obs1 = Observable.just("A", "B", "C").delay(100, TimeUnit.MILLISECONDS)
val obs2 = Observable.just("1", "2", "3").delay(50, TimeUnit.MILLISECONDS)

Observable.merge(obs1, obs2)
    .subscribe { println(it) }

// 출력 예시 (순서가 섞임)
// 1
// 2
// 3
// A
// B
// C

2. zip — 같은 순서의 아이템끼리 묶어서 콤보 생성!

동작 원리

zip은 여러 Observable에서 동일 인덱스에 해당하는 아이템을 하나씩 묶어서 하나의 결과를 만들어 내는 연산자입니다.
예를 들어, 첫 번째 Observable의 1번째 아이템과 두 번째 Observable의 1번째 아이템을 묶어서 첫 번째 결과를 방출합니다.

특징

  • 아이템 순서와 개수가 매우 중요
  • 모든 Observable이 현재 인덱스 아이템을 준비해야 다음 결과가 방출됨 → 동기화 효과
  • 병렬로 아이템을 생산할 수 있으나, 결과는 각 인덱스 단위로 직렬 처리되는 느낌

언제 쓰는가?

  • 여러 비동기 작업 결과를 한 세트로 묶어서 처리해야 할 때
  • 여러 API 호출의 결과를 조합해서 하나의 데이터 객체를 만들 때

코드 예시

val obs1 = Observable.just("A", "B", "C")
val obs2 = Observable.just("1", "2", "3")

Observable.zip(obs1, obs2) { s1, s2 -> "$s1$s2" }
    .subscribe { println(it) }

// 출력
// A1
// B2
// C3

3. combineLatest — 각 스트림의 최신 값들을 항상 최신 상태로 조합!

동작 원리

combineLatest는 여러 Observable 중 하나라도 새로운 값을 방출하면,
가장 최근에 방출된 각 Observable의 값을 모아서 새롭게 조합한 결과를 바로 방출합니다.

특징

  • 모든 Observable이 최소 1개의 값을 내놓은 뒤부터 작동
  • 이벤트가 발생할 때마다 즉시 최신 상태를 반영해서 결과 방출
  • 병렬로 동작하며 실시간 최신값 조합에 적합

언제 쓰는가?

  • 여러 UI 상태, 실시간 데이터 스트림 등을 조합해서 최신 상태를 반영해야 할 때
  • 폼 입력값 여러 개를 실시간으로 조합할 때
  • 사용자 입력, 센서 데이터, 여러 상태값이 변할 때마다 합쳐야 하는 경우

코드 예시

val obs1 = Observable.just("A", "B", "C").concatMap { Observable.just(it).delay(100, TimeUnit.MILLISECONDS) }
val obs2 = Observable.just("1", "2", "3").concatMap { Observable.just(it).delay(150, TimeUnit.MILLISECONDS) }

Observable.combineLatest(obs1, obs2) { s1, s2 -> "$s1$s2" }
    .subscribe { println(it) }

// 출력 예시 (시간에 따라 다름)
// A1
// B1
// B2
// C2
// C3

RxJava 조합 연산자 비교 정리

연산자 동작 방식 병렬/직렬 주요 특징 사용 시기 대표 예시

merge 여러 스트림에서 이벤트를 시간순 섞어서 방출 병렬 가능 순서 보장 X, 이벤트 섞임 독립적 이벤트를 순서 상관없이 처리할 때 여러 API 응답을 도착하는 대로 처리
zip 같은 인덱스 위치 아이템끼리 묶어서 방출 병렬 가능, 결과는 인덱스별 직렬 처리 각 스트림의 아이템 개수, 순서 중요 동기화된 데이터 세트를 만들어야 할 때 여러 API 결과를 하나의 객체로 조합
combineLatest 각 스트림의 최신 값을 실시간 조합 병렬 가능 이벤트 발생 시 최신 상태를 항상 반영 실시간 UI 상태나 여러 상태값 조합 시 실시간 사용자 입력값 합치기

부가 설명 - Single에서는?

  • Single은 한 번만 결과를 내는 타입이라 merge보다는 zip을 더 자주 씁니다.
  • 예: 여러 API를 병렬 호출 후 모두 완료되면 결과를 합칠 때 Single.zip() 사용.

마무리

RxJava에서 여러 비동기 작업을 조합할 때,

  • 순서와 동기화가 중요하면 zip
  • 순서가 필요 없고 이벤트가 흘러들어오는 대로 처리하려면 merge
  • 항상 최신 상태를 유지하며 실시간 조합해야 하면 combineLatest

이렇게 이해하시면 훨씬 편하게 상황에 맞는 연산자를 선택할 수 있습니다.

RxJava를 활용한 비동기 프로그래밍, 꼭 마스터하세요! 궁금한 점 있으면 언제든 질문해주세요.