ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๋ฐ˜์‘ํ˜•

img


๐ŸŽฏ RxSwift ์ดํ•ดํ•˜๊ธฐ_01


  • ๐Ÿ™‹ ์ด ๊ธ€์€ RxSwift์— ๋Œ€ํ•œ ์—„์ฒญ๋‚œ ์ดํ•ด๋‚˜ ๋ณธ์งˆ์„ ํŒŒ์•…ํ•œ๋‹ค๊ธฐ๋ณด๋‹ค ์ž…๋ฌธํ•˜๋Š” ์ž…์žฅ์—์„œ ์ž‘์„ฑํ•˜๋Š” ๊ธ€์ด์—์š”! ๊ณฐํŠ€๊น€๋‹˜์˜ ๊ฐ•์˜(ํ•˜๋‹จ์— ๋งํฌ) ๋ฅผ ๋“ค์œผ๋ฉฐ ์ •๋ฆฌํ•œ ๋‚ด์šฉ, ์ƒ๊ฐ๋“ค์„ ์ž‘์„ฑํ•  ์˜ˆ์ •์ด์—์š”. ํ‹€๋ฆฐ ๋ถ€๋ถ„์ด๋‚˜ ์ถ”๊ฐ€ํ•˜๋ฉด ์ข‹์€ ๋ถ€๋ถ„์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :]

  • ์ €์ž‘์ž์— ๋Œ€ํ•œ ํ‘œ์‹œ์™€ ๋งํฌ๊ฐ€ ์žˆ์ง€๋งŒ ํ˜น์‹œ๋‚˜ ๋ฌธ์ œ๊ฐ€ ๋  ๊ฒฝ์šฐ ๋ฐ”๋กœ ์‚ญ์ œํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    Git Contributors์— ๋ฐ˜๊ฐ€์šด ์ด๋ฆ„์ด..


๐Ÿง‘๐Ÿปโ€๐Ÿ’ป What - RxSwift๋ž€?


  • RxSwift๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์šฐ์„  Rx๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ์–ด์š”. ReactiveX ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€๋ฅผ ๋“ค์–ด๊ฐ€๋ณด๋ฉด ์ฒซ ํ™”๋ฉด์— ์•„๋ž˜์™€ ๊ฐ™์€ ์„ค๋ช…์ด ๋‚˜์™€์š”!

    An API for asynchronous programming with observable streams


    ํ•ด์„ํ•ด๋ณด๋ฉด, Rx๋ž€ observable streams๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ API ์—์š”. RxSwift๋Š” ๋‹น์—ฐํžˆ ์ด Rx๋ฅผ ํ™œ์šฉํ•˜๋Š” Swift์ด๊ฒ ์ฃ ? ๊ทธ๋Ÿผ ์ด์ œ๋ถ€ํ„ฐ ์ € ํ•œ ๋ฌธ์žฅ์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•œ ๊ธ€์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค!



โš™๏ธ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋ž€?


  • ๊ธ€์˜ ๋ชฉ์ ์ด ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋žจ์„ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค๋ณด๋‹ˆ ์ž์„ธํ•œ ์„ค๋ช…์€ ์–ด๋ ค์šธ ๊ฒƒ ๊ฐ™์ง€๋งŒ ๋ฆฌ๋งˆ์ธ๋“œ ์ˆ˜์ค€์œผ๋กœ ์ ์–ด๋ณด๋ ค๊ณ  ํ•ด์š”. ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋ฐ˜๋Œ€ ๊ฐœ๋…์ด์—์š”! ๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํ•œ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ํ•˜๋Š” ๊ฐœ๋…์ด๋‹ˆ๊นŒ, ๋น„๋™๊ธฐ๋Š” ์–ด๋–ค ํ•œ ์ž‘์—…์ด ์ง„ํ–‰๋  ๋•Œ ๋‹ค๋ฅธ ์ž‘์—…๋„ ๋™์‹œ์— ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋…์ด๊ฒ ์ฃ ?


  • ์˜ˆ๋ฅผ ๋“ค๋ฉด, ์ˆซ์ž๋ฅผ ์˜ฌ๋ ค์ฃผ๋Š” ์ž‘์—…์ด ์ง„ํ–‰๋˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด ๋ฐ›์•„์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด์—์š”. ๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋ฉด ์ˆซ์ž๋ฅผ ์˜ฌ๋ ค์ฃผ๋‹ค๊ฐ€ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ›๋Š” ์ž‘์—…์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์ˆซ์ž๋ฅผ ์˜ฌ๋ ค์ฃผ๋Š” ์ž‘์—…์€ ๋ฉˆ์ถ”๊ฒŒ ๋˜์š”. ์•„๋ž˜๋Š” ๊ทธ ๋™๊ธฐ์  ์ž‘์—…์˜ ์˜ˆ์‹œ์—์š”. ๋ฐ˜๋Œ€๋กœ ๋น„๋™๊ธฐ์ ์ด๋ผ๋ฉด ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด ๋ฐ›๋Š” ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ์ˆซ์ž๋Š” ๊ณ„์† ์˜ฌ๋ผ๊ฐˆ ๊ฑฐ์—์š”.


  • ์ด๋Ÿฐ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๋“ค๋กœ PromiseKit, Bolts๊ฐ€ ์žˆ์–ด์š”. PromiseKit์€ ๊ฐ€์žฅ ์œ ๋ช…ํ•œ๋ฐ ์ด Promise๋ผ๋Š” ๋ฐฉ์‹์ด JavaScript์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ํ•ด์š”! ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•œ ๊ธ€์€ ์ถ”ํ›„์— ์–ธ์  ๊ฐ€.. ์จ๋ณผ๊ฒŒ์š”!


๐Ÿ“š Why - RxSwift๋Š” ์™œ ์‚ฌ์šฉํ•˜๋‚˜์š”?


  • ์œ„์— PromiseKit, Bolts์—ญ์‹œ ๋งˆ์ฐฌ๊ฐ€์ง€๋ผ๊ณ  ์ƒ๊ฐํ•ด์š”. ๊ฒฐ๊ตญ "๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ"์„ ์–ด๋–ป๊ฒŒํ•˜๋ฉด ๋” ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„๊นŒ ๊ฐ€ ํฌ์ธํŠธ์—ฌ์„œ Asyncํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋˜์•ผํ•˜๋Š” ์ž‘์—…์ด ์žˆ์„ ๋•Œ ์กฐ๊ธˆ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋กœ์„œ RxSwift๋ฅผ ์‚ฌ์šฉํ•ด์š”.


  • ๊ทธ๋Ÿผ ์™œ PromiseKit์ด๋‚˜ Bolts๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๊ตณ์ด RxSwift๋ฅผ ์‚ฌ์šฉํ•˜๋‚˜?๋ผ๋Š” ์˜๋ฌธ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ƒ๊ธธ๊ฑฐ์—์š”(๋‚˜๋งŒ..?). RxSwift์—๋Š” ๋ฌด์–ธ๊ฐ€ ํ•œ ๊ฐœ๊ฐ€ ๋” ์žˆ๋Š”๋ฐ ๊ทธ๊ฒŒ Operators์—์š”. ์ž์„ธํ•œ ์„ค๋ช…์€ ์ดํ›„ ์ด์–ด์ ธ์š”! ๋‹ค๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” "RxSwift๋Š” ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฐ„๊ฒฐํ™”๋ฅผ ์œ„ํ•ด์„œ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ค‘์—์„œ๋„ ๋‹ค๋ฅธ Kit๊ณผ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค" ์ •๋„๋กœ ์ •๋ฆฌํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”.


๐Ÿ›  How - RxSwift ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ


  • ๋ชจ๋“  ์˜ˆ์ œ์ฝ”๋“œ๋Š” ๊ณฐํŠ€๊น€๋‹˜์˜ github์ด ์ถœ์ฒ˜์—์š”!

  • RxSwift์˜ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์‚ดํŽด๋ณผ๊ฒŒ์š”

    @IBAction func onLoadImage(_ sender: Any) {
            imageView.image = nil
    
            _ = rxswiftLoadImage(from: LARGER_IMAGE_URL)
                .observeOn(MainScheduler.instance)
                .subscribe({ result in
                    switch result {
                    case let .next(image):
                        self.imageView.image = image
    
                    case let .error(err):
                        print(err.localizedDescription)
    
                    case .completed:
                        break
                    }
                })
        }
    
    // MARK: - RxSwift
    func rxswiftLoadImage(from imageUrl: String) -> Observable<UIImage?> {
            return Observable.create { seal in
                asyncLoadImage(from: imageUrl) { image in
                    seal.onNext(image)
                    seal.onCompleted()
                }
                return Disposables.create()
            }
        }

    ์œ„์˜ ์ฝ”๋“œ๋Š” ์•„๊นŒ ์˜ˆ์‹œ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด ๋ฐ›๊ธฐ ์œ„ํ•ด "๋™๊ธฐ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋Š” ๊ฒฝ์šฐ์˜ ๋กœ์ง์ด์—์š”. ์—ฌ๊ธฐ์„œ rxswiftLoadImage ๋ฉ”์†Œ๋“œ๋Š” Observable์„ ๋ฐ˜ํ™˜ํ•ด์š”. ๊ทธ๋ฆฌ๊ณ  onLoadImage์—์„œ ์ด observable์„ subscribeํ•˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ disposable์ด ๋ฐ˜ํ™˜๋˜์š”. ์—ฌ๊ธฐ์„œ disposable์ด๋ž€ ์‰ฝ๊ฒŒ ๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด์—์š”! (TMI: dis(off) + pos)

    ๊ทธ๋Ÿผ ์ด ๋‹ค์šด๋กœ๋“œ ์ž‘์—…์„ ์ทจ์†Œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”! ์ด๋ฆ„์€ onCancel๋กœ ํ• ๊ฒŒ์š”.

    ์ด onCancel์—์„œ ์ทจ์†Œ๋ฅผ ํ•˜๋ ค๋ฉด ๋ญ˜ ์ทจ์†Œํ•˜๋ ค๋Š”์ง€ ์•Œ์•„์•ผ๊ฒ ์ฃ ? ๊ทธ๋ž˜์„œ ์ด ViewController๊ฐ€ ์œ„์˜ disposable์„ ์•Œ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœํผํ‹ฐ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ  ๊ทธ ํ”„๋กœํผํ‹ฐ์— disposable ๊ฒฐ๊ณผ๋ฌผ์„ ๋„ฃ๊ฒŒ ํ• ๊ฑฐ์—์š”.


    var disposable: Disposable?
    
    @IBAction func onLoadImage(_ sender: Any) {
            imageView.image = nil
            self.disposable = rxswiftLoadImage(from: LARGER_IMAGE_URL)
                  ...
    
    @IBAction func onCancel(_ sender: Any) {
            self.disposable?.dispose()
        }

    onLoadImage์—์„œ ์ƒ๊ธด disposable์„ ํ”„๋กœํผํ‹ฐ๋กœ VC๊ฐ€ ๊ฐ–๊ณ , onCancel์—์„œ VC๊ฐ€ disposable์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด(๋ฒ„๋ฆด๊ฒŒ ์žˆ๋‹ค๋ฉด) ๋ฒ„๋ฆฌ๋Š” ๊ตฌ์กฐ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž˜ ์ž‘๋™ํ•ด์š”.



  • ์ด๋ฒˆ์—” dispose bag์ด๋ผ๋Š” ๊ฐœ๋…์„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ์š”. ๋ฐ”๋กœ ์œ„์—์„œ ํ–ˆ๋˜ ์ž‘์—…์ฒ˜๋Ÿผ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ฒ„๋ฆฌ๊ณ  ์‹ถ์€๋ฐ Disposable ํ•œ ๊ฐœ์”ฉ ํ”„๋กœํผํ‹ฐ์— ์ €์žฅํ•ด์„œ ๋ฒ„๋ฆฌ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ Disposable์„ ๋‹ด์•„๋‘๋Š” ๊ฐ€๋ฐฉ์˜ ๊ฐœ๋…์ด์—์š”! ์ ์ ˆํ•œ ๋„ค์ด๋ฐ์˜ ์˜ˆ์‹œ ๊ฐ™๋„ค์š” :]

    ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‚ด์ง ๋ฐ”๊ฟ”๋ณผ๊ฒŒ์š”. ์šฐ์„  ํ”„๋กœํผํ‹ฐ disposable์„ disposeBag์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๊ณ , disposable์„ ๋งŒ๋“ค ๋•Œ ๊ฐ€๋ฐฉ์— ๋‹ด์•„๋‘˜๊ฑฐ์—์š”.


     @IBAction func onLoadImage(_ sender: Any) {
            imageView.image = nil
                    // ๋ณ€๊ฒฝ
            let disposable = rxswiftLoadImage(from: LARGER_IMAGE_URL)
                   ...
                       })
            disposeBag.insert(disposable)
    
    @IBAction func onCancel(_ sender: Any) {
            self.disposable?.dispose() // ์—๋Ÿฌ ๋ฐœ์ƒ
        }

    ์ด๋ ‡๊ฒŒ ํ•˜๊ณ  ๋์ด๋ฉด ํŽธํ•˜๊ฒ ์ง€๋งŒ.. disposable๊ณผ ๋‹ค๋ฅด๊ฒŒ dispose bag์€ dispose()๋ผ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ์—†์–ด์š”. ๊ทธ๋ž˜์„œ dispose bag์„ ๋น„์›Œ์ฃผ๋Š” ๊ฑด ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด์•ผ๋˜์š”.


    @IBAction func onCancel(_ sender: Any) {
            self.disposeBag = DisposeBag()
        }

    ์—ฌ๊ธฐ์„œ ํ•œ๋‹จ๊ณ„ ๋” ๋‚˜์•„๊ฐ€์„œ, disposable์„ ๋‹ด์„ ๋•Œ ์ƒ์ˆ˜์— ๋‹ด์•„์„œ insertํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฐ”๋กœ disposed๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.


    @IBAction func onLoadImage(_ sender: Any) {
            imageView.image = nil
    
            rxswiftLoadImage(from: LARGER_IMAGE_URL)
                .observeOn(MainScheduler.instance)
                .subscribe({ result in
                                    ...
                    }
                }).disposed(by: self.disposeBag)
        }

    ๊ฐœ์ธ์ ์œผ๋กœ๋Š” ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ๋” ์ง๊ด€์ ์ด์ง€๋งŒ ์•„๋ž˜ ๋ฐฉ๋ฒ•์ด ๋” ๊ฐ„๊ฒฐํ•œ ๊ฒƒ ๊ฐ™์•„์š”.

    2ํŽธ - https://jayb-log.tistory.com/276 ์—์„œ๋Š” Operators ์ด์•ผ๊ธฐ๋กœ ๋„˜์–ด๊ฐ€๋ณผ๊ฒŒ์š”!


ReactiveX

RxSwift by ๊ณฐํŠ€๊น€

๋ฐ˜์‘ํ˜•
๋Œ“๊ธ€
๋ฐ˜์‘ํ˜•
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
TAG more
ยซ   2024/11   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
๊ธ€ ๋ณด๊ด€ํ•จ