관리 메뉴

J

[Combine] Scheduler와 비동기 처리 이해하기 본문

iOS/Combine

[Combine] Scheduler와 비동기 처리 이해하기

yujaehui 2025. 4. 8. 23:30

Scheduler와 비동기 처리 이해하기

Combine을 실제 프로젝트에서 사용하다 보면,

"어떤 쓰레드에서 이 작업이 실행될까?", "UI 업데이트는 메인에서 해야 하지 않나?"

같은 질문이 생기게 됩니다.


이럴 때 사용하는 것이 바로 Scheduler(스케줄러) 입니다.

이번 글에서는 Combine에서의 쓰레드 처리와 비동기 흐름 제어 방법에 대해 기초부터 차근히 알아보겠습니다.


Scheduler란?

Scheduler는 Combine에서 작업을 어떤 스레드/큐에서 실행할지를 제어할 수 있게 해주는 개념입니다.
즉, 데이터를 발행하거나 받는 시점의 쓰레드를 지정할 수 있습니다.

대표적인 Scheduler 종류

Scheduler 설명
DispatchQueue.main 메인 스레드 (UI 업데이트에 필수)
DispatchQueue.global() 백그라운드 글로벌 큐
RunLoop.main 메인 런루프 (iOS UI 작업용)
OperationQueue 커스텀 큐 설정 가능

receive(on:) vs subscribe(on:)

이 두 메서드는 비슷해 보이지만 역할이 다릅니다.

✅ receive(on:)

  • Subscriber가 데이터를 처리하는 스레드를 지정
  • UI 업데이트에 자주 사용 (보통 DispatchQueue.main)

✅ subscribe(on:)

  • Publisher가 데이터를 생성하는 스레드를 지정
  • 네트워크 요청, 파일 IO 등 작업의 시작 위치에 사용

예제: 비동기 처리 + Scheduler

import Combine
import Foundation

var cancellables = Set<AnyCancellable>()

func loadData() -> AnyPublisher<String, Never> {
    return Just("서버에서 받은 데이터")
        .delay(for: .seconds(2), scheduler: DispatchQueue.global()) // 백그라운드에서 딜레이
        .subscribe(on: DispatchQueue.global()) // 작업 시작 위치
        .receive(on: DispatchQueue.main) // UI 업데이트를 위해 메인으로 전환
        .eraseToAnyPublisher()
}

loadData()
    .sink { value in
        print("UI에서 받은 값: \(value) / Thread: \(Thread.isMainThread ? "Main" : "Background")")
    }
    .store(in: &cancellables)

 

(약 2초 후)
UI에서 받은 값: 서버에서 받은 데이터 / Thread: Main

 

  1. 백그라운드에서 값이 생성되고
  2. 메인 스레드에서 UI 관련 작업이 수행됨

언제 어떤 Scheduler를 써야 할까?

상황 사용하는 Scheduler
네트워크, 디스크 작업 시작 subscribe(on: DispatchQueue.global())
UI 업데이트 receive(on: DispatchQueue.main)
타이머, 입력 debounce RunLoop.main 또는 DispatchQueue.main

Debounce와 Scheduler

사용자 입력처럼 빠르게 발생하는 이벤트를 다룰 때는 debounce와 Scheduler 조합이 매우 유용합니다.

let searchText = PassthroughSubject<String, Never>()

searchText
    .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
    .sink { keyword in
        print("검색어 처리: \(keyword)")
    }
    .store(in: &cancellables)
  • 입력이 멈춘 지 0.3초가 지나야 값을 발행
  • 너무 자주 발생하는 이벤트를 필터링하여 리소스 낭비 방지

정리

개념 설명
Scheduler 작업이 실행될 스레드를 지정
subscribe(on:) Publisher가 실행될 스레드 지정
receive(on:) Subscriber가 처리할 스레드 지정
DispatchQueue.main UI 업데이트에 필수
debounce + Scheduler 빠른 이벤트 제어에 적합

Combine을 제대로 활용하려면 흐름의 시점과 쓰레드를 명확히 구분하는 것이 중요합니다.
특히 SwiftUI, UIKit에서 Combine을 사용할 땐 UI는 항상 메인 스레드에서 처리해야 한다는 점, 잊지 마세요.