클라이언트

[Combine] Combine의 기본 개념 및 예제

애기공룡훈련병 2025. 2. 16. 06:25
반응형

Swift Combine이란?

애플이 WWDC 2019에서 공개한 Combine은 비동기 이벤트 처리를 위한 프레임워크입니다. 기존의 NotificationCenter, KVO(Key-Value Observing), Completion Handler, Delegation 방식과 달리, Combine을 활용하면 선언형(Declarative) 방식으로 데이터를 처리할 수 있습니다.

Combine의 핵심 개념

Publisher와 Subscriber

  • Publisher: 데이터를 방출하는 역할을 합니다.
  • Subscriber: 데이터를 받아서 처리하는 역할을 합니다.
  • 예제 코드:위 코드에서 Just는 단일 값을 방출하는 Publisher이며, sinkSubscriber 역할을 합니다.
import Combine

let myPublisher = Just("Hello, Combine!")
let mySubscriber = myPublisher.sink { value in
    print(value)
}

Operator

  • Combine에는 다양한 연산자(Operator)가 존재하여 데이터 변환, 필터링 등을 수행할 수 있습니다.
  • 예제 코드:
import Combine

let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher

let subscription = publisher
    .map { $0 * 2 }
    .filter { $0 > 5 }
    .sink { value in
        print(value)  // 6, 8, 10 출력
    }

Subject

  • PassthroughSubjectCurrentValueSubject가 있으며, 직접 값을 방출할 수 있습니다.
  • 예제 코드:
import Combine

let subject = PassthroughSubject<String, Never>()

let subscription = subject.sink { value in
    print("받은 값: \(value)")
}

subject.send("안녕하세요!")
subject.send("Combine 예제입니다!")

Combine을 활용한 비동기 처리

URLSession과 Combine

import Combine
import Foundation

let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!

let cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .map { $0.data }
    .decode(type: Post.self, decoder: JSONDecoder())
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("완료!")
        case .failure(let error):
            print("오류 발생: \(error)")
        }
    }, receiveValue: { post in
        print("받은 데이터: \(post)")
    })

struct Post: Codable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

모듈 간 데이터 전달

import Combine

class DataProvider {
    let dataPublisher = PassthroughSubject<String, Never>()
}

class DataReceiver {
    var cancellables = Set<AnyCancellable>()
    
    func subscribe(to provider: DataProvider) {
        provider.dataPublisher
            .sink { data in
                print("받은 데이터: \(data)")
            }
            .store(in: &cancellables)
    }
}

let provider = DataProvider()
let receiver = DataReceiver()
receiver.subscribe(to: provider)

provider.dataPublisher.send("새로운 데이터 도착!")

SwiftUI와 Combine 연동 (계산기 예제)

import SwiftUI
import Combine

class CalculatorViewModel: ObservableObject {
    @Published var input: String = ""
    @Published var result: String = "0"
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $input
            .map { self.calculate(expression: $0) }
            .assign(to: &$result)
    }
    
    func appendValue(_ value: String) {
        input += value
    }
    
    func clear() {
        input = ""
        result = "0"
    }
    
    private func calculate(expression: String) -> String {
        let exp = NSExpression(format: expression)
        if let value = exp.expressionValue(with: nil, context: nil) as? Double {
            return String(format: "%.2f", value)
        }
        return "Error"
    }
}

struct CalculatorView: View {
    @StateObject var viewModel = CalculatorViewModel()
    
    let buttons: [[String]] = [
        ["7", "8", "9", "/"],
        ["4", "5", "6", "*"],
        ["1", "2", "3", "-"],
        ["0", ".", "=", "+"]
    ]
    
    var body: some View {
        VStack {
            Text(viewModel.result)
                .font(.largeTitle)
                .padding()
            
            ForEach(buttons, id: \ .self) { row in
                HStack {
                    ForEach(row, id: \ .self) { button in
                        Button(action: {
                            if button == "=" {
                                viewModel.input = viewModel.result
                            } else {
                                viewModel.appendValue(button)
                            }
                        }) {
                            Text(button)
                                .font(.title)
                                .frame(width: 80, height: 80)
                                .background(Color.gray.opacity(0.2))
                                .cornerRadius(10)
                        }
                    }
                }
            }
            Button("Clear") {
                viewModel.clear()
            }
            .font(.title)
            .padding()
        }
    }
}

 

 

Combine은 비동기 데이터 흐름을 쉽게 관리할 수 있도록 해주는 강력한 프레임워크입니다. 

반응형