본문 바로가기

Combine

[Combine] Publisher / Operator / Subscriber 알아보기

매번 Combine 관련 글들을 찾아 읽고 잊어버리는 것의 반복되다보니 
Combine 을 제가 이해한 방식대로 정리된 글이 있으면 좋을 것 같아 글을 작성하게 되었습니다.
잘못된 점이라던지 궁금한 점이 있다면 알려주세요!


 
Combine 프레임워크는 앱이 이벤트를 처리하는 방법에 대한 선언적 접근 방식을 제공한다고 합니다.
여러 delegate callback 이나 완료 핸들러 클로저로 구현하는 것이 아닌
특정 이벤트 소스에 대한 단일 처리 체인을 제공한다고 합니다.
 
다음으로는 Combine 의 핵심이라 볼 수 있는
"Publisher, Operator, Subscriber"
에 대해 알아보도록 하겠습니다.
 

 
1. Publisher 는 데이터 스트림을 생성하고 이를 구독할 수 있는 인터페이스를 제공해줍니다.
subscribe(_:) 메서드를 사용하여 Subscriber 에게 데이터를 전달합니다.
 
2. Operator 는 Publisher 가 생성한 데이터 스트림을 가공하는 역할을 합니다.
다양한 연산자들이 있는데 이는 다음에 한번 살펴보도록 하겠습니다.
 
3. Subscriber 는 Publisher 가 생성한 데이터 스트림을 구독하고 데이터를 소비하는 역할을 합니다.
데이터 스트림에서 새로운 값이 전달되면 이를 처리하고 필요한 작업을 수행하게 됩니다.
주로 사용하는 메서드로는 receive(subscription:), receive(_:demand), receive(completion:) 등이 있으며
이러한 메서드를 통해 Subscriber 의 동작을 정의하게 됩니다.
 
 
이렇게 정의로만 작성을 해둔다면... 이해하는 것이 어려울 것 같아 간단하게 
이를 비유를 하자면...
 
Publisher 는 상품을 생산하고 배송해주는 쿠팡이라 볼 수 있습니다.
(쿠팡 = 상품을 생산하고 발송하는 주체)
 
Operator 는 상품을 처리하고 가공하는 중간 단계로 생각할 수 있습니다.
(쿠팡 물류 센터 = 상품 분류, 포장, 라벨 부착 등의 작업을 진행)
 
Subscriber 는 쿠팡 회원이라 생각할 수 있고 상품이 언제 도착하고 어떤 상태에 있는지에 관심 있습니다.
(쿠팡회원 = 상품 정보 확인)
 
 
이렇게 보면 어떤 느낌이신지 아실까요...? 저도 이렇게 표현하는 것이 맞을지 몰라 조금 조심스럽긴 합니다.
 
다음으로는 지금 이 과정을 코드로 표현하면 어떻게 할 수 있을까? 에 대해 알아 보겠습니다.

 


Publisher / Operator / Subscriber 사용하기

let publihser = Just(13)

publihser.sink { result in // sink 메서드가 subscriber 역할을 한다!
    print(result) // 13
}

 
이 코드에서 알아볼 수 있는건 저희가 위에서 배운 Publisher 라는 것.
데이터 스트림, 주체가 되는 것이라 볼 수 있습니다.
코드에 Just 라는건 처음 보게되는데, 이게 뭐지 싶어 찾아보니
 

 
Publisher 프로토콜을 따르고 있는 struct 인걸 확인할 수 있습니다.
그럼 이제 Just 라는 것 또한 publisher 라고 생각할 수 있고
다음으로 넘어가서 저 sink 라는것은 뭘까? 하고 보니
 
 

 
 
이 메서드는 subscriber 를 생성하고, subscriber 를 반환하기 전에 즉각적으로 값을 request 한다고 합니다.
정리해보면, Just 라는 publiser 를 사용하면 subscriber 와 연결할때
sink 라는 메서드를 사용하게 되고 무언가를 즉각적으로 request 하는구나 라고 알 수 있습니다.
(데이터 스트림이 구축되었구나 라고 볼 수 있습니다.)
 
또 다른 예를 살펴보도록 하겠습니다.
 

let coupangPublisher = ["요거트","사과","아이폰","에어팟"].publisher

coupangPublisher.sink(receiveCompletion: { _ in
    print("주문목록 처리 완료!")
}, receiveValue: { product in
    print(product)
})

 
데이터 스트림의 역할을 하는 coupangPublihser 가 존재하고 (Publiser)
sink 메서드를 사용하여 현재 어떤 제품이 있고, 결과가 어떠한지 알 수 있는 코드가 작성되어 있습니다. (Subscriber)
그럼 Operator 는 어떻게 사용될 수 있을까요?
 

let coupangPublisher = ["요거트","사과","아이폰","에어팟"].publisher

coupangPublisher
    .map { product in
        return "현재 주문 제품은: " + product
    }
    .sink(receiveCompletion: { _ in
    print("주문목록 처리 완료!")
}, receiveValue: { product in
    print(product)
})

 
이렇게 sink 메서드를 사용하기 전에
map 이라는 연산자(Operator)를 사용하여 원하는대로 가공할 수 있습니다.


이렇게 Publisher, Operator, Subscriber 를 살펴봤고
다음엔 Publisher 와 Subscriber 를 연결해주는 sink 를 제외한 다른 방법에 대해 글을 작성하도록 하겠습니다.
읽어주셔서 감사합니다. 🙇🏻