본문 바로가기
스파르타코딩 클럽/사전 캠프

15. 스파르타 코딩클럽 [사전캠프 - Combine]

by UDDT 2025. 2. 25.

기본

 Combine

    - Combine Framework : 애플에서 제공하는 반응형 프로그래밍(Reactive Programming) 프레임워크

      * 데이터의 흐름을 선언적으로 처리하고 비동기적으로 처리할 때 사용

       사용 예) 네트워크 요청, 사용자 입력, 타이머 등과 같은 비동기 이벤트

 

    - 주요개념

       발행자(Publisher) : 데이터를 제공하는 주체. 데이터가 발생하거나 변경될 때 이벤트를 발행

        예) 네트워크 요청 결과, 사용자 입력 이벤트, 타이머 등

       구독자(Subscriber) : 데이터를 수신하고 처리하는 주체. Publisher가 발행하는 데이터를 받아서 처리하는 역할

        예) UI에 값을 표시, 처리 결과 출력 등

       연산자(Operators) : Publisher와 Subscriber 사이에서 데이터를 변환하거나 필터링하는 중간 처리 역할

        예) 값을 변경하거나 조건에 맞는 값만 필터링

 

    - Combine의 흐름

       Publisher(데이터 발행) → Operator(데이터 변환, 필터링) → Subscriber(데이터를 최종적으로 처리

 

import Combine

// Publisher 생성
let numbers = [1, 2, 3, 4, 5].publisher

// Subscriber를 통해 값 출력
numbers
    .map { $0 * 2 } // 값 변환
    .sink { print($0) } // 값 출력

 

 

 

실습

Combine 기본 구현

간단한 Publisher와 Subscriber 만들기 :
- 배열 [10, 20, 30, 40, 50]을 Publisher로 생성하고, 각 값을 2배로 변환한 후 출력하세요.
import Combine

let numbers = [10, 20, 30, 40, 50].publisher

numbers
    .map { $0 * 2}
    .sink { print($0) }
    
/*
20
40
60
80
100
*/
Just Publisher 사용:
- 단일 값 100을 제공하는 Just를 사용하여 값을 변환(+50)하고 출력하세요.
let justPublisher = Just(100)

justPublisher
    .map { $0 + 50 }
    .sink { print($0) }

// 150

 

 Combine으로 비동기 데이터 처리하기

URLSession과 Combine 사용:
 - Combine을 사용해 네트워크 요청을 수행하고 결과를 출력하세요.
 - API: https://jsonplaceholder.typicode.com/todos/1
 - 요청 결과의 title 값을 출력하세요.
import Combine
import Foundation

let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
var cancellable: AnyCancellable?

cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .map { $0.data }  // 네트워크 응답에서 'data' 부분만 추출
    .decode(type: Todo.self, decoder: JSONDecoder())  // 'data'를 'Todo' 모델로 디코딩
    .sink(
        receiveCompletion: { print("Completion: \($0)") },  // 네트워크 요청 완료 처리
        receiveValue: { print("Title: \($0.title)") }  // 'title'을 출력
    )

struct Todo: Codable {
    let title: String
}

 

코드를 뜯어보자

// 필요한 라이브러리 가져오기
import Combine
import Foundation

 Combine 프레임워크를 가져와서 비동기 데이터 흐름을 관리,

 Foundation 표준 라이브러리를 가져와서 기본적인 데이터 처리, 네트워크 요청, 시간 처리 등의 작업 수행

 

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

 요청할 API 주소로 url 객체를 생성, 강제 추출 연산자를 사용하여 언래핑

 

// 객체 생성
var cancellable: AnyCancellable?

구독을 취소할 수 있는 cancellable 객체 생성

 

cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .map { $0.data }  // 네트워크 응답에서 'data' 부분만 추출
    .decode(type: Todo.self, decoder: JSONDecoder())  // 'data'를 'Todo' 모델로 디코딩
    .sink(
        receiveCompletion: { print("Completion: \($0)") },  // 네트워크 요청 완료 처리
        receiveValue: { print("Title: \($0.title)") }  // 'title'을 출력
    )

 - URLSession.shared.dataTaskPublisher(for: url) : 지정된 URL로 GET 요청을 보내고, 그 결과를 비동기적으로 처리

    URLSession : iOS에서 HTTP 요청을 처리하는 객체

    dataTaskPublisher(for:) : URLSession에서 제공하는 Combine Publisher.  

                                               네트워크 요청을 실행하고, 그 결과로 data와 response를 튜플로 반환

 - map { $0.data } : 튜플로 반환된 값 중 data만 추출

 - decode(type: Todo.self, decoder: JSONDecoder()) : JSON 디코딩

    Todo.self : Todo 모델로 데이터를 디코딩

    JSONDecoder : JSON 데이터를 Swift의 객체로 변환해주는 클래스(서버로부터 받은 JSON 데이터를 디코딩

 - sink : Subscriber. 여기서는 2개의 클로저를 전달

 - receiveCompletion : 요청이 완료되었을 때 호출되는 클로저. (finished 또는 failure 상태를 포함)

 - receiveValue : 요청이 성공하고, 디코딩된 Todo 객체가 전달되면 호출되는 클로저

 

struct Todo: Codable {
    let title: String
}

 

 Todo 구조체는 Codable 프로토콜을 채택한 모델.

 - Codable : 디코딩(JSON → 객체)과 인코딩(객체 → JSON)을 자동으로 처리

 


Timer Publisher 사용:
- 1초마다 현재 시간을 출력하는 Combine 타이머를 생성하세요.
- 5초 후에는 구독을 취소하세요.
import Combine

let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()
var cancellable: AnyCancellable?

cancellable = timer
    .sink { print($0) }  // 타이머 발행 이벤트를 출력

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    cancellable?.cancel()  // 5초 후 구독 취소
}

 

코드를 뜯어보자

let timer = Timer.publish(every: 1.0, on: .main, in: .common).autoconnect()

- Timer.publish : 주기적인 이벤트를 발생시키는 Publisher를 생성

- every: 1.0 : 1초마다 이벤트를 발행(매 1초마다 Date 객체를 방출)

- on: .main : 타이머의 실행 위치 (메인 스레드에서 실행)

- in: .common : 타이머가 런루프의 common 모드에서 실행되도록 설정(UI 이벤트와 타이머가 동시에 실행될 수 있도록)

- autoconnect() : 타이머 Publisher를 자동으로 연결(이 코드가 실행되는 즉시 타이머가 시작)

 

var cancellable: AnyCancellable?

cancellable = timer
    .sink { print($0) }  // 타이머 발행 이벤트를 출력

 

- sink : Subscriber

- print($0): 타이머에서 발행된 Date 값을 출력 (2025-02-25 12:00:00 +0000와 같은 형식으로 출력)

- cancellable : AnyCancellable 타입의 변수, 나중에 타이머의 구독을 취소할 수 있도록 만든 것

 

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    cancellable?.cancel()  // 5초 후 구독 취소
}

- DispatchQueue.main.asyncAfter : 주어진 시간 뒤에 비동기적으로 특정 작업을 실행하도록 예약

- .now() + 5 : 현재 시점에서 5초 뒤

- cancellable?.cancel() : 5초 후 타이머 Publisher의 구독을 취소

최근댓글

최근글

skin by © 2024 ttuttak