Combine이든 RxSwift든 리액티브 프로그래밍 프레임워크를 다루다보면 한번씩 마주하게 되는 문제가 몇가지 있다. 하나는 메모리 관리 문제고(보통 이건 초심자가 많이 겪는다), 또 하나는 배압(Backpressure) 문제다. 리액티브 프로그래밍에서 배압이란 무엇을 말하는가? 구독자와 게시자가 있다고 할 때, 구독자가 값(혹은 이벤트)을 소비하는 속도가 게시자가 값을 보내는 속도를 따라가지 못하는 상황을 일컫는다. 이게 원래는 유체동역학 용어(위키백과: Back Pressure)라는데, 프로그래밍 관점에서 리액티브 프로그래밍이라는게 값의 흐름이니 정말 적절한 용어가 아닌가 싶다.

여튼 이런 배압 문제는 일반적으로는 쉽게 발생하지 않는다. 일반적으로 리액티브 프로그래밍을 비동기 처리를 위해 사용하다보니 게시자의 값 송출이 감당하지 못할만한 상황이 자주 일어나지 않기 때문이다. 그렇기 때문에 앞에서 초심자가 많이 겪는 메모리 관리 문제보다는 좀 더 리액티브 프로그래밍을 헤비하게 사용하는 경우 맞닥뜨리기 쉽다(앱에서 발생하는 온갖 이벤트를 다 리액티브하게 처리를 한다던가…). 예를 들어 기기의 자이로 센서 등을 통해 값을 계속해서 전달받는 경우가 있다고 하자. (센서의 민감도에 따라 다르겠지만) 1초 사이에 어마어마하게 많은 값을 받게 될 수도 있다. 이런 경우에는 차라리 특정 시간차를 두고 그 사이에 온 최초 혹은 최후 혹은 어떤 하나의 값을 받아도 문제가 없을 것이다.
ReactiveX의 경우에는 throttle이나 debounce, buffer 등의 연산자를 활용하거나 경우에 따라 적절한 Cold/Hot Observable을 사용하라는 식의 지침이 있다(ReactiveX: Back pressure operators).
그렇다면 Combine의 경우에는 어떤 식으로 처리할 수 있을까? 우선, Combine도 Throttle
이나 Debounce
, Buffer
와 같은 사전정의된 Publisher
를 제공한다. 사실 이것들만 잘 활용해도 어지간한 케이스에는 대응할 수 있다.
하지만 게시자(Publisher
)와 구독(Subscription
)을 직접 구현하는 방법으로도 배압을 통제할 수 있다. Combine은 구독자(Subscriber
)가 게시자로부터 값을 당겨오는 메커니즘을 사용한다. 이때 구독자는 게시자에게 Subscribers.Demand
라는 구조체를 전달해 얼만큼의 값을 당겨올지 요청할 수 있다. 그리고 이 Demand
를 조절하는 방식으로 값을 당겨오는 것을 통제해 배압 문제를 처리할 수 있는 것이다. 예를 들어 외부에서 Demand
를 통제할 수 있는 커스텀 구독자를 만들고, 상황에 따라 이 구독자의 참조를 지닌 상태에서 값의 처리가 힘든 상황이 되면 잠시 값을 당겨오지 않도록 했다가 다시 준비가 되면 당겨올 수 있도록 하는 것이다.
이 때 주의할 것이 하나 있는데, Demand
를 조절하는 방법에는 가산(add)만이 존재한다는 것이다. 예를 들어 이미 .max(5)
와 같은 Demand
가 동작을 하고 있다면, 3번째 값을 받고 .none
을 전달해 더이상 값을 받지 않겠다고 해도 앞선 .max(5)
에 따라 4번째, 5번째 값을 받고 나서야 .none
이 효력을 발휘한다. 그렇기 때문에 직접적으로 Demand
를 조절하고자 하는 경우에는 .max(1)
과 같이 한번에 하나씩의 값을 받는 식으로 처리하는게 좀 더 유용할 것이다(무조건 이렇게 하라는 건 아니다!).