Swift Combine Subject로 데이터 스트림 제어하기
Swift Combine Subject로 데이터 스트림 제어하기
Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리와 데이터 스트림 관리를 위한 강력한 도구를 제공합니다. 이 중에서 Subject는 Publisher와 Subscriber의 역할을 동시에 수행할 수 있는 특별한 객체로, 데이터 스트림을 제어하는 데 매우 유용합니다. 이 글에서는 Swift Combine Subject를 활용하여 데이터 스트림을 제어하는 방법을 다양한 예제를 통해 자세히 살펴보겠습니다.
Subject란 무엇인가?
Subject는 Combine에서 데이터를 발행(Publisher)하고, 다른 Publisher로부터 데이터를 구독(Subscriber)할 수 있는 객체입니다. 이를 통해 데이터 스트림을 생성, 제어 및 가공할 수 있습니다. Combine에는 두 가지 주요 타입의 Subject가 있습니다:
- PassthroughSubject: 초기값 없이 새로운 값을 발행하는 Subject입니다.
- CurrentValueSubject: 초기값을 가지며, 최신 값을 유지하는 Subject입니다.
PassthroughSubject
기본 개념
PassthroughSubject는 초기값이 없으며, 새로운 값을 발행할 때마다 이를 구독자에게 전달합니다. 주로 이벤트를 전달하는 데 사용되며, 이전에 발행된 값은 보존되지 않습니다.
사용 예제
PassthroughSubject를 사용하여 간단한 이벤트 스트림을 생성해보겠습니다.
swiftimport Combine // PassthroughSubject 생성 let subject = PassthroughSubject<String, Never>() // Subscriber 생성 및 구독 let cancellable = subject.sink(receiveCompletion: { completion in switch completion { case .finished: print("Finished") case .failure(let error): print("Error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") }) // 값 발행 subject.send("Hello") subject.send("Combine") subject.send(completion: .finished)
이 예제에서 PassthroughSubject
는 "Hello"와 "Combine" 문자열을 발행하고, 이를 구독자에게 전달합니다. 마지막으로 완료 이벤트를 발행하여 스트림이 종료되었음을 알립니다.
사용자 이벤트 처리
PassthroughSubject는 사용자 이벤트를 처리하는 데 유용합니다. 예를 들어, 버튼 클릭 이벤트를 처리하는 경우를 생각해볼 수 있습니다.
swiftimport Combine import UIKit // 버튼 생성 let button = UIButton() // PassthroughSubject 생성 let buttonTapSubject = PassthroughSubject<Void, Never>() // 버튼 클릭 이벤트를 PassthroughSubject로 전달 button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) @objc func buttonTapped() { buttonTapSubject.send() } // Subscriber 생성 및 구독 let cancellable = buttonTapSubject.sink { print("Button was tapped!") }
이 예제에서 버튼 클릭 이벤트가 발생할 때마다 buttonTapSubject
가 값을 발행하고, 이를 구독자에게 전달합니다. 이를 통해 버튼 클릭 이벤트를 간단하게 처리할 수 있습니다.
CurrentValueSubject
기본 개념
CurrentValueSubject는 초기값을 가지며, 최신 값을 유지합니다. 새로운 값을 발행할 때마다 최신 값이 업데이트되며, 구독자가 구독할 때 현재 저장된 값을 즉시 받을 수 있습니다. 주로 상태 관리에 사용됩니다.
사용 예제
CurrentValueSubject를 사용하여 상태를 관리하는 예제를 살펴보겠습니다.
swiftimport Combine // CurrentValueSubject 생성 (초기값 설정) let subject = CurrentValueSubject<String, Never>("Initial value") // Subscriber 생성 및 구독 let cancellable = subject.sink(receiveCompletion: { completion in switch completion { case .finished: print("Finished") case .failure(let error): print("Error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") }) // 값 발행 subject.send("Updated value") subject.send("Another update") subject.send(completion: .finished)
이 예제에서 CurrentValueSubject
는 "Initial value"로 초기화되며, 새로운 값이 발행될 때마다 최신 값이 업데이트됩니다. 구독자는 항상 최신 값을 받을 수 있습니다.
상태 관리
CurrentValueSubject는 주로 상태 관리를 위해 사용됩니다. 예를 들어, ViewModel에서 UI 상태를 관리하는 경우를 생각해볼 수 있습니다.
swiftimport Combine class ViewModel { // CurrentValueSubject를 사용하여 상태 관리 var state = CurrentValueSubject<String, Never>("Initial state") func updateState(newState: String) { state.send(newState) } } // ViewModel 인스턴스 생성 let viewModel = ViewModel() // Subscriber 생성 및 구독 let cancellable = viewModel.state.sink { value in print("State updated to: \(value)") } // 상태 업데이트 viewModel.updateState(newState: "Loading") viewModel.updateState(newState: "Loaded")
이 예제에서 ViewModel
은 CurrentValueSubject
를 사용하여 UI 상태를 관리하며, 상태가 업데이트될 때마다 구독자에게 최신 상태를 전달합니다.
PassthroughSubject와 CurrentValueSubject 비교
PassthroughSubject와 CurrentValueSubject는 각각 고유한 용도와 특성을 가지고 있습니다. PassthroughSubject는 주로 일회성 이벤트를 처리하는 데 사용되며, CurrentValueSubject는 최신 상태를 관리하는 데 적합합니다. 두 Subject를 적절히 활용하면 다양한 비동기 데이터 스트림을 효과적으로 처리할 수 있습니다.
사용 사례 요약
-
PassthroughSubject:
- 초기값이 필요 없는 경우
- 일회성 이벤트(예: 사용자 입력, 네트워크 응답) 처리
- 값을 발행하기 전까지는 아무 데이터도 보유하지 않음
-
CurrentValueSubject:
- 초기값이 필요한 경우
- 상태 관리(예: UI 상태) 및 최신 값 유지
- 구독 시점에 최신 값을 즉시 전달
Subject로 데이터 스트림 제어하기
Subject는 데이터 스트림을 제어하는 데 매우 유용합니다. 여러 Publisher를 결합하거나 데이터를 가공하여 구독자에게 전달할 수 있습니다. 몇 가지 응용 예제를 통해 이를 살펴보겠습니다.
여러 Publisher 결합
Subject를 사용하여 여러 Publisher의 데이터를 결합할 수 있습니다.
swiftimport Combine let subject1 = PassthroughSubject<Int, Never>() let subject2 = PassthroughSubject<String, Never>() let cancellable = subject1.combineLatest(subject2) .sink { int, string in print("Received \(int) and \(string)") } subject1.send(1) subject2.send("A") subject1.send(2) subject2.send("B")
이 예제에서 subject1
과 subject2
는 각각 정수와 문자열을 발행하며, combineLatest
연산자를 사용하여 두 Subject의 최신 값을 결합하여 구독자에게 전달합니다.
데이터 가공
Subject를 사용하여 데이터를 가공하고 구독자에게 전달할 수 있습니다.
swiftimport Combine let subject = PassthroughSubject<Int, Never>() let cancellable = subject .map { $0 * 2 } .sink { value in print("Received value: \(value)") } subject.send(1) subject.send(2) subject.send(3)
이 예제에서 subject
는 정수를 발행하며, map
연산자를 사용하여 발행된 값을 두 배로 변환하여 구독자에게 전달합니다.
결론
Swift Combine의 Subject는 비동기 데이터 스트림을 제어하고 관리하는 데 매우 유용한 도구입니다. PassthroughSubject와 CurrentValueSubject를 적절히 활용하면 다양한 비동기 이벤트와 상태 변화를 효율적으로 처리할 수 있습니다. Combine을 사용하여 복잡한 비동기 로직을 간단하고 명확하게 구현할 수 있으며, 이를 통해 더 나은 사용자 경험을 제공할 수 있습니다. Subject의 기본 개념과 사용법을 이해하고 다양한 예제를 통해 익숙해지면, 리액티브 프로그래밍의 강력한 기능을 최대한 활용할 수 있을 것입니다.