본문 바로가기

Project/HiddenGem

🤔 MVVM 패턴에서 2개 이상의 값의 변화를 감지해야하는 경우에는?(CombineLatest)

https://developer.apple.com/documentation/combine/just/combinelatest(_:)

 

combineLatest(_:) | Apple Developer Documentation

Subscribes to an additional publisher and publishes a tuple upon receiving output from either publisher.

developer.apple.com

 

✅ combineLatest(_:)란?

combineLatest는 두 개 이상의 Publisher가 내보내는 최신 값을 묶어서 하나로 전달해주는 Combine의 연산자야.

 

🧠 개념 먼저 이해해보기

let publisherA = Just("🍎")
let publisherB = Just("🍌")

publisherA.combineLatest(publisherB)

 

→ 결과는: ("🍎", "🍌")

즉, A와 B 각각의 최신 값을 묶어서 한 번에 보내줌

 

 

⏱ 중요한 동작 원칙

combineLatest는 두 Publisher 모두 값이 한 번 이상 방출되어야 동작

예를 들어:

  • A만 값이 방출되고 B는 아직 안 했으면 → 아무 일도 안 일어남 ❌
  • A와 B 둘 다 값을 방출하면 → 그때부터 결과가 출력됨 ✅

 

🧩 왜 쓸까?

MVVM에서 ViewModel에서 여러 개의 데이터를 받아야 할 때 아주 유용함

private func bindViewModel() {

    Publishers.CombineLatest(
        categoriesViewModel.$emojiCategories,
        eateriesViewModel.$eateries
        )
    .receive(on: DispatchQueue.main)
    .sink { [weak self] _, _ in
        self?.reloadData()
    }
    .store(in: &cancellables)

}

 

→ emojiCategories, eateries 중 하나라도 바뀌면 최신 상태로 묶어서 처리 가능!

 

🔍 명시적 타입 방식 VS 체이닝 방식

✅ 1. Publishers.CombineLatest(...) (명시적 타입 사용)

Publishers.CombineLatest(
    categoriesViewModel.$emojiCategories,
    eateriesViewModel.$eateries
)
.receive(on: DispatchQueue.main)
.sink { [weak self] _, _ in
    self?.reloadData()
}
.store(in: &cancellables)

 

✅ 2. .combineLatest(...) (체이닝 방식)

categoriesViewModel.$emojiCategories
    .combineLatest(eateriesViewModel.$eateries)
    .sink { emojiCategories, eateries in
        print("카테고리: \(emojiCategories.count), 음식점: \(eateries.count)")
    }
    .store(in: &cancellables)

 

 

위의 두 방식의 차이는 체이닝 방식의 가독성이 좀 더 자연스럽다는 점이 있지만, 확장성 면에서는 명시적 타입을 사용하는게 유리함

따라서 Publisher가 2개만 다룰 경우에는 체이닝 방식이 좀 더 유리하나, 3개 이상을 다루는 경우에는 명시적 타입을 사용하는 부분이 유리함 다만, Publisher를 조합한 뒤 .map, .filter 등 추가 조작이 필요할 때는 체이닝 방식이 유리