티스토리 뷰
Combine, 왜 필요한가?
iOS 개발을 하다 보면 비동기 작업을 처리해야 하는 경우가 많다.
예를 들어, 네트워크 요청, UI 이벤트 처리, 데이터 스트림 관리 등이 대표적이다.
기존에는 이를 해결하기 위해 Completion Handler, NotificationCenter, KVO, Delegate 패턴 등을 사용했다.
하지만 이 방식들은 콜백 지옥(Callback Hell)을 유발하거나, 코드가 복잡해지는 문제가 있었다.
Combine은 이런 문제를 해결하고, 데이터 스트림을 선언적으로 관리할 수 있도록 도와주는 프레임워크다.
이번 글에서는 Combine의 개념, 기본 사용법, 활용하는 방법까지 하나씩 알아보자
Combine이란?
Combine은 Apple이 iOS 13부터 도입한 비동기 & 반응형 프로그래밍 프레임워크다.
RxSwift와 같은 리액티브 프로그래밍 방식을 지원하며, 데이터 스트림을 다루는 강력한 기능을 제공한다.
Combine을 활용하면:
✅ 비동기 작업을 선언적으로 처리할 수 있다.
✅ 데이터 스트림을 조합하고 변환하는 것이 쉬워진다.
✅ 콜백 기반 코드의 복잡성을 줄이고, 가독성을 높일 수 있다.
Combine의 핵심 개념
Combine에서 가장 중요한 개념 3가지는 다음과 같다.
1️⃣ Publisher(퍼블리셔) → 데이터를 방출하는 역할
2️⃣ Subscriber(서브스크라이버) → 데이터를 구독하여 처리
3️⃣ Operator(연산자) → 데이터를 변환하거나 필터링
이 개념만 이해하면 Combine을 쉽게 활용할 수 있다!
1. Publisher(퍼블리셔)
Publisher는 데이터 스트림을 생성하는 역할을 한다.
쉽게 말해, 이벤트가 발생할 때마다 데이터를 방출하는 객체다.
Combine에서 제공하는 대표적인 퍼블리셔는 다음과 같다.
✅ Just (단일 값 방출)
import Combine
let publisher = Just("Hello, Combine!")
let subscriber = publisher.sink { value in
print("📌 값 수신: \(value)")
}
// 출력: 📌 값 수신: Hello, Combine!
Just는 한 번만 값을 방출하고 끝나는 Publisher다.
✅ Timer (반복적인 값 방출)
import Combine
let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common)
.autoconnect()
let subscription = timerPublisher.sink { value in
print("⏳ 현재 시간: \(value)")
}
Timer를 사용하면 1초마다 새로운 값을 방출할 수 있다.
2. Subscriber(서브스크라이버)
Publisher가 데이터를 방출하면, 이를 받아서 처리하는 역할을 하는 것이 Subscriber다.
Combine에서는 두 가지 방식으로 값을 구독할 수 있다.
✅ sink (간단한 구독)
import Combine
let publisher = Just("Hello, Combine!")
let subscription = publisher.sink { value in
print("📌 받은 값: \(value)")
}
sink는 가장 간단한 구독 방법으로, Publisher가 데이터를 방출할 때마다 클로저를 실행한다.
✅ assign (객체 속성에 직접 할당)
import Combine
class ViewModel {
@Published var message: String = ""
}
let viewModel = ViewModel()
let publisher = Just("Hello, Combine!")
let subscription = publisher.assign(to: &viewModel.$message)
print(viewModel.message) // Hello, Combine!
assign을 사용하면, Publisher의 데이터를 객체의 속성에 직접 연결할 수 있다.
3. Operator(연산자)
Combine의 가장 강력한 기능 중 하나는 Operator(연산자)를 활용해 데이터 스트림을 변형할 수 있다는 것이다.
RxSwift의 map, filter, flatMap과 유사한 개념이다.
✅ map (데이터 변환)
import Combine
let publisher = Just(10)
.map { $0 * 2 }
let subscription = publisher.sink { value in
print("🔢 변환된 값: \(value)")
}
// 출력: 🔢 변환된 값: 20
map을 사용하면 방출된 데이터를 변환할 수 있다.
✅ filter (조건에 맞는 값만 방출)
import Combine
let numbers = [1, 2, 3, 4, 5].publisher
let subscription = numbers
.filter { $0 % 2 == 0 }
.sink { value in
print("✅ 짝수: \(value)")
}
// 출력: ✅ 짝수: 2, ✅ 짝수: 4
filter는 조건을 만족하는 데이터만 방출하도록 필터링한다.
✅ combineLatest (두 개의 Publisher 결합)
import Combine
let publisher1 = PassthroughSubject<String, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
let subscription = publisher1.combineLatest(publisher2)
.sink { value in
print("🎯 결합된 값: \(value)")
}
publisher1.send("A")
publisher2.send(1)
publisher1.send("B")
publisher2.send(2)
// 출력:
// 🎯 결합된 값: ("A", 1)
// 🎯 결합된 값: ("B", 1)
// 🎯 결합된 값: ("B", 2)
combineLatest를 사용하면 두 개의 Publisher가 방출하는 최신 값을 결합하여 제공할 수 있다.
Combine 활용하기
Combine은 비동기 네트워크 요청을 처리하는 데 특히 강력하다.
기존 URLSession과 함께 Combine을 사용하면 다음과 같이 구현할 수 있다.
import Combine
struct API {
static func fetchData() -> AnyPublisher<string, error=""> {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
return URLSession.shared.dataTaskPublisher(for: url)
.map { String(data: $0.data, encoding: .utf8) ?? "" }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
let subscription = API.fetchData()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("✅ 완료")
case .failure(let error):
print("❌ 오류 발생: \(error)")
}
}, receiveValue: { value in
print("📥 받은 데이터: \(value)")
})
</string,>
✅ URLSession.dataTaskPublisher를 사용하면 네트워크 요청을 Combine으로 간편하게 처리할 수 있다.
✅ map, receive(on:), eraseToAnyPublisher() 등을 사용하여 데이터를 변환하고 최적화할 수 있다.
정리
이번 포스팅에서는 Combine의 기본 개념과 주요 기능을 가볍게 살펴봤다.
Combine은 비동기 작업을 선언적으로 처리할 수 있도록 도와주는 강력한 프레임워크이며,
이를 활용하면 데이터 흐름을 더욱 직관적으로 관리하고, 비동기 코드의 복잡성을 줄일 수 있다.
✅ 오늘 살펴본 핵심 개념 정리
- Publisher → 데이터를 방출하는 객체
- Subscriber → 데이터를 구독하여 처리하는 객체
- Operator → 데이터를 변형하고 필터링하는 기능
하지만, Combine의 진짜 강점은 연산자를 활용한 데이터 조작과, 기존의 UIKit 및 SwiftUI와의 결합에 있다.
이번 글에서는 기본적인 개념과 사용법을 다뤘지만, 다음에는 더 심도 있는 주제들을 다뤄봐야겠다
지금까지 Combine을 사용해 본 적이 없다면, 간단한 프로젝트에서 직접 활용해 보자!
'iOS' 카테고리의 다른 글
| TCA(The Composable Architecture): iOS 개발의 새로운 패러다임 (0) | 2025.03.11 |
|---|---|
| iOS 스태틱 vs 다이나믹 라이브러리, 뭐가 다를까? (0) | 2025.03.05 |
| iOS Xcode 빌드 단계(Build Process) 완벽 정리 (0) | 2025.03.04 |
| iOS 앱 생명주기(App Lifecycle) 제대로 알아보기 (0) | 2025.03.03 |
| OperationQueue - iOS에서 비동기 작업을 효율적으로 관리하는 방법! (0) | 2025.02.27 |