<h1><center> Combine Chapter 01 - Hello Combine </center></h1>
###### tags: `๐ป TIL`
###### date: `2024-04-1617:21:33.284Z`
> [color=#724cd1][name=๋ฐ๋ฆญ]
> [Hello, Combine!](https://www.kodeco.com/books/combine-asynchronous-programming-with-swift/v4.0/chapters/1-hello-combine)
> [Publisher](https://developer.apple.com/documentation/combine/publisher)
> [Subscriber](https://developer.apple.com/documentation/combine/subscriber)
# ๊ฐ์
> Kodeco์ Combine: Asynchronous Programming With Swift๋ฅผ ๋ณด๋ฉด์ ํ์ตํ์.
## Hello, Combine
Combine ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ์ธํ ์ ๊ทผ ๋ฐฉ๋ฒ์ผ๋ก ์ฑ์์ ํ์ํ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ค. ์ฆ, ์ฌ๋ฌ ๊ฐ์ Delegate ์ฝ๋ฐฑ์ด๋ Completion Handler๋ฅผ ์ฌ์ฉํ๋ ๋์ ์ด๋ฒคํธ๋ฅผ ํ๋์ ํ๋ก์ธ์ค ์ฒด์ธ์ผ๋ก ๋ง๋ค ์ ์๋ค.
์ฒด์ธ์ ๊ฐ Part๋ ์ด์ ๋จ๊ณ์์ ๋ฐ์ Element๋ค์ ๋ํด ๋ณ๋์ ์์
์ ์ํํ๋ Combine Operator์ด๋ค.
## Asynchronous Programming
Single Thread ์ธ์ด์์ ํ๋ก๊ทธ๋จ์ ํ ์ค์ฉ ์์ฐจ์ ์ผ๋ก ์คํ๋๋ค.(ex: javascript)
```swift
// begin
var name = "Derrick"
print(name)
name += " Kim"
print(name)
// End
```
> Single Thread๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ์ ํ์ฌ ์ํ๋ฅผ ํญ์ ํ์ธํ ์ ์๋ค.
**๋ค์ค ์ค๋ ๋**
```swift
--- Thread 1 ---
begin
var name = "Tom"
print(name)
--- Thread 2 ---
name = "Billy Bob"
--- Thread 1 ---
name += " Harding"
print(name)
end
```
> ์ฌ๋ฌ ์ค๋ ๋๋ก name์ด๋ ๊ณต์ ์์์ ์ ๊ทผํ ๊ฒฝ์ฐ ํญ์ ๊ฐ์ ๊ฐ์ ๋ณด์ฅํ ์ ์๋ค.
## Foundation and UIKit / AppKit
NSThread์ ๊ฐ์ ์ ์์ค API๋ถํฐ Swift์ ํ๋์ ๋์์ฑ์ ํ์ฉํ async/await ๊ตฌ์กฐ๊น์ง ๋ค์ํ API๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
**Notification Center**: ์ฌ์ฉ์๊ฐ Device์ ๋ฐฉํฅ์ ๋ณ๊ฒฝํ๊ฑฐ๋, ํ๋ฉด์ ํค๋ณด๋๊ฐ ํ์๋๊ฑฐ๋ ์จ๊ฒจ์ง ๋์ ๊ฐ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค Observing๋ ์ฝ๋๋ฅผ ์คํํ๋ค.
**Delegate Pattern**: ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋์ ํ๊ฑฐ๋ ๋ค๋ฅธ ๊ฐ์ฒด์ ์ํธ์์ฉ์ ํตํด ํ๋ํ ๊ฐ์ฒด๋ฅผ ์ ์ํ๋ค. ์๋ฅผ ๋ค์ด, AppDelegate์์ ์๋ก์ด Remote notification์ด ๋์ฐฉํ์ ๋ ๋ฌด์์ด ์คํ๋์ผ๋์ง ํ๋์ง ์ ์ํ์ง๋ง, ์ด ์ฝ๋๊ฐ ์ธ์ ์คํ๋ ์ง, ๋ช ๋ฒ ์คํ๋ ์ง๋ ์ ์ ์๋ค.
**GCD(Grand Central Dispatch), Operations**: Task์ ์คํ์ ์ถ์ํํ๋ ๋ฐ ๋์์ ์ค๋ค. ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํด ์ฝ๋๋ฅผ ์์ฐจ์ ์ผ๋ก ์คํํ๋๋ก ์์ฝํ๊ฑฐ๋, ๋ค์ํ ์ฐ์ ์์๋ฅผ ๊ฐ์ง ๋ค์ํ Queue์์ ๋์์ ์ฌ๋ฌ ์์
์ ์คํํ ์ ์ค๋ค.
**Closure**: ์ฝ๋์์ ๋ค๋ฅธ ๊ฐ์ฒด์ ์คํ ์ฌ๋ถ, ํ์ ๋ฐ ์คํํ Context๋ฅผ ๊ฒฐ์ ํ ์ ์๋๋ก ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ ๋ฌํ ์ ์๋ค.
> ๋๋ถ๋ถ์ ์ ํ์ ์ธ ์ฝ๋๋ ๋น๋๊ธฐ์ ์ผ๋ก ์์
์ ์ํํ์ง๋ง ์คํ๋ ์์์ ๋ํด ๊ฐ์ ํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ๋ค. ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ์ข์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋จ์ ์์ฑํ๋ ๊ฒ์ ๊ฐ๋ฅํ๋ค. ๊ทธ์ ๋ณต์กํ ๋ฟ์ด๋ค.

Apple์ Timer, NotificationCenter, Core Data์ ๊ฐ์ ํ๋ ์์ํฌ์ Combine์ ํตํฉํ๊ณ ์๋ค.
### Publisher
> Publisher๋ Subscriber๊ฐ ํ๋์ด์ ์์ ๋, ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ๊ฐ์ Subscriber์๊ฒ ๋ด๋ณด๋ผ ์ ์๊ฒ ํ๋ ํ์
์ด๋ค. ์ํ ๊ณ์ฐ, ๋คํธ์ํน, ์ฌ์ฉ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฑ ๊ฑฐ์ ๋ชจ๋ ๊ฒ์ด Publisher๊ฐ ๋ ์ ์๋ค.
Publisher์ Input, Failure ๊ด๋ จ ํ์
์ Publisher๊ฐ ์ ์ธํ Output, Failure ํ์
๊ณผ ์ผ์นํด์ผ ํ๋ค.
Pulblisher๋ Subscriber๋ฅผ ์๋ฝํ๊ธฐ ์ํด receive(subscriber:)๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
๊ทธ๋ฐ ๋ค์ Publisher๋ Subscriber์์ ์๋ ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์๋ค.
- recive(subscription:): ๊ตฌ๋
์์ฒญ์ ํ์ธํ๊ณ Subsciription ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค. Subscriber๋ subscription์ ์ฌ์ฉํ์ฌ Publisher์๊ฒ Element๋ฅผ ์๊ตฌํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ์ฌ Pulisheing์ ์ทจ์ํ ์ ์๋ค.
- receive(_:): Publisher์์ Subscriber์๊ฒ Element ํ๋๋ฅผ ์ ๋ฌํ๋ค.
- receive(completion:): Publishing์ด ์ ์, ์ค๋ฅ์ ํจ๊ฒ ์ข
๋ฃ๋์์์ Subscriber์๊ฒ ์๋ฆฐ๋ค.
๋ชจ๋ Publisher๋ Downstream subscriber๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ค๋ฉด ์ด ๊ณ์ฝ์ ์ค์ํด์ผํ๋ค. Publisher์ ํ์ฅ ๊ธฐ๋ฅ์ ์ ๊ตํ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์ฒด์ธ์ ๋ง๋ค๊ธฐ ์ํด ๊ตฌ์ฑํ๋ ๋ค์ํ ์ฐ์ฐ์๋ฅผ ์ ์ํฉ๋๋ค.
๊ฐ Operator๋ Publisher๋ ํ๋กํ ์ฝ์ ๊ตฌํํ๋ ํ์
์ ๋ฐํํฉ๋๋ค. ์ด๋ฌํ ํ์
์ ๋๋ถ๋ถ์ Publisher ์ด๊ฑฐํ์ ํ์ฅ์ผ๋ก ์กด์ฌํจ. ์๋ฅผ ๋ค์ด, map(_:) Operator๋ Publishers.Map์ ์ธ์คํด์ค๋ฅผ ๋ฐํ.
Combine์ AsyncSequence์ ์ ์ฌํ์ง๋ง ๊ตฌ๋ณ๋๋ ์ญํ ์ ์ํํ๋ค.
Publisher์ AsyncSequence๋ ๋ชจ๋ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ Element๋ฅผ ๋ง๋ค์ง๋ง ์ฐจ์ด๊ฐ ์๋ค.
**Combine**
- pull ๋ชจ๋ธ์ Subsciber๊ฐ Publisher๋ก๋ถํฐ Element๋ฅผ ์์ฒญ
```swift
import Combine
let subject = PassthroughSubject<Int, Never>()
// ๊ตฌ๋
์ ์์ฑ ๋ฐ ๋ฐ์ดํฐ ์์ฒญ
let subscriber = Subscribers.Sink<Int, Never>(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Received completion")
case .failure(let error):
print("Received error: \(error)")
}
},
receiveValue: { value in
print("Received value: \(value)")
}
)
subject.subscribe(subscriber)
// ๊ฐ ๋ณด๋ด๊ธฐ
subject.send(1)
subject.send(2)
subject.send(3)
// ๋ฐํ์ ์๋ฃ
subject.send(completion: .finished)
```
> ์ด ์ฝ๋์์ subject๋ ์์๋ฅผ ๋ฐฉ์ถํ ์ ์๊ณ , Sink ๊ตฌ๋
์๋ ์ด๋ฌํ ์์๋ฅผ ๋ฐ์ ์ฒ๋ฆฌ. subscribe ํธ์ถ๋ก ๊ตฌ๋
์ ์์ํ๊ณ , send ๋ฉ์๋๋ก ์์๋ฅผ ๋ฐฉ์ถํจ.
**Async**
- for-await-in ๊ตฌ๋ฌธ์ ์ฌ์ฉํด AsyncSequence์์ ๋ง๋ Element๋ฅผ ๋ฐ๋ณต
```swift
let stream = AsyncStream<Int> { continuation in
// ์์๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐฉ์ถ
continuation.yield(1)
continuation.yield(2)
continuation.yield(3)
// ์คํธ๋ฆผ ์ข
๋ฃ
continuation.finish()
}
Task {
// ๋น๋๊ธฐ์ ์ผ๋ก ์์ ๋ฐ๋ณต
for await number in stream {
print("Received number: \(number)")
}
print("Stream completed")
}
```
**important**
> ๋ API ๋ชจ๋ Element๋ฅผ Mapping, Filteringํด์ Sequence๋ฅผ ์์ ํ๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ์ง๋ง Combine๋ง์ด **`debounce(for:scheduler:options:)`**,**`throttle(for:scheduler:latest:) `** ์ ๊ฐ์ ์๊ฐ ๊ธฐ๋ฐ ์์
๊ณผ **`merge(with:)`** , **`combineLatest(::`** ๊ฐ์ ๊ฒฐํฉ ์์
์ ์ ๊ณตํ๋ค.
**Combine๊ณผ Swift์ ๋์์ฑ ๋ชจ๋ธ ์ฌ์ด์ ์ฐ๊ฒฐ ๋ฐฉ๋ฒ**
- ์ด ๋ ๊ธฐ์ ์ ํตํฉํ๊ธฐ ์ํด, ๋ฐํ์๊ฐ ๋ด๋ณด๋ด๋ ์์๋ค์ AsyncSequence๋ก ๋ง๋ค ์ ์๋ค. ์ด๋ ๊ฒ ํ๋ฉด Subscriber๋ฅผ ์ฐ๊ฒฐํ๋ ๋์ , Swift์ for-await-in ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ์์๋ค์ ๋ฐ๋ณต์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
```swift
import Combine
import Foundation
// Combine Publisher๋ฅผ AsyncSequence๋ก ๋ณํํ๋ ํ์ฅ
extension Publisher {
func asAsyncSequence() -> AsyncThrowingStream<Output, Error> {
AsyncThrowingStream { continuation in
let cancellable = self.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
continuation.finish()
case .failure(let error):
continuation.finish(throwing: error)
}
},
receiveValue: { value in
continuation.yield(value)
}
)
continuation.onTermination = { @Sendable _ in
cancellable.cancel()
}
}
}
}
```
> Publisher๋ฅผ AsyncSequence๋ก ๋ณํํ๊ณ , for-await-in ๊ตฌ๋ฌธ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ณต ์ฒ๋ฆฌ
```swift
import Combine
// Combine์ ์ฌ์ฉํ ๊ฐ๋จํ Publisher ์์ฑ
let numbersPublisher = PassthroughSubject<Int, Never>()
numbersPublisher.send(1)
numbersPublisher.send(2)
numbersPublisher.send(3)
numbersPublisher.send(completion: .finished)
// ๋น๋๊ธฐ ์์
Task {
for try await number in numbersPublisher.asAsyncSequence() {
print("Received number: \(number)")
}
print("All numbers received and sequence has finished.")
}
// ๊ฐ์ ๋ณด๋ด๊ธฐ ์ํ ์๋ฎฌ๋ ์ด์
Task {
numbersPublisher.send(4)
numbersPublisher.send(5)
numbersPublisher.send(6)
numbersPublisher.send(completion: .finished)
}
```
> PassthroughSubject๋ก Publisher๋ฅผ ์์ฑํ๊ณ , AsyncSequence๋ก ๋ณํํ ๋ค, for-await-in ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ๊ฐ์ ์ฒ๋ฆฌ
#### Creating Your Own Publishers
Publisher ํ๋กํ ์ฝ์ ์ง์ ๊ตฌํํ๋ ๋์ , Combine ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ์ฌ๋ฌ ํ์
์ค ํ๋๋ฅผ ์ฌ์ฉํด Own Publisher๋ฅผ ์์ฑํ ์ ์๋ค.
- PassthroughSubject์ ๊ฐ์ Subject์ ๊ตฌ์ฒด์ ์ธ ํ์ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ send(_:) ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๊ฐ์ ๋ฐํ
- CurrentValueSubject๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์ฃผ์ ์ ๊ธฐ๋ณธ ๊ฐ์ด ์
๋ฐ์ดํธ๋ ๋๋ง๋ค ๊ฐ์ ๋ฐํ
- ํ๋กํผํฐ์ @Published ์ฃผ์์ ์ถ๊ฐํด์ ์ฌ์ฉํ๋ฉด, ํ๋กํผํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ Publisher๊ฐ ํ๋กํผํฐ์ ์ถ๊ฐ๋๋ค.
**NOTE**
> Upstream: ๋ฐ์ดํฐ ์์ค ๋๋ ์ด์ Operator๋ฅผ ์ง์นญํ๋ค. ์ด๋ค Publisher์ ๋ํด ์๊ฐํด๋ณผ ๋, Upstream์ Publisher๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์ด์ ๋จ๊ณ๋ Element์ด๋ค. ์ฆ, ํน์ Operator ๋๋ Publisher์ ๊ด์ ์์ ๋ฐ์ดํฐ๊ฐ ์ค๋ ๋ฐฉํฅ์ ๋งํ๋ค.
- ์ฌ๋ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์น๋ ๊ฒฝ์ฐ, ๊ฐ ๋จ๊ณ์ ์
๋ ฅ ๋ถ๋ถ์ Upstream์ด๋ค.
> Downstream: ๋ฐ์ดํฐ ์์ ์ ๋๋ ์ดํ Operator๋ฅผ ์๋ฏธํ๋ค. Downstream์ Publisher์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ถ๊ฐ ์ฒ๋ฆฌํ๋ ๋ค์ ๋จ๊ณ๋ Element์ด๋ค.
- Operator Chain์์ ๊ฐ Operator๋ ์์ ์ ์
๋ ฅ์ ๋ฐ๋ upstream๊ณผ ์ถ๋ ฅ์ ๋ณด๋ด๋ downstream ์ฌ์ด์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
```swift
import Combine
let subject = PassthroughSubject<Int, Never>()
let subscription =
subject
.map { $0 * 2 } // ์ฒซ ๋ฒ์งธ ์ฐ์ฐ์ (upstream: subject, downstream: filter)
.filter { $0 > 5 } // ๋ ๋ฒ์งธ ์ฐ์ฐ์ (upstream: map, downstream: sink)
.sink(receiveValue: { print("Received \($0)") })
// ๊ฐ ๋ฐฉ์ถ
subject.send(1)
subject.send(3)
subject.send(4)
```
### Subscriber
> Publisher๋ก๋ถํฐ ์
๋ ฅ์ ๋ฐ์ ์ ์๋ ํ์
์ ์ ์ธํ๋ ํ๋กํ ์ฝ์ด๋ค.
- Publisher๋ก๋ถํฐ Element์ ์คํธ๋ฆผ์ ๋ฐ๋ Subscriber๋ ๊ด๊ณ์ ๋ณํ๋ฅผ ์ค๋ช
ํ๋ ์๋ช
์ฃผ๊ธฐ ์ด๋ฒคํธ์ ํจ๊ป ๋ฐ๋๋ค. Subscriber์ Input, Failure๋ Publisher์ Output, Failure ํ์
๊ณผ ์ผ์นํด์ผํ๋ค.
Subscriber๋ฅผ Publisher์ ์ฐ๊ฒฐํ๋ ค๋ฉด Publisher์ subscribe(:)๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
ํธ์ถ ํ, Publisher๋ Subscirber์ receive(subscription:)๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด ๊ณผ์ ์ Subscriber์๊ฒ subscription ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๋๋ฐ ์ด ์ธ์คํด์ค๋ฅผ ์ด์ฉํด Publisher์๊ฒ Element๋ฅผ ์์ฒญํ๊ฑฐ๋ ํ์ํ ๊ฒฝ์ฐ ๊ตฌ๋
์ ์ทจ์ํ ์ ์๋ค.
์ด๊ธฐ ์์ฒญ์ ๋ฐ์ ํ, Publisher๋ receive(_:)๋ฉ์๋๋ฅผ ํธ์ถํด ๊ฐ๋ฅํ ๋น๋๊ธฐ์ ์ผ๋ก ์๋ก์ด Elements๋ฅผ ์ ๋ฌํ๋ค. ๋ฐํ์ด ์ข
๋ฃ๋ ๋๋, ์๋ฃ ๋ฐ ์ค๋ฅ๋ก ์ธํ ์ข
๋ฃ๋ฅผ ๋ํ๋ด๋ Subscribers.Completion ํ์
์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํด receive(completion:)๋ฅผ ํธ์ถ.
**Subscriber Operator**
- sink(receiveCompletion:receiveValue:): ์๋ฃ ์ ํธ๋ฅผ ๋ฐ์ ๋๋ง๋ค, ์ ์์๋ฅผ ๋ฐ์ ๋๋ง๋ค ์์์ ํด๋ก์ ๋ฅผ ์คํ
```swift
let subscription = publisher
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("Completed successfully.")
case .failure(let error):
print("Completed with an error: \(error)")
}
},
receiveValue: { value in
print("Received value: \(value)")
}
)
```
- assign(to: on:): ์ฃผ์ด์ง ์ธ์คํด์ค์ ํค ๊ฒฝ๋ก๋ก ์๋ณ๋ ์์ฑ์ ๊ฐ ์๋ก ๋ฐ์ ๊ฐ์ ๊ธฐ๋ก, Subsriber๋ ์ฌ์ฉ์ ์ ์ ์ฝ๋ ์์ด ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํด ๋ฐ์ดํฐ ๋ชจ๋ธ์ ํ๋กํผํฐ๋ UI ์ปจํธ๋กค์ ๋ฐ์ธ๋ฉํ์ฌ ํค ๊ฒฝ๋ก๋ฅผ ํตํด ํ๋ฉด์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ์ํ ์ ์๊ฒ ํด์ค๋ค.
```swift
let subscription = publisher
.assign(to: \.someProperty, on: someObject)
```
### Subscription
๊ตฌ๋
์ด ๋๋ ๋, Subscriber๋ฅผ ์ถ๊ฐํ๋ฉด Chain ์์ ๋ถ๋ถ์์ Publisher๊ฐ ํ์ฑํ ๋๋ค. Publisher๋ ์ ์ฌ์ ์ผ๋ก ์ถ๋ ฅ์ ๋ฐ์ Subscriber๊ฐ ์์ผ๋ฉด ์ด๋ค ๊ฐ๋ ๋ด๋ณด๋ด์ง ์๋๋ค.
๊ตฌ๋
์ ์ฌ์ฉ์ ์ง์ ์ฝ๋์ ์ค๋ฅ ์ฒ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋น๋๊ธฐ ์ด๋ฒคํธ ์ฒด์ธ์ ํ ๋ฒ๋ง ์ ์ธํ ์ ์๋ค.
full-Combine์ผ๋ก ์ ํํ๋ ๊ฒฝ์ฐ ๊ตฌ๋
์ ํตํด ์ฑ์ logic์ ์ค๋ช
ํ ์ ์์ผ๋ฉฐ ์๋ฃ๋๋ฉด ๋ฐ์ดํฐ๋ฅผ push, pullํ๊ฑฐ๋ ๊ฐ์ฒด๋ฅผ ์ฝ๋ฐฑํ ํ์์์ด Combine ์์คํ
์ด ๋ชจ๋ ๊ฒ์ ์คํํ๋๋ก ํ ์ ์๋ค.
**Cancellable** - Protocol
Cancellable๋ผ๋ ํ๋กํ ์ฝ๋ก ๊ตฌ๋
์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ ํ์๊ฐ ์๋ค.
์์คํ
์์ ์ ๊ณตํ๋ sink, asign ๋ชจ๋ Cancellable์ ๋ฐ๋ฅธ๋ค.
> cancel()์ ํธ์ถํ๋ฉด ํ ๋น๋ ๋ฆฌ์์ค๊ฐ ํด์ ๋๋ค. ๋ํ ํ์ด๋จธ, ๋คํธ์ํฌ ์ก์ธ์ค ๋๋ ๋์คํฌ I/O์ ๊ฐ์ side-effects๋ ์ค์งํ๋ค.
**AnyCancellable** - Class
์ทจ์๋ ๋ ์ ๊ณต๋ ํด๋ก์ ๋ฅผ ์คํํ๋ ํ์
์ ์ง์ฐ๋ ์ทจ์ ๊ฐ๋ฅํ ๊ฐ์ฒด์
๋๋ค
> AnyCancellable ์ธ์คํด์ค๋ ์ด๊ธฐํ๊ฐ ํด์ ๋๋ฉด ์๋์ผ๋ก cancel()์ ํธ์ถ