Error Handling

๐Ÿšจ Error Handling Operators โ€” catchError, retry, materialize

โ€œ์‹คํŒจ๋Š” ๋‹น์—ฐํ•˜๋‹ค. ๋ฌธ์ œ๋Š” ๋ณต๊ตฌ๋‹ค.โ€

RxSwift์˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์˜คํผ๋ ˆ์ดํ„ฐ๋Š” ์ŠคํŠธ๋ฆผ ์˜ค๋ฅ˜๋ฅผ ๊ฐ€๋กœ์ฑ„๊ณ , ๋Œ€์ฒดํ•˜๊ฑฐ๋‚˜, ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค. ์•ฑ ์•ˆ์ •์„ฑ์˜ ํ•ต์‹ฌ์ด๋ฏ€๋กœ ํ™•์‹คํžˆ ์ดํ•ดํ•ด ๋‘ก์‹œ๋‹ค.


1๏ธโƒฃ ์ฃผ์š” ์˜คํผ๋ ˆ์ดํ„ฐ ์š”์•ฝ

Operator
์—ญํ• 
์‹œ๊ทธ๋‹ˆ์ฒ˜ ์š”์•ฝ

catchError

์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋Œ€์ฒด Observable๋กœ ์Šค์œ„์น˜

(Error) -> Observable<R>

catchErrorJustReturn

ํŠน์ • ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋Œ€์ฒด

(R)

retry(_ count:Int?)

์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์žฌ๊ตฌ๋…(๋ฌดํ•œ or nํšŒ)

Int?

retryWhen

์˜ค๋ฅ˜ ์ŠคํŠธ๋ฆผ์„ ๋ณ€ํ™˜ํ•ด ์กฐ๊ฑด ์žฌ์‹œ๋„

(Observable<Error>) -> Observable<Void>

materialize

Event<Element>๋กœ ๋ž˜ํ•‘(๊ฐ’ยท์™„๋ฃŒยท์˜ค๋ฅ˜ ํฌํ•จ)

โ€”

dematerialize

๋ฐ˜๋Œ€ ์—ฐ์‚ฐ

โ€”


2๏ธโƒฃ ์‹ค์ „ ์Šค๋‹ˆํŽซ

A. catchError โ€” Offline Placeholder

api.fetchNews()
    .catchError { _ in localCache.news() }
    .bind(to: tableView.rx.items(...))

B. retry โ€” ๋„คํŠธ์›Œํฌ ์žฌ์‹œ๋„ 3ํšŒ

api.post(data)
    .retry(3)
    .subscribe(onError: showToast)

C. retryWhen โ€” ์ง€์ˆ˜ ๋ฐฑ์˜คํ”„ ์žฌ์‹œ๋„

api.fetch()
    .retryWhen { errors in
        errors.enumerated().flatMap { attempt, error -> Observable<Int> in
            guard attempt < 3 else { return Observable.error(error) }
            let delay = pow(2.0, Double(attempt))
            return Observable<Int>.timer(.seconds(Int(delay)), scheduler: MainScheduler.instance)
        }
    }

D. materialize โ€” ์˜ค๋ฅ˜๋ฅผ UI ์ด๋ฒคํŠธ๋กœ ํ‘œ์‹œ

api.fetch()
    .materialize()
    .subscribe(onNext: { event in
        switch event {
        case .next(let value): render(value)
        case .error(let e): showError(e)
        case .completed: break
        }
    })

3๏ธโƒฃ ํŒจํ„ด ์š”์•ฝ

์‹œ๋‚˜๋ฆฌ์˜ค
์ถ”์ฒœ ์˜คํผ๋ ˆ์ดํ„ฐ
์ด์œ 

๋„คํŠธ์›Œํฌ ๋‹ค์šด โ†’ ๋กœ์ปฌ ์บ์‹œ ๋Œ€์ฒด

catchError

์ŠคํŠธ๋ฆผ ์œ ์ง€ & Fallback

Transient API ์‹คํŒจ ์žฌ์‹œ๋„

retry(3)

๊ฐ„๋‹จ ๋ฐ˜๋ณต

HTTP 429 Rateโ€‘Limit ์žฌ์‹œ๋„ ํ—ค๋” ๋ฐ˜์˜

retryWhen

์„œ๋ฒ„ ์ œ๊ณต ์‹œ๊ฐ„๊นŒ์ง€ delay

์‚ฌ์šฉ์ž ์•Œ๋ฆผ ์—†์ด ์‹คํŒจ ๋ฌด์‹œ

catchErrorJustReturn(defaultValue)

UX ๊ฐ„๊ฒฐ


4๏ธโƒฃ Error vs Completion ํ๋ฆ„

  • catchError๋กœ ๋Œ€์ฒดํ•œ Observable์ด ์™„๋ฃŒ๋˜๋ฉด ์ƒ์œ„ ์ŠคํŠธ๋ฆผ๋„ ์™„๋ฃŒ.

  • retry๋Š” source Observable์„ ์žฌ๊ตฌ๋…ํ•˜๊ณ , ์—๋Ÿฌ๊ฐ€ ์•„๋‹Œ ์ •์ƒ ์™„๋ฃŒ ์‹œ ๊ทธ ์ฆ‰์‹œ ์™„๋ฃŒ.

retryWhen ๋‚ด๋ถ€ Observable์ด onCompletedํ•˜๋ฉด ์žฌ์‹œ๋„ ์ข…๋ฃŒ; onError๋ฉด ์ „ํŒŒ.


5๏ธโƒฃ pitfalls

  1. retry ๋ฌดํ•œ ๋ฃจํ”„: ์„œ๋ฒ„ ์˜๊ตฌ ๋‹ค์šด ์‹œ ๋ฐฐํ„ฐ๋ฆฌ ์†Œ๋ชจ โ€” retry(โˆž) ๋Œ€์‹  retry(3).

  2. catchErrorJustReturn ๋‚จ๋ฐœ: ์—๋Ÿฌ ๋กœ๊น… ๋ˆ„๋ฝ โ†’ ๋””๋ฒ„๊น… ์–ด๋ ค์›€.

  3. materialize ํ›„ dematerialize ๊นœ๋นก: ์ด๋ฒคํŠธ ํƒ€์ž… ์žŠ์–ด๋ฒ„๋ ค ์ฒด์ธ ์˜ค๋ฅ˜.


6๏ธโƒฃ Mini Quiz

  1. retryWhen ๋‚ด๋ถ€ Observable์ด .next() ์—†์ด .completed๋งŒ ํ•˜๋ฉด ๊ฒฐ๊ณผ๋Š”?

  2. catchError์™€ onErrorResumeNext ์ฐจ์ด๋Š”?

  3. materialize๋œ ์ŠคํŠธ๋ฆผ์„ share() ํ›„ ๋‹ค์‹œ dematerializeํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ?

Answers
  1. ์žฌ์‹œ๋„ ์ค‘๋‹จํ•˜๊ณ  source ์ŠคํŠธ๋ฆผ์„ completed๋กœ ์ข…๋ฃŒํ•œ๋‹ค.

  2. onErrorResumeNext๋Š” ์˜ค๋ฅ˜ ํƒ€์ž… ๋ฌด์‹œํ•˜๊ณ  ๋งˆ์ง€๋ง‰ Observable์„ ๋ฌด์กฐ๊ฑด ์—ฐ๊ฒฐ(Errorโ†’Next), catchError๋Š” ์กฐ๊ฑด๋ถ€ ์ฒ˜๋ฆฌ ํ›„ ์—๋Ÿฌ๋ฅผ ์ „ํŒŒํ•˜๊ฑฐ๋‚˜ ๋Œ€์ฒด.

  3. share ์ดํ›„ ๋™์ผ Event ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ Subscriber๊ฐ€ ์‚ฌ์šฉ โ€” threadโ€‘safety ๋ฌธ์ œ ์—†์ง€๋งŒ, ๋‹ค์‹œ dematerialize ์‹œ ๋ฐ˜๋“œ์‹œ ์›๋ณธ Event ํƒ€์ž… ์œ ์ง€.


๐ŸŽ‰ Operators ์ฑ•ํ„ฐ ๋! ์ด์ œ ์‹ฌํ™” ํ•™์Šต์œผ๋กœ ๊ฐ•ํ™”ํ•ด ๋ณด์„ธ์š”. ๐Ÿš€

Last updated