Tong's Blog

[Swift] Combine Publisher 종류 정리 + RxSwift와 비교해보기 (2편) 본문

iOS/Combine

[Swift] Combine Publisher 종류 정리 + RxSwift와 비교해보기 (2편)

통스 2025. 11. 20. 23:56
반응형

안녕하세요.

지난 글에서는 Combine의 기본 개념과 Publisher / Subscriber, 그리고 RxSwift와의 대응 관계를 간단히 살펴보았는데요.
이번 글에서는 Combine에서 자주 사용하는 Publisher 종류와, 이에 대응되는 RxSwift 컴포넌트를 함께 비교하면서 정리해보려고 합니다.

실제로 개발을 하다 보면 "어떤 Publisher를 써야 하지?", "RxSwift에서 쓰던 그 기능은 Combine에서는 뭐지?" 이런 고민이 자연스럽게 생기기 때문에 이번 내용이 앞으로의 글을 이해하는 데도 큰 도움이 될 것 같아요.

 

Publisher란 무엇인가?

Combine에서 Publisher는 값을 만들어 내는 생산자입니다.
하지만 모든 Publisher가 동일한 방식으로 값을 내보내는 건 아니고, 상황에 따라 다양한 타입이 존재합니다.

대표적으로 아래 세 가지 유형이 있습니다.

  • 한 번만 값을 내보내는 Publisher
  • 비동기 작업의 성공/실패를 표현하는 Publisher
  • 시간이나 이벤트 흐름에 따라 지속적으로 값을 내보내는 Publisher

이제 각각을 예시와 함께 살펴보고, RxSwift에서는 무엇과 대응되는지도 같이 보겠습니다.

 

1. Just – 값을 단 한 번만 방출하고 끝나는 Publisher

Combine에서 가장 기본적인 Publisher 입니다.

 

https://developer.apple.com/documentation/combine/just

 

Just | Apple Developer Documentation

A publisher that emits an output to each subscriber just once, and then finishes.

developer.apple.com

 

func testJust() {
    let publisher = Just("Hello, Combine")
    var bag = Set<AnyCancellable>()

    publisher
        .sink { value in
            print("값:", value)
        }
        .store(in: &bag)
}

// 값: Hello, Combine

 

특징

  • 반드시 하나의 값만 방출
  • 즉시 완료됨
  • 에러 타입이 Never

RxSwift 대응

  • Observable.just("Hello")

둘의 역할은 거의 동일합니다.

 

2. Empty – 아무 값도 방출하지 않고 즉시 완료되는 Publisher

Empty는 이름 그대로 값을 방출하지 않고, finished completion만 내보내는 Publisher입니다.
Combine의 체인을 구성할 때 “아무것도 하지 않고 정상적으로 끝낸다”라는 의도를 표현할 때 유용합니다.

https://developer.apple.com/documentation/combine/empty

 

Empty | Apple Developer Documentation

A publisher that never publishes any values, and optionally finishes immediately.

developer.apple.com

let emptyPublisher = Empty<String, Never>()
var bag = Set<AnyCancellable>()

emptyPublisher
    .sink(
        receiveCompletion: { print("완료:", $0) },
        receiveValue: { value in
            print("값:", value)
        }
    )
    .store(in: &bag)
    
// 완료: finished

 

특징

  • 어떤 값도 방출하지 않음 (receiveValue는 호출되지 않음)
  • 즉시 .finished completion 전송
  • Never 에러 타입이 기본
  • fallback / default 흐름을 만들 때 자주 사용

RxSwift 대응

  • Observable<String>.empty()

값 없이 완료만 발생하는 Observable

3. Future – 비동기 작업의 결과(성공/실패)를 단 한 번 방출

Future는 비동기 로직을 한 번 실행하고 성공(.success) 또는 실패(.failure) 값을 단 한 번 전달합니다.

https://developer.apple.com/documentation/combine/future

 

Future | Apple Developer Documentation

A publisher that eventually produces a single value and then finishes or fails.

developer.apple.com

 

예시로 간단한 네트워크 시뮬레이션을 해보면:

func loadData() -> Future<String, Error> {
    return Future { promise in
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            let success = Bool.random()

            if success {
                promise(.success("성공 결과"))
            } else {
                promise(.failure(NSError(domain: "", code: -1)))
            }
        }
    }
}

// 사용
var bag = Set<AnyCancellable>()

loadData()
    .sink(
        receiveCompletion: { print("완료: \($0)") },
        receiveValue: { print("값:", $0) }
    )
    .store(in: &bag)

// 성공하면
// 값: 성공 결과
// 완료: finished
// 실패하면
// 완료: failure(Error Domain= Code=-1 "(null)")

 

RxSwift 대응

  • Single<T>
  • 성공/실패를 한 번만 전달한다는 점에서 동일합니다.

4. Deferred – 구독 시점에 Publisher 생성

Publisher 체인을 만들 때 "미뤄둔 생성"이 필요할 때 사용합니다.

https://developer.apple.com/documentation/combine/deferred

 

Deferred | Apple Developer Documentation

A publisher that awaits subscription before running the supplied closure to create a publisher for the new subscriber.

developer.apple.com

 

let deferredPublisher = Deferred {
    Just(Int.random(in: 1...100))
}

// 이후 사용할때마다 Int 값이 생성되서 사용
 

사용할 때마다 새로운 난수를 내보냅니다.

RxSwift 대응

  • Observable.deferred { ... }

 

5. PassthroughSubject – 이벤트를 그대로 흘려보내는 Subject

Subject는 Publisher + Subscriber 역할을 동시에 수행하는 조금 특별한 타입입니다.

https://developer.apple.com/documentation/combine/passthroughsubject

 

PassthroughSubject | Apple Developer Documentation

A subject that broadcasts elements to downstream subscribers.

developer.apple.com

 

let subject = PassthroughSubject<String, Never>()
var bag = Set<AnyCancellable>()

subject
    .sink { value in
        print("값:", value)
    }
    .store(in: &bag)

subject.send("첫 번째")
subject.send("두 번째")

// 값: 첫 번째
// 값: 두 번째

 

특징

  • 초기값이 없음
  • 구독 이후 발생하는 값만 전달

RxSwift 대응

  • PublishSubject

 

6. CurrentValueSubject – 최신 값 1개를 항상 들고 있는 Subject

PassthroughSubject 와 달리 초기값을 필수로 가지고 시작합니다.

https://developer.apple.com/documentation/combine/currentvaluesubject

 

CurrentValueSubject | Apple Developer Documentation

A subject that wraps a single value and publishes a new element whenever the value changes.

developer.apple.com

let count = CurrentValueSubject<Int, Never>(0)

count
    .sink { value in
        print("값:", value)
    }
    .store(in: &bag)

count.send(1)
count.send(2)

print("현재 값:", count.value)

// 값: 0
// 값: 1
// 값: 2
// 현재 값: 2

 

특징

  • 최신 값 보관
  • 새로운 구독자에게 즉시 최신 값을 전달

RxSwift 대응

  • BehaviorSubject
  • 실무에서는 BehaviorRelay에 가장 많이 대응

 

7. Timer.TimerPublisher – 일정 시간마다 값을 반복적으로 방출

Combine에는 시간 기반 Publisher가 기본 제공됩니다.

let timer = Timer.publish(every: 1, on: .main, in: .default)
var bag = Set<AnyCancellable>()

timer
    .autoconnect()
    .sink { value in
        print("타이머:", value)
    }
    .store(in: &bag)

// 타이머: 2025-11-20 10:56:02 +0000
// 타이머: 2025-11-20 10:56:03 +0000
// 타이머: 2025-11-20 10:56:04 +0000
// 타이머: 2025-11-20 10:56:05 +0000
// ...

RxSwift 대응

  • Observable<Int>.interval
  • Observable<Int>.timer

Publisher 매핑표 업데이트

Combine Publisher 특징 RxSwift 대응
Just 단일 값 즉시 방출 Observable.just
Future 비동기 결과 1회 Single
Deferred 구독 시점 생성 Observable.deferred
Empty 값 없이 완료 Observable.empty / never
PassthroughSubject 이벤트 스트림 PublishSubject
CurrentValueSubject 최신 값 보관 BehaviorSubject / BehaviorRelay
TimerPublisher 주기적 시간 이벤트 Observable.interval / timer

 

마무리하며

이번 글에서는 Combine에서 제공하는 주요 Publisher 타입들을 정리해보고, RxSwift의 대응되는 컴포넌트와 비교해보았습니다.
다음 글에서는 이 Publisher들을 어떻게 조합하고 변환하는지, 즉 Operator 비교(map, flatMap, debounce, throttle, combineLatest 등) 를 다뤄보는 내용을 이어서 작성해보려고 합니다.

읽어주셔서 감사합니다!

반응형
Comments