๐ŸŠ RxSwift | Observable vs Observer vs Subject vs Relay ์ฐจ์ด

SeBinยท2025๋…„ 2์›” 16์ผ
post-thumbnail

์•ˆ๋…•ํ•˜์„ธ์š”, ์ด๋ฒˆ์—” RxSwift๋ฅผ ๋‹ค๋ฃจ๋ฉด์„œ ์‰ฝ๊ฒŒ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋Š”

Observable vs Observer vs Subject vs Relay ์ฐจ์ด๋ฅผ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!

RxSwift

๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜
Asynchronous Programming with Observable Stream

RxSwift๋Š” ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ(๋ฒ„ํŠผ ํƒญ, ํ‚ค๋ณด๋“œ ๋“ฑ ์œ ์ €์˜ ๋ฐ˜์‘)๊ฐ€ ์™”์„ ๋•Œ,

๊ทธ ์ด๋ฒคํŠธ๋ฅผ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค๊ณ (Observable), ๊ทธ ์ด๋ฒคํŠธ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ฒƒ(Observer)์ด ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ๋ณ€ํ™”์— ๋”ฐ๋ผ ์–ด๋–ค ๋™์ž‘์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, Observable๊ณผ Observer๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ Stream(๋ฐ์ดํ„ฐ ํ๋ฆ„)์„ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๊ณ , Operator๋กœ Steam์„ ๋ณ€๊ฒฝ, ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.


Observable

์œ„์— ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด Observable์€ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋Š” ์ƒ์‚ฐ์ž
  • ๋‹จ, ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๊ธฐ๋งŒ ํ•  ์ˆ˜ ์žˆ๊ณ  ์ง์ ‘ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ถœ(Emit)ํ•œ๋‹ค ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
let emailText = Observable.just("a@a.com")

Observer

Observable์ด ๋งŒ๋“ค์–ด์ค€ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ด๋ฒคํŠธ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š”๊ฒŒ Observer์ž…๋‹ˆ๋‹ค.

  • ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ์†Œ๋น„์ž
  • ์ •ํ™•ํžˆ๋Š” Subscribe ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ง์ ‘ ์ด๋ฒคํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

Observer๋Š” ์–ด๋–ป๊ฒŒ ๊ด€์ฐฐํ•˜๋Š”๊ฑธ๊นŒ์š”?

๋ฐ”๋กœ Observable๋ฅผ ๊ตฌ๋…(Subscribe)ํ•ด์„œ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๊ด€์ฐฐ์„ ๊ทธ๋งŒ๋‘๊ณ  ์‹ถ๋‹ค๋ฉด ๊ตฌ๋… ์ทจ์†Œ(disposeBag)๋ฅผ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
dispose๋Š” Subscribe ์ค‘์ธ Stream์„ ์›ํ•˜๋Š” ์‹œ๊ธฐ์— ์ •๋ฆฌํ•˜๋„๋ก ํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

emailText.subscribe(with: self) { owner, value in
    owner.emailTextField.text = value
} onError: { error in
    print("onError")
} onCompleted: {
    print("onCompleted")
} onDisposed: {
    print("onDisposed")
}
.disposed(by: disposeBag)

์ด๋ฒคํŠธ๋Š” 3๊ฐ€์ง€ ์ข…๋ฅ˜๋กœ ๋‚˜๋ˆ ์ง€๋ฉฐ Observer๋Š” ์ด์— ๋Œ€ํ•ด ๊ฐ๊ฐ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Next - ex) ์ตœ์‹  ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  • Error - ex) ๋””์ฝ”๋”ฉ ์‹คํŒจ, ์ƒํƒœ ์ฝ”๋“œ ์˜ค๋ฅ˜, ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์œ ์‹ค ๋“ฑ
  • Completed - ex) ๋„คํŠธ์›Œํฌ ์„ฑ๊ณต

Observable์€ Next ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•˜๊ณ  โ†’ Error ํ˜น์€ Completed ์ด๋ฒคํŠธ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋˜๋ฉด ์ข…๋ฃŒ๊ฐ€ ๋˜๊ณ  โ†’ ์ข…๋ฃŒ๋˜๋ฉด disposed๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ

์ˆœ์œผ๋กœ ์‚ฌ์ดํด์ด ๋Œ์•„๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ UI ๊ด€๋ จ ์ด๋ฒคํŠธ๋Š” ๋””์ฝ”๋”ฉ ์‹คํŒจ, ์ƒํƒœ ์ฝ”๋“œ ์˜ค๋ฅ˜ ๊ฐ™์€ Error ์ด๋ฒคํŠธ๊ฐ€ ๋‚  ์ผ์ด ์—†๋Š”๋ฐ ๊ตณ์ด ์ฒ˜๋ฆฌํ•ด์•ผํ• ๊นŒ์š”? ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†์ฃ .

๊ทธ๋ž˜์„œ ๊ฐ’ ์ „๋‹ฌ๋งŒ ํ•˜๋Š” Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜๊ฒŒ๋” ๊ฐ•์ œํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ๋‹ค์Œ์— Subscribe vs Bind vs Drive ์ฐจ์ด๋ฅผ ์ •๋ฆฌํ•˜๋ฉด์„œ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


Subject

์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๋ฉด์„œ ๋™์‹œ์— ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Observable์€ ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๊ธฐ๋งŒ ํ•  ์ˆ˜ ์žˆ๊ณ , Observer๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌ๋งŒ ํ•  ์ˆ˜ ์žˆ๋Š” ์•„์‰ฌ์›€์ด ์žˆ๋Š”๋ฐ ์ด๋ฅผ ๋ณด์™„ํ•  ์ˆ˜ ์žˆ์ฃ .

Subject์—๋Š” 4๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • BehaviorSubject - ์ดˆ๊ธฐ๊ฐ’ ์žˆ์Œ
  • PublishSubject - ์ดˆ๊ธฐ๊ฐ’ ์—†์Œ
  • ReplaySubject: ์ง€์ •๋œ ๊ฐœ์ˆ˜๋งŒํผ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ subscribe ์งํ›„ ํ•œ๋ฒˆ์— ์ด๋ฒคํŠธ ์ „๋‹ฌ
  • AsyncSubject: Completed ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „๊นŒ์ง€ ์–ด๋–ค ์ด๋ฒคํŠธ๋„ ์ „๋‹ฌํ•˜์ง€ ์•Š๋‹ค๊ฐ€, Completed ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๊ฐ’๋งŒ ๋ฐฉ์ถœ

BehaviorSubject์™€ PublishSubject์˜ ์ฐจ์ด

let num = BehaviorSubject(value: 0)
let number = PublishSubject<Int>()

๊ฐ€์žฅ ํฐ ์ฐจ์ด๋Š” ์ดˆ๊ธฐ๊ฐ’์ด ์žˆ๋ƒ ์—†๋ƒ๋„ ์žˆ์ง€๋งŒ,

Subscribe ์ด์ „์— emitํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋ƒ ์—†๋ƒ๊ฐ€ ํฝ๋‹ˆ๋‹ค.

BehaviorSubject์ธ ๊ฒฝ์šฐ, Subscribe ์ด์ „์— onNext ๋กœ Next ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ฒŒ๋˜๋ฉด ๊ฐ€์žฅ ์ตœ๊ทผ์— ์ „๋‹ฌ๋œ ์ด๋ฒคํŠธ ํ•˜๋‚˜๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PublishSubject๋Š” Subscribe ์ด์ „์˜ ์ด๋ฒคํŠธ๋“ค์€ ๋ชจ๋‘ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.


์˜ˆ๋ฅผ ๋“ค๋ฉด TextField์˜ text๋ฅผ Subject๊ฐ€ ๊ด€์ฐฐํ•˜์—ฌ 2๊ธ€์ž ๋ฏธ๋งŒ์ธ ๊ฒฝ์šฐ ๊ฒฝ๊ณ ์ฐฝ์„ ๋„์šด๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค.

TextField์˜ text ์ค‘ orEmtpy๋Š” text๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ๊ฐ’์„ ๋ณด๋‚ด์ฃผ๋Š” ์ด๋ฒคํŠธ์ธ๋ฐ์š”, ํŠน์ง•์€ ํ•ด๋‹น TextField๊ฐ€ ์žˆ๋Š” ๋ทฐ๊ฐ€ ๋กœ๋“œ๋˜์ž๋งˆ์ž ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•˜์—ฌ ๋งจ ์ฒ˜์Œ์—” ํ•ญ์ƒ ๋นˆ ๊ฐ’์ด ๋“ค์–ด์˜ต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ด text๋ฅผ BehaviorSubject๊ฐ€ ๊ด€์ฐฐํ•œ๋‹ค๋ฉด ๋นˆ๊ฐ’์ด ๋“ค์–ด์˜ด๊ณผ ๋™์‹œ์— ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋˜๊ณ , ๋‹น์—ฐํžˆ ๋นˆ๊ฐ’์€ 2๊ธ€์ž ๋ฏธ๋งŒ์ด๋ฏ€๋กœ ์œ ์ €๋Š” ํ™”๋ฉด์— ๋“ค์–ด์„œ์ž๋งˆ์ž ๊ฒฝ๊ณ ์ฐฝ์„ ๋ณด๊ฒŒ ๋˜๋Š” ๋ถˆ์ƒ์‚ฌ๊ฐ€ ์ผ์–ด๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿด ๋• Subscribeํ•˜๊ธฐ ์ „๊นŒ์ง„ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” PublishSubject๋ฅผ ํ†ตํ•ด์„œ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Relay

Relay๋Š” ์‚ฌ์‹ค์ƒ Subject์ž…๋‹ˆ๋‹ค.

  • ์ฐจ์ด๋Š” ์ด๋ฒคํŠธ๋ฅผ Next๋งŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋ƒ, ์—†๋ƒ ์ž…๋‹ˆ๋‹ค.

์œ„์— '๊ฐ’ ์ „๋‹ฌ๋งŒ ํ•˜๋Š” Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜๊ฒŒ๋” ๊ฐ•์ œํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.' ๋ผ๊ณ  ํ–ˆ๋Š”๋ฐ์š”

์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉด์„œ, Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ Relay์ž…๋‹ˆ๋‹ค.

Next ์ด๋ฒคํŠธ๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋ณดํ†ต UI ๊ด€๋ จ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ์ด๊ธฐ ๋•Œ๋ฌธ์— Relay๋Š” UI ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UI๊ฐ€ Error์™€ Compledted ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ฒŒ ๋˜๋ฉด ๋”์ด์ƒ Next ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌ ๋ฐ›์„ ์ˆ˜ ์—†๊ฒŒ ๋˜๊ณ , ๊ทธ๋Ÿฌ๋ฉด ๋ฐ˜์‘ํ˜•์˜ ์žฅ์ ์ด ์‚ฌ๋ผ์ง€๊ฒŒ ๋˜๊ฒ ์ฃ .

๊ทธ๋ž˜์„œ Relay๋Š” disposed๊ฐ€ ๋˜๊ธฐ ์ „๊นŒ์ง€ Subscribe๊ฐ€ ํ•ด์ œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. disposed๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” deinit ์‹œ์ ์ด๋‚˜ ์ง์ ‘ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Relay์—๋Š” 3๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉฐ ๋™์ž‘๋ฐฉ์‹์€ Subject์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

  • BehaviorRelay
  • PublishRelay
  • ReplayRelay

์•ฝ๊ฐ„์˜ ๋‹ค๋ฅธ ์ ์€ ๊ฐ’์„ ํ• ๋‹นํ•˜์—ฌ Next ์ด๋ฒคํŠธ๋ฅผ ๋ฐฉ์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์กฐ๊ธˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

  • Subject - onNext
  • Relay - accept
value.onNext(num)
value.accept(num)

RxSwift์˜ Observable vs Observer vs Subject vs Relay ์ฐจ์ด์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณด์•˜๋Š”๋ฐ์š”,

๋„์›€์ด ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค! ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€