๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ Today I Learned/Swift ๋ฌธ๋ฒ•

[Swift] RxSwift

by Joseph Seong 2023. 12. 15.

 

 

RxSwift

 

๐Ÿ–๏ธ Swift ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ํ•จ์ˆ˜ํ˜• ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ์˜ต์ €๋ฒ„๋ธ” ์‹œํ€€์Šค์™€ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค.

 

 

RxSwift ์ด๋ž€?

 

ReactiveX(Reactive Extensions) ํŒจํ„ด์˜ Swift ๋ฒ„์ „์œผ๋กœ, Swift ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

RxSwift๋Š” ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ๊ณผ ์ด๋ฅผ ๋‹ค๋ฃจ๋Š” ์—ฐ์‚ฐ์ž๋“ค์„ ํ†ตํ•ด ๋น„๋™๊ธฐ ๋ฐ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€์›ํ•œ๋‹ค.

  1. Observable:
    • ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž…์œผ๋กœ, ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋‚˜ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœ(emit)ํ•œ๋‹ค.
    • Observable์€ ์ด๋ฒคํŠธ ์‹œํ€€์Šค๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.
    • ์ด ์ด๋ฒคํŠธ๋Š” next, error, completed์™€ ๊ฐ™์€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.
  2. Observer:
    • Observable์—์„œ ๋ฐฉ์ถœ๋œ ๋ฐ์ดํ„ฐ๋‚˜ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฐ์ฒด๋กœ, ์ด๋ฅผ ๊ตฌ๋…(subscribe)ํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์‹œํ•˜๊ณ  ์ฒ˜๋ฆฌํ•œ๋‹ค.
  3. Operator:
    • Observable์„ ๋ณ€ํ˜•ํ•˜๊ฑฐ๋‚˜ ์กฐ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์กฐ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
    • RxSwift์—๋Š” ๋งŽ์€ ๋‹ค์–‘ํ•œ ์—ฐ์‚ฐ์ž๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ์–ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜, ๊ฒฐํ•ฉ, ์กฐ์ž‘ํ•˜๋Š” ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  4. Schedulers:
    • ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ์ž‘์—…์ด ์–ด๋А ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋Š”์ง€ ์ œ์–ดํ•œ๋‹ค.
    • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ UI ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋‚˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋“ฑ์˜ ์ผ์„ ์Šค์ผ€์ค„๋งํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

 

library

  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Library)๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ๋ชจ์Œ์„ ๋งํ•œ๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ์˜ ๋ชจ์Œ์ด๋ฉฐ, ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์ด๋‚˜ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์ ์ด๊ณ  ํ‘œ์ค€ํ™”๋œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•˜๊ณ  ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์™€ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณต๋œ๋‹ค:
    • ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Standard Library): ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค. ์ด๋Š” ์–ธ์–ด ์ž์ฒด์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํ•จ์ˆ˜, ์ž๋ฃŒ๊ตฌ์กฐ, ํŒŒ์ผ ์ž…์ถœ๋ ฅ, ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Swift์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Swift ์–ธ์–ด์™€ ํ•จ๊ป˜ ์ œ๊ณต๋˜๋Š” ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋“ค์„ ํฌํ•จํ•œ๋‹ค.
    • ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Third-party Library): ๊ฐœ์ธ์ด๋‚˜ ์กฐ์ง์ด ๊ฐœ๋ฐœํ•œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ณต๊ฐœ๋˜์–ด ์žˆ๋‹ค. ๋„คํŠธ์›Œํ‚น, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ, UI ๊ตฌ์„ฑ ์š”์†Œ, ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ, ์•”ํ˜ธํ™”, ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์–ธ์–ด์—์„œ๋Š” ํ•ด๋‹น ์–ธ์–ด๋‚˜ ํ”Œ๋žซํผ์— ๋งž๋Š” ๋‹ค์–‘ํ•œ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ•œ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋ณดํ†ต ํ•จ์ˆ˜, ํด๋ž˜์Šค, ๋ชจ๋“ˆ, ํ”„๋ ˆ์ž„์›Œํฌ ํ˜•ํƒœ๋กœ ์ œ๊ณต๋˜๋ฉฐ, ์ด๋ฅผ ํ”„๋กœ์ ํŠธ์— ์ถ”๊ฐ€ํ•˜๊ณ  ํ™œ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋“ค์€ ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ๊ณผ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ๋„์™€์ค€๋‹ค.

 

Dependency Manager(์˜์กด์„ฑ ๊ด€๋ฆฌ ๋„๊ตฌ)

  • ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์™ธ๋ถ€ ์˜์กด์„ฑ๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ํ”„๋กœ์ ํŠธ์— ํšจ๊ณผ์ ์œผ๋กœ ํ†ตํ•ฉํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ
  • ์ด๋Ÿฌํ•œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์†์‰ฝ๊ฒŒ ๊ฐ€์ ธ์˜ค๊ณ  ๋ฒ„์ „์„ ๊ด€๋ฆฌํ•˜๋ฉฐ, ํ”„๋กœ์ ํŠธ์—์„œ ํ•„์š”๋กœ ํ•˜๋Š” ์˜์กด์„ฑ๋“ค์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋Œ€ํ‘œ์ ์ธ Dependency Manager
    • CocoaPods: iOS ๋ฐ macOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ Swift/Objective-C ํ”„๋กœ์ ํŠธ์˜ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋„๊ตฌ. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์†์‰ฝ๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ณ , ํ”„๋กœ์ ํŠธ ์„ค์ •์„ ๊ฐ„์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • Carthage: CocoaPods์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋นŒ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•œ๋‹ค. ๋นŒ๋“œ๋œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ”„๋กœ์ ํŠธ์— ์ˆ˜๋™์œผ๋กœ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.
    • Swift Package Manager (SPM): Swift ํ”„๋กœ์ ํŠธ์˜ ํŒจํ‚ค์ง€ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ณต์‹์ ์ธ Swift ๋„๊ตฌ. ์• ํ”Œ์˜ ๊ณต์‹ ์ง€์›์„ ๋ฐ›๊ณ  ์žˆ์œผ๋ฉฐ, Swift-only ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

 

RxSwift ์‚ฌ์šฉ๋ฒ•

  1.  Xcode - File - New - Project
  2. Product Name ์ž…๋ ฅ
  3. CocoaPod ์„ค์น˜
  4. Podfile ์ƒ์„ฑ
  5. Podfile ํŽธ์ง‘(vi Podfile)
  6. pod install
  7. .xcworkspace ํŒŒ์ผ ์‹คํ–‰ → ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€์—์„œ Playground ์ƒ์„ฑ

 

 

RxSwift ์˜ˆ์‹œ

 

// swift ์˜ˆ์‹œ
import Foundation

class DataModel {
    var textValue: String = ""

    func updateText(to newValue: String) {
        textValue = newValue
        print("Value updated: \(textValue)")
    }
}

let dataModel = DataModel()

dataModel.updateText(to: "Hello, Swift!")
dataModel.updateText(to: "Another value")

/*
์ถœ๋ ฅ
Value changed to: Hello, Swift!
Value changed to: Another value
*/

// swift์˜ didset์„ ์ด์šฉํ•œ ์˜ˆ์‹œ
class DataModel {
    var textValue: String = "" {
        didSet {
            print("Value updated: \(textValue)")
        }
    }
}

let dataModel = DataModel()

func changeText(to newValue: String) {
    dataModel.textValue = newValue
}

changeText(to: "Hello, Swift!")
changeText(to: "Another value")

/*
์ถœ๋ ฅ
Value changed to: Hello, Swift!
Value changed to: Another value
*/

// RxSwift์˜ ์˜ˆ์‹œ
import Foundation
import RxSwift
import RxCocoa

class DataModel {
    let textValueSubject = BehaviorSubject<String>(value: "")
    
    var textValue: Observable<String> {
        return textValueSubject.asObservable()
    }
}

let dataModel = DataModel()

let disposable = dataModel.textValue.subscribe(onNext: { newValue in
    print("Value changed to: \(newValue)")
})

dataModel.textValueSubject.onNext("Hello, RxSwift!")
dataModel.textValueSubject.onNext("Another value")

/*
์ถœ๋ ฅ
Value changed to: 
Value changed to: Hello, RxSwift!
Value changed to: Another value
*/

 

  1. DataModel ํด๋ž˜์Šค๋Š” BehaviorSubject๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ textValueSubject๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ์ด๋ฅผ textValue์˜ Observable๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  2. let disposable = dataModel.textValue.subscribe(onNext: { newValue in ... })์—์„œ subscribe ๋ฉ”์„œ๋“œ๋Š” textValue์˜ Observable์„ ๊ตฌ๋…ํ•˜๊ณ , ๊ฐ’์˜ ๋ณ€๊ฒฝ์ด ์žˆ์„ ๋•Œ๋งˆ๋‹ค onNext ํด๋กœ์ € ๋‚ด์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  3. dataModel.textValueSubject.onNext("Hello, RxSwift!")๋Š” textValueSubject์˜ onNext๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ "Hello, RxSwift!"๋ฅผ ๋ฐฉ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๊ตฌ๋… ์ค‘์ธ subscribe์—์„œ "Value changed to: Hello, RxSwift!"๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  4. dataModel.textValueSubject.onNext("Another value")๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์˜ ๋ณ€๊ฒฝ๊ณผ ํ•ด๋‹น ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์ถœ๋ ฅ์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.