Combine

[Combine] Subject와 @Published... SwiftUI 연동까지

yujaehui 2025. 4. 7. 21:56

1. Subject란?

Subject는 Combine에서 Publisher와 Subscriber 역할을 동시에 수행하는 특수한 타입입니다.
외부에서 직접 값을 전달할 수 있기 때문에, 사용자 입력, 이벤트 트리거, 수동 제어가 필요한 상황에서 많이 사용됩니다.

Combine에서 제공하는 Subject는 2가지

타입 설명
PassthroughSubject 현재 값을 저장하지 않고 발행
CurrentValueSubject 하나의 값을 저장하며 새로운 구독자에게 즉시 전달

✅ PassthroughSubject 예제

import Combine

let subject = PassthroughSubject<String, Never>()

subject
    .sink { print("받은 값:", $0) }

subject.send("Hello") // 출력: 받은 값: Hello
subject.send("World") // 출력: 받은 값: World
  • 이전 값을 기억하지 않기 때문에, 구독 이전에 보낸 값은 전달되지 않음

✅ CurrentValueSubject 예제

let subject = CurrentValueSubject<Int, Never>(0)

subject
    .sink { print("구독자1:", $0) }

subject.send(1)

subject
    .sink { print("구독자2:", $0) } // 구독 즉시 현재 값인 1을 받음

subject.send(2)
  • 항상 최신 값을 유지하고 새로운 구독자에게도 바로 전달됨

2. @Published – SwiftUI와의 연결 고리

@Published는 Combine에서 가장 많이 사용되는 Property Wrapper 중 하나입니다.
클래스 내부에서 선언된 프로퍼티에 값이 변경될 때마다 Publisher처럼 동작하며, SwiftUI의 View를 자동으로 갱신하는 데 사용됩니다.

class MyViewModel: ObservableObject {
    @Published var username: String = ""
}

@Published는 실제로 내부적으로 이런 Publisher를 가짐

$username: Published<String>.Publisher

이를 통해 외부에서 Combine을 통해 값의 변화를 감지하거나 가공할 수 있습니다.


3. SwiftUI + Combine 연동 예제

import SwiftUI
import Combine

class CounterViewModel: ObservableObject {
    @Published var count = 0
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $count
            .sink { newValue in
                print("카운트 변경됨: \(newValue)")
            }
            .store(in: &cancellables)
    }
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject private var viewModel = CounterViewModel()
    
    var body: some View {
        VStack(spacing: 20) {
            Text("카운트: \(viewModel.count)")
                .font(.largeTitle)
            
            Button("증가하기") {
                viewModel.increment()
            }
        }
        .padding()
    }
}

✅ 포인트 정리

  • @Published로 선언된 count의 값이 바뀌면 SwiftUI가 뷰를 자동으로 갱신
  • 내부에서는 Combine의 Publisher처럼 동작하여 sink로 값 추적 가능
  • ObservableObject를 사용하면 SwiftUI에서 ViewModel의 변경 사항을 구독할 수 있음

한 눈에 정리

개념 설명
Subject 외부에서 직접 값을 발행할 수 있는 Publisher
PassthroughSubject 이전 값을 저장하지 않음
CurrentValueSubject 최신 값을 저장하고 새 구독자에게도 즉시 전달
@Published 값이 변경될 때 Combine Publisher로 값 발행
SwiftUI 연동 ObservableObject + @Published로 UI 자동 갱신 가능