Swift Combine sink: 비동기 이벤트 처리의 핵심
Swift Combine sink: 비동기 이벤트 처리의 핵심
Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리와 데이터 스트림 관리에 탁월한 성능을 발휘합니다. Combine의 핵심 요소 중 하나인 sink
는 Publisher로부터 전달된 데이터를 처리하는데 매우 중요한 역할을 합니다. 이 글에서는 sink
의 개념과 사용법, 그리고 다양한 활용 예제를 통해 sink
가 비동기 이벤트 처리에서 왜 중요한지를 자세히 살펴보겠습니다.
sink란 무엇인가?
sink
는 Combine에서 가장 많이 사용되는 연산자 중 하나로, Publisher로부터 발행된 값을 수신하고 처리하는 Subscriber를 생성합니다. sink
는 클로저를 통해 값을 처리하고, 필요한 경우 오류를 처리할 수 있습니다. 이를 통해 비동기 데이터 스트림을 간단하고 명확하게 처리할 수 있습니다.
sink의 기본 사용법
sink
를 사용하려면 먼저 Publisher를 생성하고, 그 Publisher에 sink
를 적용하여 Subscriber를 생성해야 합니다. 기본적인 사용법은 다음과 같습니다:
swiftimport Combine let publisher = Just("Hello, Combine!") let cancellable = publisher.sink(receiveCompletion: { completion in switch completion { case .finished: print("Finished") case .failure(let error): print("Error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") })
이 예제에서 Just
Publisher는 "Hello, Combine!" 문자열을 발행하며, sink
를 통해 이 값을 수신하고 출력합니다. 완료 이벤트가 발생하면 "Finished"를 출력합니다.
비동기 이벤트 처리
Combine의 주요 목적 중 하나는 비동기 이벤트를 처리하는 것입니다. sink
는 이러한 비동기 이벤트를 처리하는 데 매우 유용합니다. 비동기 작업의 예로 네트워크 요청을 생각해볼 수 있습니다.
네트워크 요청 처리
아래 예제는 URLSession을 사용하여 네트워크 요청을 보내고, sink
를 통해 응답 데이터를 처리하는 방법을 보여줍니다:
swiftimport Combine guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { fatalError("Invalid URL") } let cancellable = URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } .decode(type: Todo.self, decoder: JSONDecoder()) .sink(receiveCompletion: { completion in switch completion { case .finished: print("Request finished") case .failure(let error): print("Request failed with error: \(error)") } }, receiveValue: { todo in print("Received todo: \(todo)") }) struct Todo: Codable { let id: Int let title: String let completed: Bool }
이 예제에서 URLSession.shared.dataTaskPublisher
는 지정된 URL로 네트워크 요청을 보내고, 응답 데이터를 map
과 decode
연산자를 사용하여 가공합니다. sink
는 가공된 데이터를 수신하고 출력하며, 완료 이벤트와 오류를 처리합니다.
사용자 인터페이스 이벤트 처리
Combine과 sink
를 사용하여 사용자 인터페이스(UI) 이벤트도 처리할 수 있습니다. 예를 들어, 버튼 클릭 이벤트를 처리하는 경우를 살펴보겠습니다.
swiftimport Combine import UIKit // 버튼 생성 let button = UIButton() 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
가 값을 발행하고, sink
를 통해 이를 처리합니다.
데이터 변환 및 처리
sink
는 단순히 값을 수신하는 것 외에도, 데이터 변환 및 처리에 유용합니다. Combine의 다양한 연산자와 결합하여 데이터를 가공할 수 있습니다.
데이터 변환 예제
아래 예제는 숫자 배열을 문자열 배열로 변환하여 처리하는 방법을 보여줍니다:
swiftimport Combine let numbers = [1, 2, 3, 4, 5].publisher let cancellable = numbers .map { "\($0)" } .sink(receiveCompletion: { completion in switch completion { case .finished: print("Finished") case .failure(let error): print("Error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") })
이 예제에서 숫자 배열을 발행하는 Publisher에 map
연산자를 사용하여 숫자를 문자열로 변환하고, sink
를 통해 변환된 값을 수신하고 출력합니다.
오류 처리
Combine을 사용한 비동기 작업에서는 오류 처리가 매우 중요합니다. sink
를 사용하면 오류를 간단하게 처리할 수 있습니다.
오류 처리 예제
아래 예제는 네트워크 요청에서 발생할 수 있는 오류를 처리하는 방법을 보여줍니다:
swiftimport Combine let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! let cancellable = URLSession.shared.dataTaskPublisher(for: url) .tryMap { data, response -> Data in guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw URLError(.badServerResponse) } return data } .decode(type: Todo.self, decoder: JSONDecoder()) .sink(receiveCompletion: { completion in switch completion { case .finished: print("Request finished") case .failure(let error): print("Request failed with error: \(error)") } }, receiveValue: { todo in print("Received todo: \(todo)") }) struct Todo: Codable { let id: Int let title: String let completed: Bool }
이 예제에서 tryMap
연산자를 사용하여 HTTP 응답의 상태 코드를 검사하고, 상태 코드가 200이 아닌 경우 오류를 발생시킵니다. sink
는 오류를 처리하고 적절한 메시지를 출력합니다.
취소 가능성
Combine에서 생성된 모든 sink
는 AnyCancellable
객체를 반환합니다. 이를 통해 비동기 작업을 언제든지 취소할 수 있습니다.
취소 예제
아래 예제는 타이머를 사용하여 주기적으로 값을 발행하고, 일정 시간 후에 구독을 취소하는 방법을 보여줍니다:
swiftimport Combine let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .default).autoconnect() let cancellable = timerPublisher.sink { value in print("Timer fired at: \(value)") } // 5초 후에 구독 취소 DispatchQueue.main.asyncAfter(deadline: .now() + 5) { cancellable.cancel() print("Subscription cancelled") }
이 예제에서 타이머는 1초마다 현재 시간을 발행하며, 5초 후에 구독을 취소합니다.
결론
Combine의 sink
는 비동기 이벤트 처리의 핵심 도구로, Publisher로부터 전달된 데이터를 간단하고 효과적으로 처리할 수 있습니다. sink
를 사용하면 네트워크 요청, 사용자 인터페이스 이벤트, 데이터 변환 및 오류 처리 등을 손쉽게 구현할 수 있습니다. sink
의 강력한 기능을 활용하여 더욱 안정적이고 효율적인 비동기 로직을 구현해 보세요. Combine과 sink
를 통해 비동기 프로그래밍의 복잡성을 줄이고, 더욱 직관적인 코드 작성을 할 수 있을 것입니다.