iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

[RxSwift] RxSwift ์ž…๋ฌธํ•˜๊ธฐ

๋ˆ„๋ฆฌ๋‹ฌ์ดํ‹€ 2021. 3. 30. 23:15

์‹œ๊ฐ„์ด ๊ฐˆ์ˆ˜๋ก ์ ์  RxSwift๋Š” ๊ผญ ์•Œ์•„์•ผ ํ•˜๋Š” ํŠธ๋ Œ๋“œ์ฃ ~~?ใ…Žใ…Ž

Apple์—์„œ๋„ Rx+MVVM Base์ธ SwiftUI๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ์ ์  ์ง€ํ–ฅํ•˜๋Š” ๊ฒƒ ๊ฐ™๊ณ ์š” ใ…Žใ…Ž (๊ฐœ์ธ์ ์ธ ์ƒ๊ฐใ…‹)

์—…๋ฌด๋ฅผ ํ•˜๋ฉด์„œ๋Š” ์ ‘ํ•ด๋ณผ ๊ธฐํšŒ๊ฐ€ ์—†์–ด์„œ, ํ˜ผ์ž ๋”ฐ๋กœ๋ผ๋„ ํ•œ๋ฒˆ ๊ณต๋ถ€ํ•˜๋ฉด์„œ "์ ์–ด๋„ ๊ฐœ๋…์ •๋„๋Š” ์•Œ๊ณ  ๊ธฐ๋ณธ์ •๋„๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ๊ฒ ๋‹ค" ํ•ด์„œ ์‹œ์ž‘ํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”!! ๐Ÿ˜Ž

 

๊ทธ๋งŒํผ ์™•์ดˆ๋ณด๋ฅผ ์œ„ํ•œ ์ž…๋ฌธ์šฉ ํฌ์ŠคํŒ…์ด ๋˜๊ฒ ์ง€๋งŒ, ํ•ต์‹ฌ ๊ฐœ๋…์ด๋‚˜ ์›๋ฆฌ๋Š” ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์ƒ์„ธํžˆ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด๋ผ ์กฐ๊ธˆ์€ ๊ธด ํฌ์ŠคํŒ…์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!! ๐Ÿ˜ญ

๊ทธ์น˜๋งŒ! ์ด ํฌ์ŠคํŒ…์„ ์ฒœ์ฒœํžˆ๋ผ๋„ ์™„์ฃผํ•˜์‹ ๋‹ค๋ฉด RxSwift์— ๋Œ€ํ•œ ๊ฐœ๋…์ด๋‚˜ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์€ ์ตํžˆ์‹ค ์ˆ˜ ์žˆ์„๊ฑฐ์—์š” ๐Ÿง‘‍๐Ÿ’ป

 


RxSwift์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ๊ธฐ๋ณธ์ ์ธ Swift์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์จ๋ณผ๊ฑฐ์—์š”.

๋จผ์ € ํ•œ๊ฐ€์ง€ ์ƒ˜ํ”Œ์„ ์†Œ๊ฐœ๋“œ๋ฆฌ๋ฉด์„œ ์‹œ์ž‘ํ•ด๋ณผ๊ฒŒ์š”!

์ด๋ ‡๊ฒŒ ์ƒ๋‹จ์— ํƒ€์ด๋จธ๊ฐ€ ํ˜๋Ÿฌ๊ฐ€๊ณ , LoadJson ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ Json์„ ๋‚ด๋ ค๋ฐ›๊ณ  ์žˆ์–ด์š”.

์ด๋•Œ ๋‹ค์šด๋ฐ›๋Š” ๋™์•ˆ ํƒ€์ด๋จธ๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ , User Interaction์— ๋ฐ˜์‘ํ•˜๊ธฐ์œ„ํ•ด, ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ Json์„ ๋‚ด๋ ค๋ฐ›๋Š” Task๋Š” background์—์„œ ์ผ์–ด๋‚˜๊ณ , UI์™€ ๊ด€๋ จ๋œ Task๋Š” ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•˜๊ฒŒ๋” ๋น„๋™๊ธฐ์ ์œผ๋กœ ์„ค๊ณ„ํ•˜์ฃ ~?

func downloadJson(urlString: String, completion: @escaping (String) -> Void) {
        guard let url = URL(string: urlString) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard data != nil, error == nil else { return }
            
            guard let text = String(data: data!, encoding: .utf8) else { return }
            completion(text)
            
        }.resume()
    }

์•„๋งˆ ๋Œ€๋ถ€๋ถ„ ์ด๋Ÿฐ์‹์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด completion ํด๋กœ์ € ํ˜น์€ Delegate, Notification ๋“ฑ์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ , ์ „๋‹ฌ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋กœ UI ๋ณ€๊ฒฝ๊ณผ ๊ด€๋ จ๋œ ๋กœ์ง์„ ์‹คํ–‰ํ• ๊ฑฐ์—์š”.

downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY) { text in
            DispatchQueue.main.async {
                self.contentsTextView.text = text
                self.setVisibleWithAnimation(status: false)
            }
        }

 

 

์ด๋ ‡๊ฒŒ ๋ฐ์ดํ„ฐ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ์ธ์ง€ํ•˜๊ณ  ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ completion๊ฐ™์€ ํด๋กœ์ €๋ฅผ ํ†ตํ•ด ๋ฐ›์€ ๋’ค, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” Task๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ณผ์ •์„ ํ†ตํ•ด์„œ UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์•ผํ•˜์ฃ .

์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ , ์ „๋‹ฌ๋ฐ›๊ณ , UI๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ผ๋ จ์˜ ๊ณผ์ •์„ ๋”ฐ๋กœ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ , ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ "๊ด€์ฐฐ" ํ•˜๊ณ  ์žˆ๋Š”๋ฐ ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ "๋ณ€๊ฒฝ" ๋ ๋•Œ ์•Œ์•„์„œ UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์ฃผ๋ฉด ๋˜์ง€ ์•Š์„๊นŒ?


๋ฐ”๋กœ ์ด๋Ÿฐ ์ƒ๊ฐ์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์ด Reactive Programming(๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ) ์ด์—์š”. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฑธ Swift์—์„œ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ด์ฃผ๋Š”๊ฒŒ RxSwift์—์š”. ์ฆ‰ ๋น„๋™๊ธฐ๋กœ ์ƒ๊ธฐ๋Š” ๊ฒฐ๊ณผ๊ฐ’์„ completion ํด๋กœ์ €๋“ฑ์„ ํ†ตํ•ด ์ „๋‹ฌํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ์–ด๋–ค ๋ฐ์ดํ„ฐ์— ๊ด€์‹ฌ์„ ๊ฐ–๊ณ  ๊ด€์ฐฐ(Observe)ํ•˜๊ฒŒ๋”๋งŒ ํ•ด๋†“์œผ๋ฉด ์•Œ์•„์„œ ํ•ด์ค€๋‹ค๋Š”๊ฑฐ์ฃ 

 

์—ฌ๊ธฐ ์ €๊ธฐ๋ฅผ ์ฐพ์•„๋ด๋„, Rx์˜ ์ •์˜์— ๋Œ€ํ•ด์„œ๋Š” ๋Œ€๋ถ€๋ถ„ ๋น„์Šทํ• ๊ฑฐ์—์š”.

"๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ์‹ ๋ฐ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋žจ์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ"

 

์‘~ ๊ทผ๋ฐ ๊ตณ์ด RxSwift๋ฅผ ์จ์•ผ๋ผ~? ๊ทธ๋ƒฅ Swift๋กœ๋„ didSet์„ ์ด์šฉํ•˜๊ฑฐ๋‚˜ ๋“ฑ๋“ฑ ํ™œ์šฉํ•˜๋ฉด ๋˜์ง€์•Š์•„~?

๋งž์Šต๋‹ˆ๋‹ค!!  RxSwift ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ถฉ๋ถ„ํžˆ Reactiveํ•˜๊ฒŒ๋” ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ์–ด์š”.

 

๊ทธ๋Ÿฐ๋ฐ ์šฐ๋ฆฌ๊ฐ€ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์œ„ํ•ด Alamofire๊ฐ™์€ 3rd-party ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ฃ ~? ์™œ์‚ฌ์šฉํ•ด์š”~?

๋‹ต์€ ๋‹น์—ฐํžˆ "์‚ฌ์šฉํ•˜๊ธฐ ํŽธํ•˜๋‹ˆ๊นŒ" ์ฃ ! URLSession์œผ๋กœ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๊ตฌํ˜„ํ•ด๋„ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ์น˜๋งŒ ๋ˆ„๊ตฐ๊ฐ€ Alamofire๋ฅผ ๋งŒ๋“ค๊ณ , ๋„คํŠธ์›Œํฌ ํ†ต์‹ ํ• ๋•Œ ์“ฐ๊ธฐ ํŽธํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ๋†“์•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ๊ฑธ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ ๋ฟ์ด์ฃ !

 

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋ฐ”๋กœ Reactive eXtension Swift (RxSwift)๊ฐ€ Reactive ํ•˜๊ฒŒ Swift ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์š”.

๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฐœ๋…๊ณผ ์šฉ๋„๋ฅผ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด์•˜๋Š”๋ฐ์š”! ๊ทธ๋Ÿผ ์ด์ œ RxSwift์— ๋Œ€ํ•ด ํ•œ๋ฒˆ ์•Œ์•„๋ณผ๊นŒ์š”~?๐Ÿง‘‍๐Ÿ’ป

 

github.com/ReactiveX/RxSwift

 

ReactiveX/RxSwift

Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

github.com

 

๋จผ์ € RxSwift์˜ ํ•ต์‹ฌ ๊ฐœ๋… 3๊ฐ€์ง€์™€, ๊ฐ๊ฐ์˜ ์‚ฌ์šฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฒŒ์š”.

 

Observable

  • ์ด๋ฒคํŠธ๋ฅผ ์‹œ๊ฐ„ ํ๋ฆ„์— ๋”ฐ๋ผ ์ „๋‹ฌํ•˜๋Š” "์ „๋‹ฌ์ž"
  • ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๋Š” ์ผ๋ จ์˜ ํ•ญ๋ชฉ๋“ค์„ ๋‚˜ํƒ€๋‚ด๋Š” "์‹œํ€€์Šค"
  • Observable์€ 3๊ฐ€์ง€ ํƒ€์ž…์˜ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜๊ณ , Observer๊ฐ€ Observable์„ "๊ตฌ๋…"ํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹  ํ•ฉ๋‹ˆ๋‹ค.
  • next : next๋Š” ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์ถœํ•˜๋Š” ์ด๋ฒคํŠธ, ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ Observer๊ฐ€ ์ˆ˜์‹  (Emission)
  • completed : completed๋Š” ์‹œํ€€์Šค๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์ข…๋ฃŒํ•˜๊ณ  ๋”์ด์ƒ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜์ง€ ์•Š์Œ (Notification)
  • error : error๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์‹œํ€€์Šค๋ฅผ ์ข…๋ฃŒํ•˜๊ณ , ๋”์ด์ƒ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜์ง€ ์•Š์Œ (Notification)

์•„๊นŒ ์œ„์˜ ์˜ˆ์‹œ์—์„œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ "๊ด€์ฐฐ" ํ•œ๋‹ค๊ณ  ํ–ˆ์ฃ ~? ๋ง ๊ทธ๋Œ€๋กœ observable ์€ "๊ด€์ฐฐ๊ฐ€๋Šฅํ•œ" ์นœ๊ตฌ์—์š”. 

์œ„์—์„œ ๋“ค์—ˆ๋˜ ์˜ˆ์‹œ๋ฅผ ๋˜‘๊ฐ™์ด Observable์„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ์š”.

func downloadJson(urlString: String) -> Observable<String?> {
        // ๋น„๋™๊ธฐ๋กœ ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ Observable๋กœ ๊ฐ์‹ธ์„œ ๋ฆฌํ„ด
        return Observable.create { emitter in
            let url = URL(string: urlString)
            let task = URLSession.shared.dataTask(with: url!) { data, response, error in
                guard error == nil else {
                    emitter.onError(error!)
                    return
                }
                guard data != nil else { return }
                guard let text = String(data: data!, encoding: .utf8) else { return }

                // ์‹ค์ œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
                emitter.onNext(text)
                // ์™„๋ฃŒ ์ฒ˜๋ฆฌ
                emitter.onCompleted()
            }
            task.resume()
            // ์ทจ์†Œ๋˜์—ˆ์„๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊ฑด์ง€ ์ „๋‹ฌ
            return Disposables.create() {
                task.cancel()
            }
        }
    }

์ด๋ ‡๊ฒŒ Observableํ•œ ์–ด๋–ค ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ Observable.create() ๋ฉ”์†Œ๋“œ๋กœ ํ•˜๋‚˜์˜ Observable์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. ์‹ค์ œ๋กœ ์ •์˜๋ฅผ ๋ณด๋ฉด ์–ด๋–ค Observable์„ ๋ฆฌํ„ดํ•˜๋„๋ก ๋˜์–ด์žˆ๋„ค์š”!

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, ๋ฐฉ๊ธˆ ์„ค๋ช…ํ–ˆ๋˜ 3๊ฐ€์ง€ ์ด๋ฒคํŠธ๊ฐ€ ๋ชจ๋‘ ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ ๋ณด์ด์‹œ๋‚˜์š”~? 

๋จผ์ € error๊ฐ€ ๋ฐœ์ƒํ–ˆ์œผ๋ฉด onError ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•ด์š”! ํ˜น์€ ์ •์ƒ์ ์œผ๋กœ ๋ญ”๊ฐ€ ์ฒ˜๋ฆฌ๋˜์—ˆ๋‹ค๋ฉด, onNext ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์ถœํ•˜๊ณ , ์ด ํ›„์—๋Š” onCompleted ์ด๋ฒคํŠธ๋กœ Observable์˜ ์ด๋ฒคํŠธ๋ฅผ ์™„๋ฃŒ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์ •์˜๋ฅผ ๋ณด๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด๊ฐ€๋Š” ํด๋กœ์ €๊ฐ€ Disposable์ด๋ผ๋Š” ๊ฑธ ๋ฆฌํ„ดํ•˜๊ฒŒ๋” ๋˜์–ด์žˆ์ฃ ?

์ด๊ฒŒ ๋ฐ”๋กœ return Disposables.create() ์—์š”. ์ด๊ฑด ๋งŒ์•ฝ์— ์ด Observable์ด ์ทจ์†Œ๋˜์—ˆ์„๋•Œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊ฑด์ง€ ์ „๋‹ฌํ•˜๋Š”๊ฑฐ์—์š”. ์ฆ‰, ์—ฌ๊ธฐ์„œ๋Š” ๋งŒ์•ฝ ์ทจ์†Œ๋˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ–ˆ๋˜ Task๋ฅผ ์ทจ์†Œํ•˜๋ผ๊ณ  ํ•ด์ค€ ๊ฑฐ์—์š”!

 

์ด์ œ Observable(๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ) ๋Œ€์ƒ์„ ๋งŒ๋“ค์–ด ์คฌ์œผ๋‹ˆ, ์ด ๋ฐ์ดํ„ฐ๋ฅผ "๊ด€์ฐฐ" ํ•ด์•ผ๊ฒ ์ฃ ~? ๊ทธ๊ฑธ RxSwift์—์„œ๋Š” "๊ตฌ๋…"ํ•œ๋‹ค๊ณ  ํ•˜๊ณ , subscribeํ•œ๋‹ค๊ณ  ํ•ด์š”.

downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .debug()
            .subscribe { event in
                switch event {
                case .next(let text):
                    DispatchQueue.main.async {
                        self.contentsTextView.text = text
                        self.setVisibleWithAnimation(status: false)
                    }
                case .completed:
                    break
                case .error:
                    break
                }
            }

์ด๋ ‡๊ฒŒ ์œ„์—์„œ ๋งŒ๋“  downloadJson Observable์„ subscribe(๊ตฌ๋…) ํ•ด์ฃผ๋Š”๊ฑฐ์—์š”.

๊ทธ๋Ÿฌ๋ฉด ์œ„์—์„œ ์˜ต์ €๋ฒ„๋ธ”์ด ๋ฐฐ์ถœํ•œ onNext, onCompleted, onError ์ด๋ฒคํŠธ๋ฅผ ์—ฌ๊ธฐ์„œ ์ˆ˜์‹ ํ•˜๊ฒŒ ๋˜์š”.

์—ฌ๊ธฐ์„œ, subscribe ํด๋กœ์ €๋ฅผ ๋ณด๋ฉด self.~ํ•˜๊ณ  ํด๋กœ์ € ๋‚ด์—์„œ ์ž๊ธฐ์ฐธ์กฐ๋ฅผ ํ•˜๊ณ  ์žˆ์ฃ !? ๋ฐ”๋กœ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํฌ์ธํŠธ์—์š”. ์‚ฌ์‹ค ์šฐ๋ฆฌ๋Š” [weak self] ๋ฅผ ํ†ตํ•ด์„œ ์ž๊ธฐ์ฐธ์กฐ๋กœ ์ธํ•œ ์ˆœํ™˜์ฐธ์กฐ๋ฅผ ๋ง‰์•„์ฃผ๋Š”๋ฐ์š”~!

 

์—ฌ๊ธฐ์„œ๋Š” ์ € subscribe ํด๋กœ์ €๊ฐ€ complete ์ดํ›„์— ์ข…๋ฃŒ๋˜๋ฉด ํด๋กœ์ €๊ฐ€ ์‚ญ์ œ๋˜๊ณ , ํด๋กœ์ €๊ฐ€ ์‚ญ์ œ๋˜๋ฉด ์ž๊ธฐ์ฐธ์กฐ ํ•˜๋˜ reference count๋„ ๊ฐ™์ด ์ค„์–ด๋“ค๊ธฐ ๋•Œ๋ฌธ์—, ๋”ฐ๋กœ [weak self]๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค๊ณ  ํ•ด์š”. (๊ทผ๋ฐ ์ €๋Š” ์“ธ๋“ฏ.. ๐Ÿ˜‚)

์ด ์˜ต์ €๋ฒ„๊ฐ€ ์–ด๋–ค ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•˜๊ณ , ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•˜๋Š”์ง€๋Š” .debug() ๋ฉ”์†Œ๋“œ๋ฅผ ์จ์ฃผ๋ฉด ์ฝ˜์†”์—์„œ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

 

์•„๊นŒ Observable.create()์˜ ์ •์˜๋ฅผ ๋ดค์„๋•Œ, ํด๋กœ์ €๋Š” disposable์„ ๋ฆฌํ„ดํ•˜๋„๋ก ๋˜์–ด์žˆ์—ˆ์ฃ ?

let disposable = downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .subscribe { event in
            ...
            }
        disposable.dispose()

์ด๋ ‡๊ฒŒ๋„ ์“ธ ์ˆ˜ ์žˆ์„๊ฑฐ์—์š”. dispose() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด error, complete ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์ „์—๋„ ๊ตฌ๋…์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

์ด disposable์— ๋Œ€ํ•ด ์กฐ๊ธˆ๋งŒ ๋” ์•Œ์•„๋ณผ๊ฒŒ์š”.

var disposables: [Disposable] = []

let disposable = downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .subscribe {
            ...
            }
        disposables.append(disposable)

๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Disposable ๋ฐฐ์—ด์„ ๋งŒ๋“ค๊ณ , ๋ญ”๊ฐ€ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์ง„ํ–‰์ค‘์ด์ง€๋งŒ ์ด ํ™”๋ฉด์„ ๋ฒ—์–ด๋‚ ๋•Œ๋Š” ๋‹ค์šด๋กœ๋“œ๋ฅผ ๋ฉˆ์ถ”๊ฒŒ ํ•œ๋‹ค๋Š” ๋“ฑ์˜ ์•ก์…˜์„ ํ• ๋•Œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”!

override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        disposables.forEach { $0.dispose() }
    }

๊ทผ๋ฐ ์ด๊ฑด ์•ฝ~๊ฐ„ ๊ท€์ฐฎ์ฃ ??ใ…Žใ…Ž ๊ทธ๋ž˜์„œ RxSwift์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” DisposeBag ์ด๋ผ๋Š” ์นœ๊ตฌ๊ฐ€ ์žˆ์–ด์š”.

๋ง๊ทธ๋Œ€๋กœ Disposable ๋“ค์„ ๋‹ด๊ณ ์žˆ๋Š” ๊ฐ€๋ฐฉ์ด์—์š”. ์ด๋Ÿฐ ์นœ๊ตฌ๋“ค์„ Sugar API๋˜๋Š” Operator๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์•„๋ž˜์—์„œ ์ƒ์„ธํžˆ ๋‹ค๋ฃฐ๊ฒŒ์š”!

์ด์นœ๊ตฌ๋Š” viewWillDisappear์—์„œ ๋”ฐ๋กœ disposeํ•ด์ฃผ์ง€ ์•Š์•„๋„, ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์˜ ์ง€์—ญ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด๋†“์œผ๋ฉด ์ด ํ™”๋ฉด์ด ์—†์–ด์งˆ๋•Œ ์ž๋™์ ์œผ๋กœ ๊ฐ™์ด ์—†์–ด์ ธ์„œ ๋”ฐ๋กœ dispose๋ฅผ ํ˜ธ์ถ”ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋˜์š”.

 var disposeBag = DisposeBag()

๊ทธ๋ฆฌ๊ณ  ์˜ต์ €๋ฒ„๋ธ”์ด ์ƒ๊ธธ๋•Œ append ๋Œ€์‹  insert ๋ฉ”์†Œ๋“œ๋กœ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ์–ด์š”.

disposeBag.insert(disposable)

์‘.. ๊ทผ๋ฐ ๊ทธ๋Ÿผ ์˜ต์ €๋ฒ„๋ธ”์„ ๋งŒ๋“ค๋•Œ๋งˆ๋‹ค disposeBag์—๋‹ค๊ฐ€ insertํ•ด์ค˜์•ผ๋ผ??

์•„.๋‹ˆ.์š”.! ์ด๋ ‡๊ฒŒ disposed() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์˜ต์ €๋ฒ„๋ธ”์„ ์ƒ๊ธธ๋•Œ๋งˆ๋‹ค ๊ทธ๋ƒฅ ์–ด๋–ค disposeBag์—์„œ ๊ด€๋ฆฌํ• ๊ฑด์ง€๋งŒ ์•Œ๋ ค์ฃผ๋ฉด ๋˜์š”!

let disposable = downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .subscribe ({
            ...
            })
            .disposed(by: disposeBag)

 

 

๊ทธ๋Ÿผ, Observable์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ์ด๋ ‡๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•„์š”.

  1. Create
  2. Subscribe
  3. onNext
  4. onCompleted / onError
  5. Disposed

์ด๋ ‡๊ฒŒ ๋จผ์ € ์ฒซ๋ฒˆ์งธ ํ‚ค์›Œ๋“œ์ธ Observable์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์•˜๋Š”๋ฐ์š”~! ์–ด๋–ค๊ฑด์ง€ ์ข€ ๋А๋‚Œ์ด ์˜ค์‹œ๋‚˜์š”~?

์š”์•…ํ•˜๋ฉด ๋”ฑ ์ด๊ฑฐ์—์š”.

Observable(๊ด€์ฐฐ๋Œ€์ƒ)์„ ๋งŒ๋“ค๊ณ , Observer๋Š” ๊ทธ ๊ด€์ฐฐ๋Œ€์ƒ์„ subscribe(๊ตฌ๋…) ํ•ฉ๋‹ˆ๋‹ค. Observable์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜๋ฉด, Observer๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•ด์„œ ์–ด๋–ค ์•ก์…˜์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

๊ทธ๋Ÿฐ๋ฐ.. ๋ญ”๊ฐ€ ๊ต‰์žฅํžˆ ์‰ฝ๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ์†Œ๊ฐœํ•ด๋†“๊ณ , ์‚ฌ์‹ค์€ ๋˜‘๊ฐ™์ด ๋ณต์žกํ•˜๋„ค? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“œ์‹ค๊ฑฐ์—์š”.

์‚๋น…- ์ •์ƒ์ž…๋‹ˆ๋‹ค. ๐Ÿง‘‍๐Ÿ’ป

 


์‚ฌ์‹ค์€ ์ง€๊ธˆ๊นŒ์ง€ Observable์˜ ์›๋ฆฌ๋ฅผ ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด์„œ ํ•œ๋ฒˆ ์•Œ์•„๋ณธ๊ฑฐ๊ณ , ์ง€๊ธˆ๋ถ€ํ„ฐ๋Š” Observable์„ ์กฐ๊ธˆ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฑฐ์—์š”!

 

์˜ˆ๋ฅผ ๋“ค์–ด์„œ "Hello World"๋ผ๋Š” ๋ฌธ์ž์—ด์„ ์ด๋ฒคํŠธ๋กœ ๋ณด๋‚ด๋Š” Observable์ด ์žˆ์–ด์š”.

func something() -> Observable<String?> {
        return Observable.create { emitter in
            emitter.onNext("Hello World")
            emitter.onCompleted()
            return Disposables.create()
        }
    }

์ง€๊ธˆ๊นŒ์ง€ ๋Œ€๋กœ๋ผ๋ฉด.. ์ด ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด์„ ํ•˜๋‚˜ ๋ณด๋‚ด๋Š”๋ฐ ์ด๋Ÿฐ ๋งŽ์€ ๊ณผ์ •์ด ํ•„์š”ํ•˜์ฃ ~~? ๐Ÿ˜‚ ๊ทธ๋Ÿผ ๊ตณ์ด ์™œ์จ...

๊ทธ๋ž˜์„œ~!! RxSwift์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” Operator(Sugar API) ์˜ ๊ฐœ๋…์ด ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค.

func something() -> Observable<String?> {
        return Observable.just("Hello World")
    }

just๋Š” ์ •๋ง ๋‹จ์ˆœํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์ถœํ• ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Operator์—์š”. ๋‹จ, ํ•˜๋‚˜๋งŒ ๋ฐฐ์ถœํ•  ์ˆ˜ ์žˆ์–ด์š”!

๊ทธ๋Ÿผ ๋‘๊ฐœ ์ด์ƒ์€ ์–ด๋–ป๊ฒŒ ํ•˜๋ƒ~~?

func something() -> Observable<[String?]> {
        return Observable.just(["Hello World", "Hi World"])
    }

์ด๋ ‡๊ฒŒ! just๋ฅผ ๋˜‘๊ฐ™์ด ์‚ฌ์šฉํ•˜๋˜ [ ] ๋ฐฐ์—ด๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋ฒ„๋ฆด ์ˆ˜๋„ ์žˆ์–ด์š” ใ…‹.ใ…‹ ๐Ÿ‘

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ํ•ด๋ฒ„๋ฆฌ๋ฉด, ๋ฐฐ์—ด ์ „์ฒด๋ฅผ ํ•œ๋ฒˆ์— ๋ฐ›๋Š” ๋ฐฉ๋ฒ•๋ฐ–์—๋Š” ์—†์–ด์š”.

๋งŒ์•ฝ ๋ฐฐ์—ด ์•ˆ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•˜๋‚˜์”ฉ ๋ฐ›๊ณ  ์‹ถ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”~? ์ด๋Ÿด๋•Œ from์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์–ด์š”.

func something() -> Observable<String?> {
        return Observable.from(["Hello World", "Hi World"])
    }

์ด๋Ÿฌ๋ฉด ๋ฐฐ์—ด ์•ˆ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•˜๋‚˜์”ฉ ์ด๋ฒคํŠธ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐฐ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Œ~~ ๊ทผ๋ฐ ๋ฐฐ์—ด์€ ์‹ซ์€๋ฐ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ด๋ฒคํŠธ๋กœ ๋ณด๋‚ด๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋Š”๋ฐ~~?

์ด๋•Œ! of ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

func something() -> Observable<String?> {
        return Observable.of("Hello World", "Hi World")
    }

 

์š”์•ฝ

  • ํ•˜๋‚˜์˜ ์š”์†Œ๋ฅผ ๋ฐฐ์ถœํ•˜๋Š” ์˜ต์ €๋ฒ„๋ธ” ์ƒ์„ฑ์‹œ์—๋Š” just
  • ๋‘˜ ์ด์ƒ์˜ ์š”์†Œ๋ฅผ ๋ฐฐ์ถœํ•˜๋Š” ์˜ต์ €๋ฒ„๋ธ” ์ƒ์„ฑ์‹œ์—๋Š” of
  • just์™€ of๋Š” ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐฐ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜๋ฉด ๋ฐฐ์—ด์„ ๊ทธ๋Œ€๋กœ ๋ฐฐ์ถœ
  • ๋ฐฐ์—ด์— ์ €์žฅ๋œ ์š”์†Œ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐฐ์ถœํ•˜๋Š” ์˜ต์ €๋ฒ„๋ธ” ์ƒ์„ฑ์‹œ์—๋Š” from

 

๊ทธ๋Ÿผ subscribeํ• ๋•Œ๋Š” ์ด๋Ÿฐ๊ฒŒ ์—†์„๊นŒ์š”~? ๋ฌผ๋ก  ์žˆ์Šต๋‹ˆ๋‹ค!!

func getSomething() {
        something()
            .subscribe(onNext: { print($0) },
                       onError: { err in print(err) },
                       onCompleted: { print("completed") } )
    }

์ด๋ ‡๊ฒŒ subscribeํ• ๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ onNext, onError, onCompleted, onDisposed๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”! 

๋ชจ๋‘ ์˜ต์…”๋„๋กœ, ์„ ํƒ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด์„œ๋งŒ ๊ตฌ๋…ํ•˜๊ฒŒ๋” ๊ฐ€๋Šฅํ•ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ onNext ์ด๋ฒคํŠธ๋งŒ ๋ฐ›์•„์„œ ๋ญ”๊ฐ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด, ์ด๋ ‡๊ฒŒ ํ•œ์ค„์ด๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ต๋‹ˆ๋‹ค ๐Ÿ‘

func getSomething() {
        something()
            .subscribe(onNext: { print($0) })
    }

 

๊ทธ๋Ÿผ ์œ„์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์˜ˆ์‹œ๋„ Sugar API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ~?

@IBAction func loadTapped(_ sender: Any) {
        setVisibleWithAnimation(status: true)
        downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .subscribe(onNext: { text in
                DispatchQueue.main.async {
                    self.contentsTextView.text = text
                    self.setVisibleWithAnimation(status: false)
                }
            })
    }

 

์ด ์™ธ์—๋„ ๋‹ค์–‘ํ•œ Operator(Sugar API)๊ฐ€ ์กด์žฌํ•˜๊ณ , ์ƒ์„ธ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

Operator ์ค‘์—๋„ Observable์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ์ƒ์„ฑ Operator, Observable ๋ฐฐ์ถœํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜๋Š” Operator, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ subscribe(๊ตฌ๋…) ํ•˜๋Š” Operator ๋“ฑ๋“ฑ ์žˆ์–ด์š”. ์ด Operator๋Š” RxSwift์˜ ํ•ต์‹ฌ ๊ฐœ๋… 3๊ฐ€์ง€์ค‘ ํ•˜๋‚˜์—์š”! ๐Ÿ˜Ž ๊ทธ๋Ÿผ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•ด์„œ ํ•œ๋ฒˆ ์•Œ์•„๋ณด๋„๋ก ํ• ๊ฒŒ์š”!

 

 

Operator(์—ฐ์‚ฐ์ž)

์—ฐ์‚ฐ์ž์˜ ์ข…๋ฅ˜๋Š” ๋ชฉ์ ์— ๋”ฐ๋ผ ์ƒ์„ฑ, ๋ฐ์ดํ„ฐ ๋ณ€ํ˜•, ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ, ์—๋Ÿฌํ•ธ๋“ค๋ง, ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋“ฑ ๋ถ„๋ฅ˜๋กœ ๋‚˜๋ˆ ์ ธ์š”.

์—ฐ์‚ฐ์ž๋Š” ๋Œ€ํ‘œ์ ์œผ๋กœ observe(on:), Map, Filter, CombineLatest ๋“ฑ์ด ์žˆ์–ด์š”!

observe(On:) observOn์ด ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋„ค์š”. ์€ ์–ด๋–ค ์Šค์ผ€์ฅด๋Ÿฌ(์Šค๋ ˆ๋“œ)์—์„œ ๊ด€์ฐฐํ• ๊ฑด์ง€๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์—ฌ๊ธฐ์„œ ๋‚˜์˜จ Scheduler๋Š” ์žˆ๋‹ค๊ฐ€ ์ข€ ๋” ์ž์„ธํ•˜๊ฒŒ ์•Œ์•„๋ณผ๊ฒŒ์š”!

์˜ˆ๋ฅผ ๋“ค๋ฉด ๋ฐฉ๊ธˆ ์œ„์—์„œ ๋ดค๋˜ ์ฝ”๋“œ์—์„œ ์šฐ๋ฆฌ๊ฐ€ DispatchQueue.main.async ์ด๋ ‡๊ฒŒ ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ํ•ด์ฃผ๋˜๊ฑธ, ๊ทธ๋ƒฅ observe(on:) ๋ฉ”์†Œ๋“œ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”.

@IBAction func loadTapped(_ sender: Any) {
        setVisibleWithAnimation(status: true)
        downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { text in
                self.contentsTextView.text = text
                self.setVisibleWithAnimation(status: false)
            })
    }

 

map, filter๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•Œ๋˜ ๊ณ ์ฐจํ•จ์ˆ˜์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•ด์š”. ์ด 2๊ฐ€์ง€์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์€ ํŒจ์Šคํ• ๊ฒŒ์š”! 

ํ˜น์‹œ๋ผ๋„ ์ž˜ ๋ชจ๋ฅด์‹œ๋ฉด ์ด ํฌ์ŠคํŒ…์„ ํ•œ๋ฒˆ ๋ด์ฃผ์„ธ์š”~!

downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
            .map { text in text?.count ?? 0 }
            .filter { cnt in cnt > 0 }
            .map { "\($0)"}
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { text in
                self.contentsTextView.text = text
                self.setVisibleWithAnimation(status: false)
            })

์ด๋Ÿฐ์‹์œผ๋กœ ์˜ต์ €๋ฒ„๋ธ”์ด ๋ฐฐ์ถœํ•œ ๋ฐ์ดํ„ฐ๋ฅผ map, filter๋ฅผ ํ†ตํ•ด ๊ฐ€๊ณตํ•˜๊ณ  subscribeํ•˜๊ฒŒ๋” ํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

์ด๋ฒˆ์—๋Š” ๋ณ‘ํ•ฉ ์—ฐ์‚ฐ์ž์ค‘์— ํ•˜๋‚˜์ธ zip์— ๋Œ€ํ•ด์„œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”?

๋ณ‘ํ•ฉ์€ ๋ง๊ทธ๋Œ€๋กœ ์—ฌ๋Ÿฌ๊ฐœ์˜ ์˜ต์ €๋ฒ„๋ธ”์„ ํ•ฉ์น˜๋Š”๊ฑฐ์—์š”. Zip์€ ๋‘ ์˜ต์ €๋ฒ„๋ธ”์˜ ์š”์†Œ๋“ค์„ ํ•ฉ์ณ์„œ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋กœ ๋ณ‘ํ•ฉํ•œ ์˜ต์ €๋ฒ„๋ธ”์„ ๋งŒ๋“œ๋Š”๋ฐ์š”~! ์˜ˆ๋ฅผ๋“ค๋ฉด ์ด๋ ‡๊ฒŒ "Hello"๋ผ๋Š” ๋ฐ์ดํ„ฐ์™€ ์šฐ๋ฆฌ๊ฐ€ ์„œ๋ฒ„์—์„œ ๋‚ด๋ ค๋ฐ›์•˜๋˜ Json ๋ฐ์ดํ„ฐ๋ฅผ ํ•ฉ์ณ์„œ ์˜ต์ €๋ฒ„๋ธ”์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜์š”.

let jsonObservable = downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
let helloObservable = Observable.just("Hello")
            
        Observable.zip(jsonObservable, helloObservable) { $1 + "\n" + $0  }
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { text in
                self.contentsTextView.text = text
                self.setVisibleWithAnimation(status: false)
            })

 

์ข€์ „์— ์œ„์—์„œ ์ž ๊น ์†Œ๊ฐœํ•œ ์—ฐ์‚ฐ์ž ์ด ์™ธ์—๋„ ์—„์ฒญ ๋‹ค์–‘ํ•œ ์—ฐ์‚ฐ์ž๋“ค์ด ์žˆ๊ณ , ์—ฌ๊ธฐ์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ–ˆ์ฃ ~?

์ข…๋ฅ˜๋Š” ๋งŽ๊ณ , ๋ญ๊ฐ€ ๋ญ”์ง€ ๋ชจ๋ฅด๊ฒ ๋”๋ผ๊ตฌ์š”!! ๋Œ€์‹  ๊ทธ์ค‘์— ํ•˜๋‚˜๋ฅผ ํ•œ๋ฒˆ ๋ˆŒ๋Ÿฌ๋ณด๋ฉด, ๋ญ”๊ฐ€ Input๊ณผ Output์ด ์žˆ๋Š” ๋‹ค์ด์–ด๊ทธ๋žจ์ด ๋ณด์ผ๊ฑฐ์—์š”. ์ด ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋งˆ๋ธ” ๋‹ค์ด์–ด๊ทธ๋žจ ์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ์š”.

 

์Œ.. ์˜ค์ผ€์ด. ๊ทผ๋ฐ ๋งˆ๋ธ” ๋‹ค์ด์–ด๊ทธ๋žจ ์ด๊ฑฐ ์–ด๋–ป๊ฒŒ ๋ณด๋Š”๊ฑด๋ฐ?

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด์„œ ์กฐ๊ธˆ์”ฉ ์‚ดํŽด๋ณผ๊ฒŒ์š”!! ๋จผ์ € ์ƒ์„ฑ Operator์ค‘ ํ•˜๋‚˜์ธ Just๋ฅผ ๋ณผ๊ฒŒ์š”. 

๋นจ๊ฐ„ ํ…์ŠคํŠธ๋กœ ์–ด๋–ค ๋ชจ์–‘์ด ๋ญ˜ ์˜๋ฏธํ•˜๋Š”์ง€๋ฅผ ํ•œ๋ฒˆ ์ ์–ด๋ดค์–ด์š”!

์ฆ‰, Just ์—ฐ์‚ฐ์ž๋Š” ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  Just ์—ฐ์‚ฐ์ž๋ฅผ ์ ์šฉ์‹œํ‚ค๋ฉด, ์–ด๋–ค Observable(๊ฐ€๋กœํ™”์‚ดํ‘œ)์ด ๋‚˜์˜ค๋Š”๋ฐ, "์ด ์˜ต์ €๋ฒ„๋ธ”์€ Input์— ๋„ฃ์—ˆ๋˜ ๋ฐ์ดํ„ฐ์™€ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฒคํŠธ(next)์— ํฌํ•จ๋˜๊ณ  Completed ์ด๋ฒคํŠธ๊ฐ€ ์žˆ๋‹ค." ๋ผ๊ณ  ํ•ด์„ํ•˜๋ฉด ๋˜๊ฒ ๋„ค์š”!

 

์•„์ง ์•ฝ๊ฐ„ ํ—ท๊ฐˆ๋ฆฌ์‹œ์ฃ ~!? ๋ฐฉ๊ธˆ ๊ฐ™์ด ์‚ดํŽด๋ดค๋˜ From ์—ฐ์‚ฐ์ž๋ฅผ ๋ณผ๊นŒ์š”?

์–ด๋–ค ๋ฐฐ์—ด์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  From ์—ฐ์‚ฐ์ž๋ฅผ ์ ์šฉ์‹œ์ผฐ๋”๋‹ˆ, ์˜ต์ €๋ฒ„๋ธ”์ด ๋‚˜์™”๋„ค์š”. ๊ทผ๋ฐ ์ด ์˜ต์ €๋ฒ„๋ธ”์€ Input์œผ๋กœ ๋„ฃ์—ˆ๋˜ ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ๋“ค์„ ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ(next)๋กœ ํฌํ•จํ•˜๊ณ , Completed์ด๋ฒคํŠธ๋„ ๊ฐ™์ด ์žˆ๋Š” ์˜ต์ €๋ฒ„๋ธ”์ด๋„ค์š”!!

์ด์ œ ์ข€ ์ด ๋งˆ๋ธ” ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์–ด๋–ป๊ฒŒ ๋ด์•ผ ํ•˜๋Š”์ง€ ๋А๋‚Œ ์˜ค์‹œ๋‚˜์š”~~?

 

์ƒ์„ฑ ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•œ๊ฑด ๋ดค์œผ๋‹ˆ, ํ•„ํ„ฐ๋ง ์—ฐ์‚ฐ์ž์— ๋Œ€ํ•ด์„œ ํ•œ๋ฒˆ ๋ณผ๊ฒŒ์š”!

์–ด๋–ค ์˜ต์ €๋ฒ„๋ธ”์„ filter ์—ฐ์‚ฐ์ž์— ๋„ฃ์—ˆ๋”๋‹ˆ, ๊ทธ ์กฐ๊ฑด์— ๋งž๋„๋ก ํ•„ํ„ฐ๋ง๋œ ์˜ต์ €๋ฒ„๋ธ”์ด ๋‚˜์™”๋‹ค. ๋ผ๊ณ  ํ•ด์„ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ?

์—ฌ๊ธฐ์„œ ์ƒ์„ฑ๊ณผ ํ•„ํ„ฐ๋ง ์—ฐ์‚ฐ์ž์˜ ์ฐจ์ด ์ค‘ ํ•˜๋‚˜๋Š” Input์˜ ํ˜•ํƒœ๊ฐ€ ๊ทธ๋ƒฅ ๋ฐ์ดํ„ฐ์ด๊ฑฐ๋‚˜, ์˜ต์ €๋ฒ„๋ธ”์ด๊ฑฐ๋‚˜! ์ด ์ฐจ์ด๊ฐ€ ์žˆ๋„ค์š”!! ๐Ÿ‘

 

์ž, ์ด์ œ ํ•œ๊ฐ€์ง€๋งŒ ๋” ์ดํ•ดํ•˜์‹œ๋ฉด ๋งˆ๋ธ”๋‹ค์ด์–ด ๊ทธ๋žจ์„ ๋‹ค ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„๊ฑฐ์—์š”.

๋ฐ”๋กœ observeOn ์—ฐ์‚ฐ์ž์—์š”! ์ด๊ฑด ์ง€๊ธˆ๊นŒ์ง€ ํ•œ๊ฒƒ๋“ค์„ ํ•œ๋ฒˆ ์ข…ํ•ฉ์ ์œผ๋กœ ๋ณผ๊ฒŒ์š”!

์Œ.. ์ผ๋‹จ ๋ณต์žกํ•˜๋„ค์š” ๐Ÿ˜‚ ์•„๋‹™๋‹ˆ๋‹ค!! ์ „ํ˜€ ๋ณต์žกํ•˜์ง€ ์•Š์•„์š”. ์ฒœ์ฒœํžˆ ํ•ด์„ํ•ด๋ด…์‹œ๋‹ค.

 

์œ„์—์„œ ์ž ๊น observeOn์€ ์–ด๋–ค ์“ฐ๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ• ๊ฑด์ง€๋ฅผ ์ •ํ•œ๋‹ค๊ณ  ํ–ˆ์ฃ ?

  1. ์ฒ˜์Œ์—” ํŒŒ๋ž€์ƒ‰ ์“ฐ๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ๋™๊ทธ๋ผ๋ฏธ ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ง„ ์˜ต์ €๋ฒ„๋ธ”์ด ์žˆ๋„ค์š”!
  2. ์ด ์˜ต์ €๋ฒ„๋ธ”์ด observeOn(์ฃผํ™ฉ์ƒ‰) ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋‚˜ ์ฃผํ™ฉ์ƒ‰ ์“ฐ๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋Š” ์˜ต์ €๋ฒ„๋ธ”์ด ๋์–ด์š”.
  3. ๊ทธ๋ฆฌ๊ณ  ์ด ์˜ต์ €๋ฒ„๋ธ”์€ map ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋‚˜ ๋„ค๋ชจ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜์ด ๋˜์—ˆ์–ด์š”!
  4. ๊ทผ๋ฐ subscribeOn(ํŒŒ๋ž‘) ์€ ๋ญ˜๊นŒ์š”? ๋ฐ”๋กœ ์ตœ์ดˆ์— ์–ด๋–ค ์“ฐ๋ ˆ๋“œ์—์„œ ์‹œ์ž‘ํ• ์ง€๋ฅผ ์ •ํ•˜๋Š”๊ฑฐ์—์š”. ์ด subscribeOn(ํŒŒ๋ž‘)์˜ ์˜ํ–ฅ์œผ๋กœ ์ œ์ผ ์ฒ˜์Œ ์‹œ์ž‘๋˜๋Š” ์“ฐ๋ ˆ๋“œ๊ฐ€ ํŒŒ๋ž€์ƒ‰ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋˜๋Š”๊ฑฐ์—์š”.
  5. ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ observeOn(๋ถ„ํ™)์„ ๋งŒ๋‚˜, ๊ฒฐ๋ก ์ ์œผ๋กœ ๋ถ„ํ™์“ฐ๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋Š” ๋„ค๋ชจ๋ฐ์ดํ„ฐ(next) ์ด๋ฒคํŠธ๋“ค๊ณผ, completed ์ด๋ฒคํŠธ๊ฐ€ ์žˆ๋Š” ์˜ต์ €๋ฒ„๋ธ”์ด ์™„์„ฑ๋˜์—ˆ์–ด์š”!

์–ด๋ ต์ง€ ์•Š์ฃ !? ๋ชจ๋ฅด๊ณ  ๋ณด๋ฉด ๊ต‰์žฅํžˆ ๋ณต์žกํ•˜๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์•Œ๊ณ  ๋ณด๋ฉด ๊ฐ„๋‹จํ•ด์š”! ๐ŸคŸ๐ŸคŸ๐ŸคŸ

์ด๋ ‡๊ฒŒ ๋งˆ๋ธ” ๋‹ค์ด์–ด๊ทธ๋žจ์„ ํ•ด์„ํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ์•Œ์•„๋ดค์œผ๋‹ˆ, ๊ทธ๋Ÿผ ์ด์ œ๋Š” ์ € ๋งํฌ์— ์žˆ๋˜ ๋‹ค์–‘~~~~ํ•œ ์—ฐ์‚ฐ์ž๋“ค์„ ๋ˆŒ๋Ÿฌ์„œ ๋งˆ๋ธ”๋‹ค์ด์–ด๊ทธ๋žจ๋งŒ ๋ณด๋ฉด ์•„~ ์ด๊ฒŒ ์–ด๋–ค ์—ฐ์‚ฐ์ž๊ตฌ๋‚˜. ํ•˜๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ !?

 

 

Scheduler

ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ 3๊ฐ€์ง€์ค‘ 2๊ฐ€์ง€ (Observable, Operator)๋ฅผ ์•Œ์•„๋ดค๊ณ , ์ด์ œ ๋งˆ์ง€๋ง‰! Scheduler์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋ ค๊ณ  ํ•ด์š”!

์œ„์—์„œ observe(on:) ์—ฐ์‚ฐ์ž๋ฅผ ์†Œ๊ฐœํ•˜๋ฉด์„œ ์–ด๋–ค "์“ฐ๋ ˆ๋“œ"์—์„œ ๋™์ž‘ํ•˜๊ฒŒ ํ• ๊ฑด์ง€ ์ •ํ•œ๋‹ค๊ณ  ํ–ˆ์—ˆ์–ด์š”. ๊ธฐ์–ต๋‚˜์‹œ๋‚˜์š”?๐Ÿ˜

 

์šฐ๋ฆฌ๋Š” RxSwift๋ฅผ ์“ฐ์ง€ ์•Š์„๋•Œ๋Š” ์–ด๋–ป๊ฒŒ ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•˜๋„๋ก ํ–ˆ์—ˆ์ฃ ~?

๋ฐ”๋กœ GCD๋ฅผ ์‚ฌ์šฉํ•ด์„œ์˜€์ฃ ! DispatchQueue, OperationQueue ์ด๊ฒƒ๋“ค์š”!

GCD๋ฅผ ํ†ตํ•ด ์–ด๋–ค "ํ"์— ์ž‘์—…์„ ํ• ๋‹นํ•˜๊ณ , ๊ทธ "ํ"๊ฐ€ ์–ด๋–ค "์“ฐ๋ ˆ๋“œ" ์—์„œ ์‹ค์ œ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ• ์ง€๋Š” ์ •ํ•ด์„œ ์•Œ์•„์„œ ํ•ด์คฌ์ฃ ?

๋งˆ์ฐฌ๊ฐ€์ง€์—์š”. GCD์™€ ๊ฐ™์€๊ฒŒ Scheduler๋ผ๊ณ  ์ƒ๊ฐํ•ด๋„ ๋  ๊ฒƒ ๊ฐ™์•„์š”.

let jsonObservable = downloadJson(urlString: Constants.BASE_URL + Constants.API_KEY)
let helloObservable = Observable.just("Hello")
            
        Observable.zip(jsonObservable, helloObservable) { $1 + "\n" + $0  }
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { text in
                self.contentsTextView.text = text
                self.setVisibleWithAnimation(status: false)
            })
            .disposed(by: disposeBag)

์—ฌ๊ธฐ์„œ .observe(on: MainScheduler.instance) ์ด๋ ‡๊ฒŒ ๋ฉ”์ธ์Šค์ผ€์ฅด๋Ÿฌ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค์ผ€์ฅด๋Ÿฌ, ์ปค์Šคํ…€ ์Šค์ผ€์ฅด๋Ÿฌ ์ค‘ ์šฉ๋„์— ๋งž๊ฒŒ ์Šค์ผ€์ฅด๋Ÿฌ์— ๋“ฑ๋กํ•˜๊ณ , ์Šค์ผ€์ฅด๋Ÿฌ๋Š” ์•Œ์•„์„œ ์“ฐ๋ ˆ๋“œ๋ฅผ ํ• ๋‹นํ•ด์„œ ์ž‘์—…์„ ํ•ด์ค€๋‹ค๋Š”๊ฑฐ์ฃ ! ํ•ต์‹ฌ ๊ฐœ๋…์ด์ง€๋งŒ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ์ฃ ~~?

 

 

์ง€๊ธˆ๊นŒ์ง€ ๊ธด ์‹œ๊ฐ„๋™์•ˆ RxSwift์— ํ•ต์‹ฌ ๊ฐœ๋…๊ณผ, ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋Š”๋ฐ์š”!

** ์š”์•…

Observable์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์ถœํ•˜๋Š” ๊ด€์ฐฐ๊ฐ€๋Šฅํ•œ ์‹œํ€€์Šค์ด๋‹ค. Observer๋Š” Observable์„ ๊ตฌ๋…(subscribe)ํ•˜๊ณ , Observable์ด ์ด๋ฒคํŠธ๋ฅผ ๋ฐฐ์ถœํ•˜๋ฉด ํ•ด๋‹น Observable์„ ๊ตฌ๋…ํ•˜๋Š” Observer๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•œ๋‹ค. Observable์„ ์ƒ์„ฑํ•˜๊ณ , ๊ตฌ๋…ํ•˜๋Š” ๊ณผ์ •์—์„œ ์‚ฌ์šฉ์˜ ํŽธ๋ฆฌ์„ฑ์ด๋‚˜ ๊ธฐ๋Šฅํ™•์žฅ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ Operator(์—ฐ์‚ฐ์ž) ๊ฐ€ ์žˆ์—ˆ๊ณ , ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ํ›„ observeOn๊ณผ Scheduler๋ฅผ ํ†ตํ•ด ์–ด๋–ค ์Šค์ผ€์ฅด๋Ÿฌ์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ• ์ง€ ์ •ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

 

์›Œ์šฐ~~ ์งง๊ฒŒ ์š”์•ฝํ•œ๋‹ค๊ณ  ํ•ด๋ดค๋Š”๋ฐ ์ด์ œ ์ด ๋ฌธ์žฅ์ด ์ดํ•ด๊ฐ€ ๊ฐ€์‹œ๋‚˜์š”!? ๐Ÿ˜‚

์†”์งํžˆ RxSwift์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•˜๊ธฐ ์ „์—๋Š” Observable, ์‹œํ€€์Šค, ๊ตฌ๋… ๋“ฑ๋“ฑ ๋ญ”๋ง์ด์•ผ~ ์‹ถ์—ˆ๋Š”๋ฐ, ์ด์ œ ๋ณด๋‹ˆ ์ดํ•ด๊ฐ€ ๋˜์ฃ ?

์ €๋Š” ๊ธฐํšŒ๊ฐ€ ๋˜๋ฉด RxSwift๋ฅผ ํ™œ์šฉํ•ด์„œ ์ตœ์†Œ Toy Project๋‚˜, ์‹ค์ œ ์—…๋ฌด์—์„œ๋„ ์Šค์œฝ ํ•˜๊ณ  ํ™œ์šฉํ•ด๋ณด๋ ค๊ณ ์š”!! 

 


 

๊ทธ๋Ÿผ RxSwift๋ฅผ ์“ฐ๋ฉด ์žฅ์ ์€ ๋ญ๊ณ , ๋‹จ์ ์€ ๋ญ˜๊นŒ์š”~?

์ด๋ฒˆ ํฌ์ŠคํŒ…์ด ๊ณผํ•˜๊ฒŒ ๊ธธ์–ด์กŒ์œผ๋‹ˆ.. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ์žฅ๋‹จ์ ์„ ์‚ดํŽด๋ณผ๊ฒŒ์š”!!

 

 

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋Š˜๋„ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž˜์š” ๐Ÿ™

 

 

์ฐธ๊ณ ์ž๋ฃŒ : ์•ผ๊ณฐ๋‹˜์˜ ์œ ํˆฌ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์„ฑ๋œ ๊ธ€์ž…๋‹ˆ๋‹ค.