Skip to content

Instantly share code, notes, and snippets.

@casamia918
Forked from staltz/introrx.md
Last active August 22, 2024 02:54
Show Gist options
  • Save casamia918/93b8db69beb9ee06b92a96b2a234d48e to your computer and use it in GitHub Desktop.
Save casamia918/93b8db69beb9ee06b92a96b2a234d48e to your computer and use it in GitHub Desktop.
[Korean ver.] The introduction to Reactive Programming you've been missing

๋‹น์‹ ์ด ๋†“์น˜๊ณ  ์žˆ๋˜ Reactive Programming์— ๋Œ€ํ•œ ์•ˆ๋‚ด (ํ•œ๊ธ€ ๋ฒˆ์—ญ)

The introduction to Reactive Programming you've been missing (Korean ver.)

(by @andrestaltz)

(orginal source: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)


์ด ํŠœํ† ๋ฆฌ์–ผ์€ ๋น„๋””์˜ค ์‹œ๋ฆฌ์ฆˆ๋„ ์žˆ์Šต๋‹ˆ๋‹ค

๋งŒ์•ฝ ์‹ค์ œ ์ฝ”๋”ฉํ•˜๋Š” ๋น„๋””์˜ค ํŠœํ† ๋ฆฌ์–ผ์„ ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด, ์ด ๊ธ€๊ณผ ๊ฐ™์€ ๋‚ด์šฉ์œผ๋กœ ๋…นํ™”ํ•œ ๋น„๋””์˜ค ์‹œ๋ฆฌ์ฆˆ๋ฅผ ํ™•์ธํ•˜์„ธ์š”: Egghead.io - Introduction to Reactive Programming.


์•„๋งˆ ๋‹น์‹ ์€ Reactive Programming, ํŠนํžˆ ์ด๊ฒƒ์˜ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์ธ Rx, Bacon.js, RAC ๋“ฑ๋“ฑ์„ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์— ๋Œ€ํ•ด์„œ ๊ด€ํ•ด์„œ ๊ด€์‹ฌ์žˆ๋Š” ์‚ฌ๋žŒ์ผ๊ฒ๋‹ˆ๋‹ค.

Reactive Programming์€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด ์ข‹์€ ํ•™์Šต์ž๋ฃŒ๊ฐ€ ๋ณ„๋กœ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์šฐ๊ธฐ๊ฐ€ ๋” ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ œ๊ฐ€ Reactive Programming์„ ์ฒ˜์Œ ์‹œ์ž‘ํ–ˆ์„๋•Œ, ํŠœํ† ๋ฆฌ์–ผ์„ ์ฐพ์•„๋ณด๋ ค ํ–ˆ์ง€๋งŒ, ์†Œ์ˆ˜์˜ ๊ฐ€์ด๋“œ ๋ฌธ์„œ๊ฐ€ ์ „๋ถ€์˜€์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ทธ๊ฒƒ๋“ค์€ ์ˆ˜๋ฐ• ๊ฒ‰ํ•ฅ๊ธฐ์‹ ๋‚ด์šฉ์ด์˜€๊ณ , Reactive Programming ์œผ๋กœ ์•„ํ‚คํ…์ณ๋ฅผ ์Œ“๋Š” ์–ด๋ ค์šด ๋ถ€๋ถ„์€ ์ „ํ˜€ ๋‹ค๋ฃจ์ง€ ์•Š์•˜์ง€์š”. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฌธ์„œ๋Š” ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ ์ดํ•ดํ•˜๋ ค๊ณ  ํ• ๋•Œ ๊ฑฐ์˜ ๋„์›€์ด ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ง„์งœ์—์š”. ์ด๊ฑธ ๋ณด์„ธ์š”:

Rx.Observable.prototype.flatMapLatest(selector, [thisArg])

Observable sequence์— ์žˆ๋Š” ๊ฐ ์š”์†Œ๋ฅผ ์š”์†Œ์˜ index์™€ ์—ฐ๊ณ„ํ•˜์—ฌ ์ƒˆ๋กœ์šด Observable sequence์˜ sequence๋กœ project์‹œํ‚ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ ๋‚˜์„œ, observable sequence๋“ค์˜ observable sequence๋ฅผ ๊ฐ€์žฅ ์ตœ๊ทผ observable sequence์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ์ƒ์‚ฐํ•ด๋‚ด๋Š” observable sequence๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

Projects each element of an observable sequence into a new sequence of observable sequences by incorporating the element's index and then transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.

์ œ๊ธฐ๋ž„...

์ „ ์ฑ…์„ ๋‘๊ถŒ ์ฝ์—ˆ๋Š”๋ฐ์š”, ํ•˜๋‚˜๋Š” ๊ทธ๋ƒฅ ํฐ๊ทธ๋ฆผ์„ ๊ทธ๋ ค์คฌ๊ณ , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” Reactive library ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋งŒ ์•Œ๋ ค์คฌ์Šต๋‹ˆ๋‹ค. ์ด์ฏค์—์„œ ์ „ Reactive Programming ์„ ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฐฐ์šฐ๋Š”๊ฑธ ํฌ๊ธฐํ–ˆ๊ณ , ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ์•Œ์•„๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. Futurice ์—์„œ ์ผํ•  ๋•Œ, ์ €๋Š” Reactive Programming ์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•ด๋ณด๊ธฐ ์‹œ์ž‘ํ–ˆ๊ณ , ๋ฌธ์ œ๊ฐ€ ๋ถ€๋”ชํ˜”์„ ๋• ๋ช‡๋ช‡ ๋™๋ฃŒ๋“ค์˜ ๋„์›€์„ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

Reactive Programming์„ ๋ฐฐ์šธ๋•Œ ๊ฐ€์žฅ ์–ด๋ ค์› ๋˜ ๋ถ€๋ถ„์€, Reactive์ฒ˜๋Ÿผ ์ƒ๊ฐํ•˜๊ธฐ ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€, ๋ช…๋ น(imperative)๊ณผ ์ƒํƒœ(stateful)๋ฅผ ์„œ์ˆ ํ•˜๋Š” ์ „ํ˜•์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๋ฐฉ์‹์„ ๋ฒ„๋ฆฌ๊ณ , ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์œผ๋กœ ์‚ฌ๊ณ ๋ฅผ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ด๋Ÿฐ ๊ด€์ ์œผ๋กœ ์“ฐ์ธ ๊ฐ€์ด๋“œ๋ฅผ ์ธํ„ฐ๋„ท์—์„œ ์ฐพ์ง€ ๋ชปํ–ˆ๊ณ , ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด Reactive๋กœ ์ƒ๊ฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๋ ค์ฃผ๋Š” ํ˜„์‹ค์ ์ธ ํŠœํ† ๋ฆฌ์–ผ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฌธ์„œ๋Š” ๊ทธ ์ดํ›„์— ๋„์›€์ด ๋  ๊ฒ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๊ฐ€ ๋‹น์‹ ์—๊ฒŒ ๋„์›€์ด ๋˜๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

"Reactive Programming์ด ๋ญ˜๊นŒ์š”?"

์ธํ„ฐ๋„ท์—๋Š” Reactive Programming์— ๋Œ€ํ•œ ๋‚˜์œ ์„ค๋ช…๊ณผ ์ •์˜๋“ค์ด ๋„๋ ค์žˆ์Šต๋‹ˆ๋‹ค. Wikipedia ๋Š” ๋Š˜ ๊ทธ๋ ‡๋“ฏ์ด ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ด๊ณ  ์ด๋ก ์ ์ž…๋‹ˆ๋‹ค. Stackoverflow์— ์žˆ๋Š” ํ‘œ์ค€ ๋‹ต๋ณ€์€, ์ดˆ์‹ฌ์ž๋“คํ•œํ…Œ ๊ฒฐ์ฝ” ์ ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Reactive Manifesto ๋Š” ๋งˆ์น˜ ํšŒ์‚ฌ์—์„œ ํ”„๋กœ์ ํŠธ ๋งค๋‹ˆ์ €๋‚˜ ๊ฑฐ๋ž˜์ฒ˜ ์‚ฌ๋žŒํ•œํ…Œ ์†Œ๊ฐœํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. Microsoft์˜ Rx terminology "Rx = Observables + LINQ + Schedulers" ์€ ์„ค๋ช…์ด ๋„ˆ๋ฌด ๋ฌด๊ฒ๊ณ , ์šฐ๋ฆฌ ๋Œ€๋ถ€๋ถ„์„ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฒŒํ•˜๋Š” Microsoftishํ•œ ์Šคํƒ€์ผ ์ž…๋‹ˆ๋‹ค. "reactive"์™€ "propagation of change" ๋ผ๋Š” ์šฉ์–ด๋Š” ์ „ํ˜•์ ์ธ MV* ์™€ ์ธ๊ธฐ์žˆ๋Š” ์–ธ์–ด์—์„œ ์ด๋ฏธ ์“ฐ๊ณ ์žˆ๋Š” ๊ฐœ๋…์ด๋ผ์„œ ์ „ํ˜€ ์ƒˆ๋กญ๊ฒŒ ๋А๊ปด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„ ๋ฌผ๋ก , ์ œ ํ”„๋ ˆ์ž„์›์˜ view๋“ค์€ ๋ชจ๋ธ๋“ค๋กœ๋ถ€ํ„ฐ ๋ฐ˜์‘(react)ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€ํ™”๊ฐ€ ์ „ํŒŒ๋œ๋‹ค๋Š” ๊ฒƒ์€(change is propagated) ๋‹น์—ฐํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ทธ๊ฒŒ ์•ˆ๋œ๋‹ค๋ฉด, ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋˜์ง€ ์•Š๊ฒ ์ฃ .

ํ—›์†Œ๋ฆฌ๋Š” ์—ฌ๊ธฐ๊นŒ์ง€๋กœ ๋๋ƒ…์‹œ๋‹ค.

Reactive programming ์€ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค.

Reactive programming is programming with asynchronous data streams.

์–ด๋–ค ๋ฉด์—์„œ ์ด๊ฒƒ์€ ์ „ํ˜€ ์ƒˆ๋กญ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ๋ฒ„์Šค๋‚˜, ์ „ํ˜•์ ์ธ ํด๋ฆญ ์ด๋ฒคํŠธ๋Š” ๋‹น์—ฐํžˆ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ๋“ค์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ์ด๊ฒƒ๋“ค์„ observeํ•  ์ˆ˜ ์žˆ๊ณ , side effect๋ฅผ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Reactive๋Š” ์ด ์•„์ด๋””์–ด๋ฅผ ํ™•์žฅ์‹œํ‚จ๊ฒ๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ํด๋ฆญ์ด๋‚˜ hover ์ด๋ฒคํŠธ ์™ธ์—, ์–ด๋–ค๊ฒƒ์œผ๋กœ๋„ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์€ ๊ฐ€๋ณ๊ณ , ํ”ํ•ฉ๋‹ˆ๋‹ค. variables, user inputs, properties, caches, data structures, ๊ธฐํƒ€ ๋“ฑ๋“ฑ, ์–ด๋–ค๊ฒƒ์ด๋“  ์ŠคํŠธ๋ฆผ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด, ํŠธ์œ„ํ„ฐ ํ”ผ๋“œ๋ฅผ ํด๋ฆญ ์ด๋ฒคํŠธ์™€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๊ทธ ์ŠคํŠธ๋ฆผ์— listen ํ•  ์ˆ˜ ์žˆ๊ณ , ์ ์ ˆํ•˜๊ฒŒ react ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ์— ๋”ํ•˜์—ฌ, ๋‹น์‹ ์€ ์ŠคํŠธ๋ฆผ๋“ค์„ ์กฐํ•ฉํ•˜๊ณ , ๋งŒ๋“ค๊ณ , ํ•„ํ„ฐ๋งํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋งˆ์–ด๋งˆํ•œ ํ•จ์ˆ˜ ๊พธ๋Ÿฌ๋ฏธ๋“ค์„ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฑด "ํ•จ์ˆ˜ํ˜•(functional)"์ด๋ผ๋Š” ๋งˆ๋ฒ•์ด ๋น›์„ ๋ฐœํ•˜๋Š” ์ˆœ๊ฐ„์ž…๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์€ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์˜ input์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณต์ˆ˜์˜ ์ŠคํŠธ๋ฆผ๋“ค๋„ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์˜ input์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ์ŠคํŠธ๋ฆผ์„ ํ•ฉ์น ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ ํ•„ํ„ฐ๋ง ์‹œ์ผœ์„œ, ๋‹น์‹ ์ด ๊ด€์‹ฌ์žˆ๋Š” ์ด๋ฒคํŠธ๋“ค๋งŒ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๊ฐ’๋“ค์„ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์œผ๋กœ map ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ŠคํŠธ๋ฆผ์ด Reactive์—์„œ ๊ทธ๋ ‡๊ฒŒ ์ค‘์š”ํ•œ ๊ฒƒ์ด๋ผ๋ฉด, ์ข€๋” ์ž์„ธํ•˜๊ฒŒ ์‚ดํŽด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ด๋ฏธ ๋‹ค๋“ค ์ตํžˆ ์•Œ๊ณ  ์žˆ์„ "๋ฒ„ํŠผ ํด๋ฆญ" ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ด…์‹œ๋‹ค.

Click event stream

์ŠคํŠธ๋ฆผ์€ ์‹œ๊ฐ„ ์ˆœ์œผ๋กœ ์ •๋ ฌ๋œ ์ง„ํ–‰์ค‘์ธ ์ด๋ฒคํŠธ ๋“ค์˜ ๋‚˜์—ด ์ž…๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์€ (ํƒ€์ž…์„ ๊ฐ–๊ณ  ์žˆ๋Š”) ๊ฐ’, ์—๋Ÿฌ, ์™„๋ฃŒ ์‹ ํ˜ธ, ์ด ์„ธ๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ(emit)์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. "์™„๋ฃŒ"๋Š” ์ฐฝ์ด๋‚˜ ์ด ๋ฒ„ํŠผ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ํ™”๋ฉด์ด ๋‹ซํ˜€์ง€๋Š” ์ˆœ๊ฐ„์„ ์˜๋ฏธํ•œ๋‹ค๊ณ  ๋ด…์‹œ๋‹ค.

์šฐ๋ฆฌ๋Š” ๊ฐ ์ด๋ฒคํŠธ(๊ฐ’, ์—๋Ÿฌ, ์™„๋ฃŒ)๊ฐ€ ๋ฐœ์ƒํ• ๋•Œ ์‹คํ–‰๋˜์–ด์งˆ ํ•จ์ˆ˜๋“ค์„ ๊ฐ๊ฐ ์ •์˜ํ•จ์œผ๋กœ์จ, ์ด ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋“ค์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ๋งŒ ํฌ์ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ข…์ข…, ์—๋Ÿฌ์™€ ์™„๋ฃŒ ์ด๋ฒคํŠธ๋Š” ์ƒ๋žตํ•˜๊ณ , ๊ฐ’์„ ๋‚ด๋ณด๋‚ด๋Š” ์ด๋ฒคํŠธ๋งŒ ํฌ์ฐฉํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ "listening"ํ•˜๋Š” ๊ฒƒ์€, subscribing(๊ตฌ๋…) ์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ํ•จ์ˆ˜๋“ค์€ observer๋“ค์ž…๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์€ observed ๋˜๋Š” ๋Œ€์ƒ(subject)์ž…๋‹ˆ๋‹ค("observable"์ด๋ผ๊ณ ๋„ ํ•˜์ฃ ) ์ด๊ฒƒ์€ Observer Design Pattern ๊ณผ ์ •ํ™•ํžˆ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

์œ„ ๋„ํ‘œ๋Š” ASCII๋กœ๋„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋„ ์ผ๋ถ€ ์‚ฌ์šฉํ• ๊ฒ๋‹ˆ๋‹ค.

--a---b-c---d---X---|->

a, b, c, d are emitted values
X is an error
| is the 'completed' signal
---> is the timeline

๋„ˆ๋ฌด ์ต์ˆ™ํ•œ ๊ฒƒ๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋” ์ง€๋ฃจํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์ง€ ์•Š๋„ค์š”. ์ด๋ฒˆ์—” ์ƒˆ๋กœ์šด๊ฑธ ํ•ด๋ด…์‹œ๋‹ค. ๊ธฐ์กด์˜ ํด๋ฆญ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ๋ณ€ํ˜•์‹œ์ผœ์„œ, ์ƒˆ๋กœ์šด ํด๋ฆญ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ €, ๋ฒ„ํŠผ์ด ๋ช‡๋ฒˆ์ด๋‚˜ ํด๋ฆญ๋˜์—ˆ๋Š”์ง€๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ณ„์ˆ˜(counter) ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค. ๋ณดํ†ต Reactive library์—๋Š”, ์ŠคํŠธ๋ฆผ์— ๋ถ™์ผ ์ˆ˜ ์žˆ๋Š” ๋งŽ์€ ํ•จ์ˆ˜ ๋ชฉ๋ก(map, filter, scan)์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. clickStream.map(f)์ฒ˜๋Ÿผ ๊ทธ ํ•จ์ˆ˜๋“ค ์ค‘ ํ•˜๋‚˜๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด, ํด๋ฆญ ์ŠคํŠธ๋ฆผ์— ๊ธฐ์ดˆํ•ด์„œ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ ์„ ๋˜๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ธฐ์กด์˜ ํด๋ฆญ ์ŠคํŠธ๋ฆผ์„ ์ „ํ˜€ ๋ณ€ํ˜•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์†์„ฑ์€ ๋ถˆ๋ณ€์„ฑ(immutability) ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋ฉฐ, ๋ง‰๊ฑธ๋ฆฌ์™€ ํŒŒ์ „์ด ์ฐฐ๋–ก๊ถํ•ฉ์ธ ๊ฒƒ์ฒ˜๋Ÿผ Reactive ์ŠคํŠธ๋ฆผ๊ณผ ์ž˜ ์–ด์šธ๋ฆฌ๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค. ์ด ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” clickStream.map(f).scan(g)์ฒ˜๋Ÿผ ํ•จ์ˆ˜ ์ฒด์ด๋‹๋„ ํ• ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  clickStream: ---c----c--c----c------c-->
               vvvvv map(c becomes 1) vvvv
               ---1----1--1----1------1-->
               vvvvvvvvv scan(+) vvvvvvvvv
counterStream: ---1----2--3----4------5-->

map(f) ํ•จ์ˆ˜๋Š” ๊ฐ๊ฐ ๋ฐœ์ƒ(emit)ํ•˜๋Š” ๊ฐ’๋“ค์„, ๋‹น์‹ ์ด ์ •์˜ํ•œ ํ•จ์ˆ˜ f์— ํ†ต๊ณผ์‹œํ‚จ ํ›„, ์ด ์ƒˆ๋กœ์šด ๊ฐ’๋“ค์„ ๊ฐ€์ง€๋Š” ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ์ƒํ™ฉ์„ ์˜ˆ๋กœ ๋“ค์–ด๋ณด์ž๋ฉด, ์šฐ๋ฆฌ๋Š” ๊ฐ๊ฐ์˜ ํด๋ฆญ์„ 1๋กœ map์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. scan(g) ํ•จ์ˆ˜๋Š”, ์ŠคํŠธ๋ฆผ์—์„œ ์ด์ „ ๊ฐ’๋“ค์„ ๋ชจ๋‘ ํ•ฉ์ณ์„œ, x = g(accumulated, current) ๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค. g๋Š” ์ด ์˜ˆ์ œ์—์„œ ์ •์˜ํ•œ ๋‹จ์ˆœํžˆ ๋”ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„, counterStream์€ ํด๋ฆญ์ด ๋ฐœ์ƒํ• ๋•Œ๋งˆ๋‹ค ์ด ํด๋ฆญ ํšŸ์ˆ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

Reactive์˜ ์ง„์งœ ํž˜์„ ๋ณด๊ธฐ ์œ„ํ•ด, ์ด๋ฒˆ์—” ๋‹น์‹ ์ด "๋”๋ธ” ํด๋ฆญ" ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ๊ฐ–๊ธธ ์›ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ๋” ์žฌ๋ฐŒ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ, ์šฐ๋ฆฌ๋Š” ์ƒˆ ์ŠคํŠธ๋ฆผ์ด ๋”๋ธ”ํด๋ฆญ์„ ๋ฐ›๋“ฏ์ด ์‚ผ์ค‘ํด๋ฆญ๋„ ๋ฐ›๋„๋ก ์ทจ๊ธ‰ํ•˜๋˜๊ฐ€, ์•„๋‹ˆ๋ฉด ์•„์˜ˆ ์ผ๋ฐ˜ํ™”ํ•ด์„œ, ๋‹ค์ค‘ ํด๋ฆญ์„ ๋ฐ›๋„๋ก ํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ฃ . ์ž, ์ˆจ ํ•œ๋ฒˆ ํฌ๊ฒŒ ๋“ค์ด์‰ฌ๊ณ , ์ „ํ†ต์ ์ธ ๋ช…๋ น(imperative)์™€ ์ƒํƒœ(stateful)๋ฅผ ์„œ์ˆ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•  ์ง€ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ์žฅ๋‹ด์ปจ๋ฐ ์•„์ฃผ ํ˜•ํŽธ ์—†๋Š” ๋ฐฉ๋ฒ•์ผ๊ฒ๋‹ˆ๋‹ค. ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ , ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ ์กฐ์ž‘์„ ์ข€ ํ•ด์ฃผ๊ฒ ์ฃ .

ํ›—, Reactive์—์„  ๊ฐ„๋‹จํ•˜๋‹ค๊ตฌ์š”. ์‚ฌ์‹ค, ๊ทธ ๋กœ์ง์€ 4์ค„์งœ๋ฆฌ ์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ฝ”๋“œ๋Š” ์ž ๊น ์žŠ๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ดˆ์‹ฌ์ž๊ฑด ์ „๋ฌธ๊ฐ€๊ฑด, ์ŠคํŠธ๋ฆผ์„ ์ดํ•ดํ•˜๊ณ  ๋งŒ๋“œ๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€, ๋„ํ‘œ(๋‹ค์ด์–ด๊ทธ๋žจ, ๊ทธ๋ฆผ)๋ฅผ ์ƒ๊ฐํ•˜๋Š”๊ฒ๋‹ˆ๋‹ค.

Multiple clicks stream

ํšŒ์ƒ‰ ์ƒ์ž๋Š” ํ•œ ์ŠคํŠธ๋ฆผ์„ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊พธ๋Š” ํ•จ์ˆ˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ฒซ์งธ๋กœ,buffer(clickStream.throttle(250ms))์€, 250ms์˜ "์ด๋ฒคํŠธ ์นจ๋ฌต"์ด ๋ฐœ์ƒํ•˜๋ฉด ํด๋ฆญ์„ ๋ฆฌ์ŠคํŠธ์— ๋ˆ„์ ์‹œํ‚ต๋‹ˆ๋‹ค. ๋‹น์žฅ ๋„ˆ๋ฌด ์ž์„ธํ•˜๊ฒŒ ๋‹ค ์•Œ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๊ทธ์ € Reactive์˜ ์‹œ์—ฐ์„ ํ•ด๋ณด๊ณ  ์žˆ๋Š” ๊ฑฐ๋‹ˆ๊น์š”. ๊ทธ ๊ฒฐ๊ณผ๋Š”, ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์ŠคํŠธ๋ฆผ์— map()์„ ์ ์šฉ์‹œ์ผœ์„œ, ๊ฐ๊ฐ์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ทธ ๋ฆฌ์ŠคํŠธ์˜ ๊ธธ์ด๋ฅผ ๊ฐ€์ง„ ์ •์ˆ˜ ๊ฐ’์œผ๋กœ mapping ์‹œํ‚ฌ๊ฒ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ, ์šฐ๋ฆฌ๋Š” filter(x >= 2) ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์„œ ์ •์ˆ˜ 1์„ ๋ฌด์‹œํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๊ฒŒ ๋‹ค์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์„ธ ๋‹จ๊ณ„์˜ ์ž‘์—…์ด ์ „๋ถ€์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๊ณ ๋‚˜๋ฉด ๋งˆ์ง€๋ง‰ ์ŠคํŠธ๋ฆผ์— subscribe๋ฅผ ํ•จ์œผ๋กœ์จ(์ŠคํŠธ๋ฆผ์„ listenํ•˜๋Š”๊ฒ๋‹ˆ๋‹ค), ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ ๋ฐ˜์‘ํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋ถ€๋”” ๋‹น์‹ ์ด ์ด๋Ÿฐ ์ ‘๊ทผ์˜ ์•„๋ฆ„๋‹ค์›€์„ ์ฆ๊ฒผ์œผ๋ฉด ํ•˜๋„ค์š”. ์ด ์˜ˆ์ œ๋Š” ๋น™์‚ฐ์˜ ์ผ๊ฐ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๋˜‘๊ฐ™์€ ์ž‘์—…์„ ์„œ๋กœ ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์ŠคํŠธ๋ฆผ์— ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์ž๋ฉด, API ์‘๋‹ต ์ŠคํŠธ๋ฆผ ๋ง์ด์ง€์š”. ๋˜ ํ•œํŽธ์œผ๋ก , ๊ทธ ์™ธ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋งŽ์€ ํ•จ์ˆ˜๋“ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

"์™œ ๋‚ด๊ฐ€ Reactive Programming์„ ๋„์ž…ํ•  ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋‚˜์š”?"

Reactive Programming์€ ์ฝ”๋“œ์˜ ์ถ”์ƒํ™” ๋ ˆ๋ฒจ์„ ๋Œ์–ด์˜ฌ๋ ค์คŒ์œผ๋กœ์จ, ๋‹น์‹ ์œผ๋กœ ํ•˜์—ฌ๊ธˆ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ทœ์ •ํ•˜๋Š” ์ด๋ฒคํŠธ๋“ค ๊ฐ„์˜ ์ƒํ˜ธ ๊ด€๊ณ„์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ๋”์ด์ƒ ์„ธ๋ถ€ ๊ตฌํ˜„์„ ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜ ๊ณ„์† ๋งŒ์ง€์ž‘๊ฑฐ๋ฆฌ๊ณ  ์žˆ์„ ํ•„์š”๊ฐ€ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Reactive Programming ์œผ๋กœ ์ง  ์ฝ”๋“œ๋Š” ๋งค์šฐ ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์žฅ์ ์€, ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ๋“ค์ด ๋งŽ์€ UI ์ด๋ฒคํŠธ๋“ค๊ณผ ์—ฐ๊ณ„ํ•˜์—ฌ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ  ์žˆ๋Š” ์ตœ๊ทผ์˜ ์›น์•ฑ์ด๋‚˜ ๋ชจ๋ฐ”์ผ์•ฑ๋“ค์—๊ฒŒ ๋šœ๋ ทํ•˜๊ฒŒ ๋„๋“œ๋ผ์ง‘๋‹ˆ๋‹ค. 10๋…„์ „์—๋Š”, ์›น ํŽ˜์ด์ง€๋“ค์˜ ์ƒํ˜ธ์ž‘์šฉ์ด ๋‹จ์ˆœํ•˜๊ฒŒ ๊ธด ํผ์„ ๋ฐฑ์—”๋“œ์— ๋ณด๋‚ด๊ณ  ํ”„๋ก ํŠธ์—”๋“œ์—์„  ๋‹จ์ˆœํ•œ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒŒ ์ „๋ถ€์˜€์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง€๊ธˆ์˜ ์•ฑ๋“ค์€ ๋”์šฑ ์ง„ํ™”ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ ํผ ํ•„๋“œ์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋ฉด ๋ฐฑ์—”๋“œ์—์„œ ์ž๋™์œผ๋กœ ์ €์žฅ์ด ๋˜์–ด๋ฒ„๋ฆฌ์ฃ . ์–ด๋–ค ์ปจํ…์ธ ์— "์ข‹์•„์š”"๋ฅผ ๋ˆ„๋ฅด๋ฉด, ๋‹ค๋ฅธ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” ์œ ์ €์—๊ฒŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ˜์˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์˜ ์•ฑ๋“ค์€ ๋†’์€ ์ˆ˜์ค€์œผ๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ƒํ˜ธ์ž‘์šฉ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ๋“ค์ด ๋„˜์ณ๋‚˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์„ ์ ์ ˆํ•˜๊ฒŒ ๋‹ค๋ฃฐ ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, Reactive Programming์€ ๋ฐ”๋กœ ๊ทธ ํ•ด๋‹ต์ž…๋‹ˆ๋‹ค.

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด์„œ Reactive Programming ์œผ๋กœ ์ƒ๊ฐํ•ด๋ณด๊ธฐ

์ด๋ฒˆ์—” ์‹ค์ œ์ ์ธ ๊ฒƒ๋“ค์„ ๋‹ค๋ค„๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. Reactive Programming ๋ฐฉ์‹์œผ๋กœ ์ƒ๊ฐํ•˜๊ธฐ ์œ„ํ•œ ํ˜„์‹ค ์„ธ๊ณ„ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. ์–ต์ง€๋กœ ๋งŒ๋“  ์˜ˆ์‹œ๋„ ์•„๋‹ˆ๊ณ , ๋ฐ˜์ฏค ์„ค๋ช…๋œ ์ปจ์…‰๋„ ์•„๋‹™๋‹ˆ๋‹ค. ์ด ํŠœํ† ๋ฆฌ์–ผ์ด ๋๋‚ ๋•Œ์ฏค์ด๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ๊ฐ๊ฐ์˜ ๊ฒƒ๋“ค์„ ์™œ ํ–ˆ๋Š”์ง€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ๊ณผ ๋™์‹œ์—, ์ง„์งœ ํ•จ์ˆ˜ํ˜• ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

์ €๋Š” JavaScript์™€ RxJS ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ  ๋•Œ๋ฌธ์— ํ•™์Šต ๋„๊ตฌ๋กœ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. JavaScript๋Š” ์ง€๊ธˆ ์‹œ์ ์—์„œ ๊ฐ€์žฅ ๋„๋ฆฌ ์•Œ๋ ค์ง„ ์–ธ์–ด ์ค‘ ํ•˜๋‚˜์ด๊ณ , Rx* library family๋Š” ๋งŽ์€ ์–ธ์–ด์™€ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. (.NET, Java, Scala, Clojure, JavaScript, Ruby, Python, C++, Objective-C/Cocoa, Groovy, etc). ๊ทธ๋ž˜์„œ, ๋‹น์‹ ์ด ์–ด๋–ค ๋„๊ตฌ๋ฅผ ์“ฐ๋˜ ๊ฐ„์—, ์ด ํŠœํ† ๋ฆฌ์–ผ์„ ๋”ฐ๋ผํ•  ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

"Who to follow" ์ถ”์ฒœ ๋ฐ•์Šค ๊ตฌํ˜„ํ•˜๊ธฐ

ํŠธ์œ„ํ„ฐ์— ๋ณด๋ฉด, ๋‹น์‹ ์ด ํŒ”๋กœ์šฐ ํ•  ๋งŒํ•œ ๋‹ค๋ฅธ ๊ณ„์ •์„ ์ถ”์ฒœํ•ด์ฃผ๋Š” UI ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Twitter Who to follow suggestions box

์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋ฐฉํ•ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค:

  • ํ™”๋ฉด์ด ๊ฐœ์‹œ๋˜๋ฉด, API๋กœ๋ถ€ํ„ฐ ๊ณ„์ • ์ •๋ณด๋ฅผ ๋กœ๋“œํ•˜๊ณ , 3๊ฐœ๋ฅผ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.
  • "Refresh"๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ, ์ƒˆ๋กœ์šด 3๊ฐœ์˜ ๊ณ„์ •์„ ๋กœ๋“œํ•˜๊ณ  3ํ–‰์œผ๋กœ ๋„ฃ์Šต๋‹ˆ๋‹ค.
  • ๊ณ„์ • ํ–‰์—์„œ 'x'๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ํ•ด๋‹น ๊ณ„์ •๋งŒ ๋น„์›Œ๋‚ด๊ณ  ์ƒˆ๋กœ์šด ๊ฒƒ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
  • ๊ฐ ํ–‰์€ ๊ณ„์ •์˜ ์•„ํŒŒํƒ€์™€ ๊ทธ๋“ค์˜ ํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐ๋œ ๋งํฌ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

๊ทธ ์™ธ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ๊ธฐ๋Šฅ๊ณผ ๋ฒ„ํŠผ๋“ค์€ ๋ฐฐ์ œํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ํŠธ์œ„ํ„ฐ๊ฐ€ ์ตœ๊ทผ unauthrorized public์—๊ฒŒ API๋ฅผ ๋‹ซ์•„๋ฒ„๋ ธ๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด๋‹น UI๋ฅผ Github ์—์„œ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒ๋‹ˆ๋‹ค. ์ž์„ธํ•œ๊ฑด Github API for getting users ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์™„์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ ์‹ถ์œผ์‹œ๋ฉด http://jsfiddle.net/staltz/8jFJH/48/ ์— ๊ฐ€์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์š”์ฒญ๊ณผ ์‘๋‹ต(Request and response)

์ด ๋ฌธ์ œ๋ฅผ Rx๋กœ ์–ด๋–ป๊ฒŒ ์ ‘๊ทผํ•  ๊ฒƒ์ธ๊ฐ€? ๊ธ€์Ž„, ์‹œ์ž‘ํ•˜๊ธฐ์— ์•ž์„œ์„œ, (๊ฑฐ์˜) ๋ชจ๋“ ๊ฒƒ์€ ์ŠคํŠธ๋ฆผ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ๋ผ๋Š” Rx์˜ ์ฃผ๋ฌธ์„ ๋จผ์ € ์Š์–ด๋ณธ๋‹ค์Œ, ๋งจ ์ฒ˜์Œ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. "ํ™”๋ฉด์ด ๊ฐœ์‹œ๋˜๋ฉด, API๋กœ๋ถ€ํ„ฐ 3๊ฐœ์˜ ๊ณ„์ •์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค". ์—ฌ๊ธฐ์„  ๋ณ„๋กœ ํŠน๋ณ„ํ•œ๊ฒŒ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฑด ๋‹จ์ˆœํžˆ (1)์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  (2)์‘๋‹ต์„ ๋ฐ›๊ณ  (3)์‘๋‹ต์„ ๋ Œ๋”๋ง ํ•˜๋ฉด ๋˜๋Š”๊ฑฐ๋‹ˆ๊น์š”. ๊ทธ๋ ‡๋‹ค๋ฉด, ์ข€ ๋” ๋‚˜์•„๊ฐ€์„œ, ์šฐ๋ฆฌ์˜ ์š”์ฒญ์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ํ‘œํ˜„ ํ•ด๋ณด๋„๋ก ํ•ฉ์‹œ๋‹ค. ์ฒ˜์Œ์—” ๋„ˆ๋ฌด ์˜ค๋ฒ„ํ•˜๋Š”๊ฒƒ์ฒ˜๋Ÿผ ๋А๊ปด์งˆ์ˆ˜๋„ ์žˆ์„๊ฑฐ์—์š”. ํ•˜์ง€๋งŒ ์šฐ๋ฆฐ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•  ํ•„์š”๊ฐ€ ์žˆ์–ด์š”. ๊ทธ๋ ‡์ฃ ?

ํ™”๋ฉด์ด ๊ฐœ์‹œ๋˜๋ฉด, ์šฐ๋ฆฌ๋Š” ํ•˜๋‚˜์˜ ์š”์ฒญ๋งŒ ์‹คํ–‰ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ชจ๋ธ๋งํ•˜๋ฉด, ํ•œ๊ฐœ์˜ ๊ฐ’์ด ๋ฐœ์ƒํ•˜๋Š” ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”. ์ฐจํ›„์—๋Š” ๋งŽ์€ ์š”์ฒญ์ด ๋ฐœ์ƒํ• ์ˆ˜๋„ ์žˆ๊ฒ ์ง€๋งŒ, ์ผ๋‹จ ์ง€๊ธˆ์€, ํ•œ๊ฐœ๋งŒ ์ƒ๊ฐํ•ฉ์‹œ๋‹ค.

--a------|->

Where a is the string 'https://api.github.com/users'

์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์š”์ฒญํ•˜๊ณ ์ž ํ•˜๋Š” URL์˜ ์ŠคํŠธ๋ฆผ์ž…๋‹ˆ๋‹ค. ์š”์ฒญ ์ด๋ฒคํŠธ๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ๋‘๊ฐ€์ง€๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค : ์–ธ์ œ, ๊ทธ๋ฆฌ๊ณ  ๋ฌด์—‡์„. (when and what). ์š”์ฒญ์ด ์‹คํ–‰๋˜์–ด์ง€๋Š” "๋•Œ"๋Š”, ์š”์ฒญ์ด ๋ฐœ์ƒํ•œ ๋•Œ์ž…๋‹ˆ๋‹ค. ์š”์ฒญํ•˜๋Š” "๊ฒƒ"์€ ๋ฐœ์ƒํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ฆ‰ URL์„ ๋‹ด๊ณ ์žˆ๋Š” string์ด์ฃ .

๋‹จ์ผ ๊ฐ’์„ ๋‹ด๊ณ  ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ Rx*์—์„œ ๋งค์šฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์„ ์ผ์ปซ๋Š” ๊ณต์‹์ ์ธ ์šฉ์–ด๋Š” "Observable" ์ž…๋‹ˆ๋‹ค. ๊ด€์ฐฐ๋  ์ˆ˜ ์žˆ๋‹ค, ์ด๊ฑฐ์ฃ  ๋ญ. ๊ทผ๋ฐ ์ €๋Š” ์ข€ ๋ฉ์ฒญํ•œ ์ž‘๋ช…์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ๋ƒฅ ์ŠคํŠธ๋ฆผ ์ด๋ผ๊ณ  ๊ณ„์† ๋ถ€๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

var requestStream = Rx.Observable.just('https://api.github.com/users');

๊ทธ๋Ÿฐ๋ฐ ์ง€๊ธˆ ์ €๊ฒƒ์€, ํ•œ string์„ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๋Ÿฐ ์ž‘์—…๋„ ํ•˜์ง€ ์•Š๊ณ  ์žˆ์–ด์š”. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š”, ์ € ๊ฐ’์ด ๋ฐœ์ƒ(emit)ํ• ๋•Œ ๋ฌด์Šจ ์ผ์ด ์ƒ๊ฒจ๋‚˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ŠคํŠธ๋ฆผ์— subscribing์„ ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

requestStream.subscribe(function(requestUrl) {
  // execute the request
  jQuery.getJSON(requestUrl, function(responseData) {
    // ...
  });
}

์—ฌ๊ธฐ์„œ ์ฃผ๋ชฉํ•  ์ ์€ ์š”์ฒญ ์ž‘์—…์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” jQuery Ajax callback์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. (๋‹น์‹ ์ด ์‚ฌ์ „์ง€์‹์„ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค) ๊ทธ๋Ÿฐ๋ฐ ์ž ๊น, Rx๋Š” ๋น„๋™๊ธฐ์ ์ธ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ๋‹ค๋ฃฌ๋‹ค๋ฉด์„œ์š”? ์ € ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด ๋ฏธ๋ž˜ ์–ด๋А ์‹œ์ ์— ๋„์ฐฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์ด ๋  ์ˆœ ์—†๋‚˜์š”? ๊ฐœ๋…์ ์œผ๋กœ, ๋‹น์—ฐํžˆ ๊ทธ๋ ‡๊ฒŒ ๋ณด์ด๋Š”๊ตฐ์š”. ๊ทธ๋Ÿผ ํ•œ๋ฒˆ ์‹œ๋„ํ•ด๋ด…์‹œ๋‹ค.

requestStream.subscribe(function(requestUrl) {
  // execute the request
  var responseStream = Rx.Observable.create(function (observer) {
    jQuery.getJSON(requestUrl)
    .done(function(response) { observer.onNext(response); })
    .fail(function(jqXHR, status, error) { observer.onError(error); })
    .always(function() { observer.onCompleted(); });
  });
  
  responseStream.subscribe(function(response) {
    // do something with the response
  });
}

Rx.Observable.create()์ด ํ•˜๋Š” ์ผ์€, observer์—๊ฒŒ (observer์˜ ๋‹ค๋ฅธ ๋ง์€ "subscriber" ์ž…๋‹ˆ๋‹ค.) ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ(onNext()), ์—๋Ÿฌ ์ด๋ฒคํŠธ(onError()), ์™„๋ฃŒ ์ด๋ฒคํŠธ(onCompleted())๊ฐ€ ์–ธ์ œ ๋ฐœ์ƒํ•˜๋Š”์ง€๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์•Œ๋ ค์คŒ์œผ๋กœ์จ ์ปค์Šคํ…€ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ํ•œ๊ฑด, jQuery Ajax Promise๋ฅผ ๋‹จ์ˆœํžˆ Wrap ํ•œ๊ฑฐ์—์š”. ์ž ๊น, ๊ทธ๊ฑด ๊ณง Promise๊ฐ€ Observableํ•˜๋‹จ ์†Œ๋ฆฌ ์•„๋‹ˆ์š”?

ย  ย  ย  ย  ย 

Amazed

๋งž์Šต๋‹ˆ๋‹ค.

Observable์€ Promiss++ ์ž…๋‹ˆ๋‹ค. Rx์—์„  'var stream = Rx.Observable.fromPromise(promise)' ๋ฅผ ํ†ตํ•ด์„œ Promise๋ฅผ ์†์‰ฝ๊ฒŒ Observable๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑธ ํ•œ๋ฒˆ ์จ๋ณด๋„๋ก ํ•˜์ฃ . ๋”ฑ ํ•œ๊ฐ€์ง€ ์ฐจ์ด๋Š”, Observable์ด Promises/A+ ๋ฅผ ๋”ฐ๋ฅด๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋Š”๊ฒƒ ๋ฟ์ž…๋‹ˆ๋‹ค๋งŒ, ๊ฐœ๋…์ƒ์œผ๋กœ ์ถฉ๋Œํ•˜์ง„ ์•Š์Šต๋‹ˆ๋‹ค. Promise๋Š” ๋‹จ์ผ ๊ฐ’๋งŒ ๋ฐœ์ƒ์‹œํ‚ค๋Š” Observable์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. Rx ์ŠคํŠธ๋ฆผ์€ ๋‹ค์–‘ํ•œ ๋ฆฌํ„ด ๊ฐ’์„ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Promises๋ฅผ ๋„˜์–ด์„ฐ์Šต๋‹ˆ๋‹ค.

๊ฝค ํ›Œ๋ฅญํ•˜๋„ค์š”. ๊ทธ๋ฆฌ๊ณ  Observable์ด ์–ด๋–ป๊ฒŒ ์ตœ์†Œํ•œ Promises ๋งŒํผ ์œ ์šฉํ•œ์ง€ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š”๊ตฐ์š”. ๋งŒ์•ฝ ๋‹น์‹ ์ด Promises์˜ ์„ ์ „ ๋ฌธ๊ตฌ๋ฅผ ๋ฏฟ๋Š”๋‹ค๋ฉด, Rx Observables ๋กœ๋Š” ์–ด๋–ค๊ฒŒ ๊ฐ€๋Šฅํ•œ์ง€ ์ง€์ผœ๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์šฐ๋ฆฌ ์˜ˆ์ œ๋กœ ๋Œ์•„์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ˆˆ์น˜๊ฐ€ ์ข€ ๋น ๋ฅด๋‹ค๋ฉด ์•Œ์•„์ฑ„์…จ๊ฒ ์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ๋‹ค๋ฅธ subscribe()์•ˆ์—์„œ ์ฝœ ๋˜๊ณ  ์žˆ๋Š” ํ•œ subscribe()๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ์ง€์˜ฅ์˜ ๋‚Œ์ƒˆ๊ฐ€ ๋‚˜๋Š”๊ตฐ์š”. ๋˜ํ•œ, responseStream์˜ ์ƒ์„ฑ์ด requestStream์—๊ฒŒ ์˜์กด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์•Œ๋‹ค์‹œํ”ผ, Rx๋Š” ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์„ ๋ณ„๊ฐœ์˜ ๊ฒƒ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ณ€ํ˜•ํ•˜๋Š” ๋‹จ์ˆœํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์—ฌ๊ธฐ์„œ๋„ ๊ทธ๋ ‡๊ฒŒ ๋”ฐ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์ด ๊ผญ ์•Œ์•„์•ผํ•˜๋Š” ํ•จ์ˆ˜ ์ค‘ ํ•˜๋‚˜๋Š” map(f) ์ž…๋‹ˆ๋‹ค. ์ด๊ฑด ์ŠคํŠธ๋ฆผ A์˜ ๊ฐ ๊ฐ’์„ ์ทจํ•œ ๋‹ค์Œ, ๊ฐ ๊ฐ’๋“ค์—๊ฒŒ f()๋ฅผ ์ ์šฉ์‹œํ‚ค๊ณ , ์ƒ์„ฑ๋œ ๊ฐ’์„ ์ŠคํŠธ๋ฆผB์— ์ง‘์–ด๋„ฃ๋Š” ์ผ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ทธ๊ฑธ ์ด ์š”์ฒญ, ์‘๋‹ต ์ŠคํŠธ๋ฆผ์— ์ ์šฉ์‹œํ‚จ๋‹ค๋ฉด, ์šฐ๋ฆฌ๋Š” ์š”์ฒญ URL์„ ์‘๋‹ต (์ŠคํŠธ๋ฆผ์˜ ํ˜•ํƒœ๋ฅผ ํ•˜๊ณ  ์žˆ๋Š”) Promises์— map ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var responseMetastream = requestStream
  .map(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

์ด๋กœ์จ ์šฐ๋ฆฌ๋Š”, "metastream"์ด๋ผ๊ณ  ํ•˜๋Š” ๊ดด๋ฌผ์„ ๋งŒ๋“ค์–ด๋ƒˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑด ์ŠคํŠธ๋ฆผ์˜ ์ŠคํŠธ๋ฆผ์ž…๋‹ˆ๋‹ค. ๊ฒ๋จน์ง€๋งˆ์„ธ์š”! ๋ฉ”ํƒ€์ŠคํŠธ๋ฆผ์€ ๊ฐ๊ฐ์˜ ๋ฐœ์ƒ๋œ ๊ฐ’์ด(each emitted value) ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์— ์žˆ๋Š” ๊ฒƒ ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด๊ฑด ๋งˆ์น˜ ํฌ์ธํ„ฐ ์ฒ˜๋Ÿผ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ๋ฐœ์ƒ๋œ ๊ฐ’(each emitted value)๋Š” ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ์˜ ํฌ์ธํ„ฐ ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ์˜ˆ์ œ์—์„ , ๊ฐ๊ฐ์˜ ์š”์ฒญ URL์ด ์—ฐ๊ด€๋œ ์‘๋‹ต์„ ๊ฐ–๊ณ  ์žˆ๋Š” Promise ์ŠคํŠธ๋ฆผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ๋กœ ๋งคํ•‘๋œ ๊ฒƒ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (each request URL is mapped to a pointer to the promise stream containing the corresponding response.)

Response metastream

์‘๋‹ต์— ๋Œ€ํ•œ ๋ฉ”ํƒ€์ŠคํŠธ๋ฆผ์€ ๊ฝค ๋ณต์žกํ•ด๋ณด์ด๋Š”๋ฐ๋‹ค๊ฐ€, ๋ณ„๋กœ ๋„์›€๋„ ์•ˆ ๋˜์–ด ๋ณด์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ๋ƒฅ ๋‹จ์ˆœํ•œ ์‘๋‹ต์„ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ์„ ์›ํ•  ๋ฟ์ด์—์š”. ์ด ์ŠคํŠธ๋ฆผ์€ JSON ๊ฐ์ฒด๋ฅผ ๋‹ด๊ณ ์žˆ๋Š” Promise๊ฐ€ ์•„๋‹Œ, ๊ทธ๋ƒฅ JSON object ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ๋œ๋‹ค๊ตฌ์š”. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ Flatmap ์„ ๋งŒ๋‚  ์ฐจ๋ก€๊ฐ€ ๋˜์—ˆ๊ตฐ์š”. Flamtmap์€ ๋ฉ”ํƒ€์ŠคํŠธ๋ฆผ์„ ํ‰๋ฉดํ™”(flatten)์‹œํ‚ค๋Š” map()์˜ ํ•œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜์ž๋ฉด, ๊ฐ€์ง€(branch)์ŠคํŠธ๋ฆผ์—์„œ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ชจ๋“  ๊ฒƒ๋“ค์„, ์ค„๊ธฐ(trunk)์ŠคํŠธ๋ฆผ์—์„œ ๋ฐœ์ƒ์‹œํ‚ค๋„๋ก ํ•˜๋Š”๊ฑฐ์ฃ . ๋ฉ”ํƒ€์ŠคํŠธ๋ฆผ์€ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ๊ณ , flatmap์ด ๊ทธ๊ฑธ ๊ณ ์นœ๋‹ค๋Š” ๊ฒƒ์€ ๋”๋”์šฑ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฑด ๋‹จ์ง€ Rx์˜ ๋น„๋™๊ธฐ ์‘๋‹ต์„ ๋‹ค๋ฃจ๋Š” ๋„๊ตฌ์ผ ๋ฟ์ด์—์š”.

var responseStream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

Response stream

์ข‹๋„ค์š”. ๊ฒŒ๋‹ค๊ฐ€ ์‘๋‹ต ์ŠคํŠธ๋ฆผ์ด ์š”์ฒญ ์ŠคํŠธ๋ฆผ์— ๋’ค๋”ฐ๋ผ์„œ ์ •์˜๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋งŒ์•ฝ ์ถ”ํ›„์— ์š”์ฒญ ์ŠคํŠธ๋ฆผ์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ๋” ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, ์•„๋ž˜ ๋ณด์ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์—ฐ๊ด€๋˜๋Š”(corresponding) ์‘๋‹ต ์ด๋ฒคํŠธ๊ฐ€ ์‘๋‹ต ์ŠคํŠธ๋ฆผ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

requestStream:  --a-----b--c------------|->
responseStream: -----A--------B-----C---|->

(lowercase is a request, uppercase is its response)

์ด์ œ ๋“œ๋””์–ด ์‘๋‹ต ์ŠคํŠธ๋ฆผ์„ ๊ฐ–๊ฒŒ ๋์œผ๋‹ˆ, ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋์Šต๋‹ˆ๋‹ค..

responseStream.subscribe(function(response) {
  // render `response` to the DOM however you wish
});

์ง€๊ธˆ๊นŒ์ง€์˜ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ํ•ฉ์น˜๋ฉด, ์ด๋ ‡๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

var requestStream = Rx.Observable.just('https://api.github.com/users');

var responseStream = requestStream
  .flatMap(function(requestUrl) {
    return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));
  });

responseStream.subscribe(function(response) {
  // render `response` to the DOM however you wish
});

์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ

์ด๋Ÿฐ, ๊นœ๋ฐ•ํ•œ๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต์— ์žˆ๋Š” JSON์—๋Š” ์œ ์ € 100๋ช…์˜ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋‹ด๊ฒจ์žˆ์–ด์š”. API๋Š” page size๊ฐ€ ์•„๋‹Œ, page offset๋งŒ ๋ช…์‹œํ• ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋•๋ถ„์— ์šฐ๋ฆฌ๋Š” 3๊ฐœ์˜ ๋ฐ์ดํ„ฐ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด 97๊ฐœ๋ฅผ ๋‚ญ๋น„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์ง€๊ธˆ ๋‹น์žฅ์€ ๋ฌด์‹œํ•  ๊ฑฐ์ง€๋งŒ, ์ฐจํ›„์— ์‘๋‹ต์„ ์–ด๋–ป๊ฒŒ ์บ์‹ฑํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ฃ .

์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค, ์š”์ฒญ ์ŠคํŠธ๋ฆผ์€ ์ƒˆ๋กœ์šด URL์„ ๋ฐœ์ƒ์‹œํ‚ค๊ณ , ์ดํ›„์— ์šฐ๋ฆฌ๋Š” ์ƒˆ๋กœ์šด ์‘๋‹ต์„ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋‘๊ฐ€์ง€๋ฅผ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์˜ ํด๋ฆญ ์ด๋ฒคํŠธ๋กœ ์ด๋ฃจ์–ด์ง„ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๊ณผ (์ฃผ๋ฌธ: ๋ชจ๋“ ๊ฒƒ์€ ์ŠคํŠธ๋ฆผ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.), ์š”์ฒญ ์ŠคํŠธ๋ฆผ์ด ์ƒˆ๋กœ๊ณ ์นจ ํด๋ฆญ ์ŠคํŠธ๋ฆผ์— ์˜์กดํ•˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ณ ๋ง™๊ฒŒ๋„, RxJS๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ Observables๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋„๊ตฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

var refreshButton = document.querySelector('.refresh');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');

์ƒˆ๋กœ๊ณ ์นจ ํด๋ฆญ ์ด๋ฒคํŠธ ์ž์ฒด๋กœ๋Š” API URL์„ ๋‹ด์•„๋‚ด์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๋Š” ๊ฐ๊ฐ์˜ ํด๋ฆญ์„ ์‹ค์ œ URL๋กœ ๋งคํ•‘ ์‹œ์ผœ์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจ ํด๋ฆญ ์ŠคํŠธ๋ฆผ์„ ๋žœ๋คํ•œ offset ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” API endpoint ๋กœ ๋งคํ•‘ ๋˜๊ฒŒ ํ•œ ํ›„, ๊ธฐ์กด์— ๋งŒ๋“  ์š”์ฒญ ์ŠคํŠธ๋ฆผ์„ ์ด๊ฒƒ์œผ๋กœ ๋ฐ”๊ฟ”๋ด…์‹œ๋‹ค.

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

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

์šฐ๋ฆฌ๋Š” ๊ฐ๊ฐ์˜ ์ผ€์ด์Šค์— ๋Œ€ํ•ด์„œ ๋ถ„๋ฆฌ๋œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์ด๋ฏธ ์•Œ๊ณ ์žˆ์Šต๋‹ˆ๋‹ค.

var requestOnRefreshStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });
  
var startupRequestStream = Rx.Observable.just('https://api.github.com/users');

๊ทธ๋Ÿฐ๋ฐ ์ด ๋‘๊ฐœ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜๋กœ "ํ•ฉ์น "์ˆ˜ ์žˆ์„๊นŒ์š”? merge() ๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ์ด merge๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

stream A: ---a--------e-----o----->
stream B: -----B---C-----D-------->
          vvvvvvvvv merge vvvvvvvvv
          ---a-B---C--e--D--o----->

์•„์ฃผ ์‰ฝ๊ตฐ์š”.

var requestOnRefreshStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });
  
var startupRequestStream = Rx.Observable.just('https://api.github.com/users');

var requestStream = Rx.Observable.merge(
  requestOnRefreshStream, startupRequestStream
);

ํ•œํŽธ, ์ค‘๊ฐ„๋‹จ๊ณ„ ์ŠคํŠธ๋ฆผ์„ ์ œ๊ฑฐํ•˜๋ ค๋ฉด, ์ด๋ ‡๊ฒŒ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  })
  .merge(Rx.Observable.just('https://api.github.com/users'));

๋” ์งง๊ฒŒ, ๋” ๊ฐ€๋…์„ฑ์„ ๋†’์—ฌ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

var requestStream = refreshClickStream
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  })
  .startWith('https://api.github.com/users');

startWith() ํ•จ์ˆ˜๋Š” ๋‹น์‹ ์ด ์ƒ๊ฐํ•˜๋Š” ๊ทธ ์ž‘์—…์„ ํ•ฉ๋‹ˆ๋‹ค. input ์ŠคํŠธ๋ฆผ์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒจ๋จน์—ˆ๋“ , startWith(x)๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ output ์ŠคํŠธ๋ฆผ์€, x๋ฅผ ์‹œ์ž‘ ๋‹จ๊ณ„์— ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

StartWith

์ด๋ถ€๋ถ„์„ ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค

atream A: ----c--------c----c---->
          vvvv map(return E) vvvvv
stream B: ----E--------E----E---->
          vvvv startWith( S ) vvvv
stream C: S---E--------E----E---->

c is click event
E is API Endpoint
S is Startup API Endpoint

์—ฌ๊ธฐ์„œ ์ œ๊ฐ€ map(return E) ๋ผ๊ณ  ์“ด ๋ถ€๋ถ„์„ ์ž˜ ๊ธฐ์–ตํ•ด๋‘์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. map(f)ํ•จ์ˆ˜ ์•ˆ์— ์žˆ๋Š” project function f๋Š” input ์ธ์ž๊ฐ€ ์—†๊ณ , API Endpoint๋งŒ ๋˜๋Œ๋ ค์ฃผ๊ณ  ์žˆ๊ธฐ ๋–„๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์•„์ง๋„ ์ถฉ๋ถ„ํžˆ DRY ํ•˜์งˆ ์•Š๊ตฐ์š”. (์ฃผ์„: ์ฝ”๋“œ๋ฅผ ๋œ ์ถ•์•ฝ์‹œ์ผฐ๋‹ค๋Š” ๋ง์ž…๋‹ˆ๋‹ค.) ๊ทธ ์ด์œ ๋Š” API endpoint ๋ฌธ์ž์—ด์ด ์—ฌ์ „ํžˆ ๋ฐ˜๋ณต๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒํ•˜๋ฉด ์ด๊ฑธ ๊ณ ์น  ์ˆ˜ ์žˆ์„๊นŒ์š”? startWith() ํ•จ์ˆ˜์— ์ฃผ๋ชฉํ•ด๋ด…์‹œ๋‹ค. ์ง€๊ธˆ์˜ startWith()ํ•จ์ˆ˜๋Š”, API Endpoint ์ŠคํŠธ๋ฆผ ์•ž์—, Startup API Endpoint๋ฅผ ๋ถ™์—ฌ์„œ ์ƒˆ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ฐœ์ƒ์„ ๋ฐ”๊ฟ”์„œ, refresh ํด๋ฆญ ์ŠคํŠธ๋ฆผ์ด ๊ฐœ์‹œ๋  ๋•Œ, ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์—ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ "emulate"์‹œ์ผœ๋ฒ„๋ฆฌ๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ฆ‰, startWith()ํ•จ์ˆ˜๋ฅผ refreshClickStream ๋’ค์— ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

var requestStream = refreshClickStream.startWith('startup-click')
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

์ด๊ฑธ ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์ด๋ ‡๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

atream A: ----c--------c----c---->
          vvvv startWith( s ) vvvvv
stream B: s---c--------c----c---->
          vvvv map(return E) vvvvv
stream C: E---E--------E----E---->
          
c is click event
s is startup event (in case of this, 'startup-click' string event)
E is API Endpoint

์•ž์„œ ์–ธ๊ธ‰ํ–ˆ๋‹ค์‹œํ”ผ, ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ๋œ map(f) ํ•จ์ˆ˜์˜ project function์ธ f๋Š”, input ์ธ์ž๋ฅผ ๋‹ค ๋ฌด์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์—, startWith() ํ•จ์ˆ˜์—์„œ ์ธ์ž๋กœ ๋“ค์–ด๊ฐ„ 'startup-click'์ด๋ผ๊ณ  ์“ด ๋ถ€๋ถ„์€ ์–ด๋–ค ๋ฌธ์ž์—ด์ด ์™€๋„ ์ƒ๊ด€ ์—†์Šต๋‹ˆ๋‹ค.

ํ›Œ๋ฅญํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์•„๊นŒ ์ œ๊ฐ€ "๋ฉ์ฒญํ•˜๊ณ , ์ž๋™ํ™” ํ…Œ์ŠคํŠธ๋ฅผ ์•ˆ๋งŒ๋“ค์–ด๋†”์„œ" ๋ผ๊ณ  ๋งํ–ˆ๋˜ ๋ถ€๋ถ„์— ์žˆ๋Š” ์ฝ”๋“œ์™€ ์ง€๊ธˆ ์ฝ”๋“œ๋ฅผ ๋น„๊ตํ•ด๋ณด๋ฉด, startWith()๊ฐ€ ๋‹ฌ๋ ค์žˆ๋Š”๊ฒƒ ๋นผ๊ณค ๋™์ผํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

(์ฃผ์„: ์›๋ฌธ์—์„œ startWith() ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๋ถ€๋ถ„์˜ ์„ค๋ช…์ด ๋‹ค์†Œ ๋ถ€์กฑํ•ด์„œ, ์›๋ฌธ์— ์žˆ๋Š” ๋Œ“๊ธ€๊ณผ ๋ ˆํผ๋Ÿฐ์Šค ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ œ๊ฐ€ ์„ค๋ช…์„ ์ข€ ๋ง๋ถ™์˜€์Šต๋‹ˆ๋‹ค.)

3๊ฐœ์˜ ์ถ”์ฒœ์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ชจ๋ธ๋งํ•˜๊ธฐ

์ง€๊ธˆ๊นŒ์ง€๋Š”, response ์ŠคํŠธ๋ฆผ์˜ subscribe()๋ฅผ ํ†ตํ•ด์„œ ์ƒ๊ฒจ๋‚œ suggestion UI ์š”์†Œ๋ฅผ ๋ Œ๋”๋ง ํ•˜๋Š” ๋ถ€๋ถ„๊นŒ์ง€๋งŒ ๋‹ค๋ฃจ๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์„ ์‚ดํŽด๋ณด์ž๋ฉด, ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ๋ฐ”๋กœ ์งํ›„์—๋Š” ๊ธฐ์กด์— ์žˆ๋˜ 3๊ฐœ์˜ ์ถ”์ฒœ์ด ์—†์–ด์ง€์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์ถ”์ฒœ์€ ์‘๋‹ต์ด ๋„์ฐฉํ•œ ์ดํ›„์—๋งŒ ์ƒ๊ฒจ๋‚˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. UI๋ฅผ ์ข€๋” ๋ณด๊ธฐ ์ข‹๊ฒŒํ•˜๊ธฐ ์œ„ํ•ด์„ , ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์ž๋งˆ์ž ๊ธฐ์กด์˜ ์ถ”์ฒœ์„ ์—†์• ์ฃผ๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

refreshClickStream.subscribe(function() {
  // clear the 3 suggestion DOM elements 
});

๊ทธ๋Ÿฐ๋ฐ ์ด๋Ÿฐ ๋ฐฉ์‹์€ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ๊ธฐ์กด์— ๋งŒ๋“  responseStream.subscribe() subscriber๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์œ„ ์ฝ”๋“œ๋ฅผ ๋„ฃ์œผ๋ฉด ์ถ”์ฒœ DOM์— ์˜ํ–ฅ์„ ๋ผ์น˜๋Š” 2๊ฐœ์˜ subscriber๋ฅผ ๊ฐ–๊ฒŒ ๋˜๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ด๊ฑด Separation of concerns ์™€๋Š” ๋‹ค๋ฅธ ๊ฒ๋‹ˆ๋‹ค. ์•„๊นŒ ๋ช‡๋ฒˆ ์–ธ๊ธ‰ํ–ˆ๋˜ Reactive์˜ ์ฃผ๋ฌธ์„ ๊ธฐ์–ตํ•˜๋‚˜์š”?

ย  ย  ย  ย 

Mantra

๊ทธ๋ ‡๋‹ค๋ฉด, ์ถ”์ฒœ์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ชจ๋ธ๋ง ํ•ด ๋ด…์‹œ๋‹ค. ์ด ์ŠคํŠธ๋ฆผ์ด ๋ฐœ์ƒ(emit)์‹œํ‚ค๋Š” ๊ฐ’๋“ค์€, ์ถ”์ฒœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” JSON ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” 3๊ฐœ์˜ ์ถ”์ฒœ์— ๋Œ€ํ•ด์„œ ๊ฐ๊ฐ ๋ถ„๋ฆฌํ•ด์„œ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค์–ด๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์ถ”์ฒœ1์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

var suggestion1Stream = responseStream
  .map(function(listUsers) {
    // get one random user from the list
    return listUsers[Math.floor(Math.random()*listUsers.length)];
  });

๋‚˜๋จธ์ง€ suggestion2Stream ๊ณผ suggestion3Stream ์€ suggestion1Stream์„ ๋‹จ์ง€ ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฑด ๋‹น์—ฐํžˆ DRYํ•˜์ง€ ์•Š์ง€๋งŒ, ์ด ํŠœํ† ๋ฆฌ์–ผ์„ ์ง„ํ–‰ํ• ๋•Œ ์˜ˆ์‹œ๋ฅผ ์ข€ ๋‹จ์ˆœํ•˜๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ์„๊ฒƒ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ง๋ถ™์—ฌ์„œ, ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ๋ฐ˜๋ณต์„ ์–ด๋–ป๊ฒŒ ํ”ผํ•˜๋Š”์ง€ ๊ณ ๋ฏผํ•˜๋Š”๊ฑด ์ข‹์€ ์—ฐ์Šต์ด ๋  ๊ฒƒ ๊ฐ™๋„ค์š”.

์ž ์ด๋ฒˆ์—”, responseStream์˜ subscribe()์—์„œ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๊ฒŒ ํ•˜์ง€ ๋ง๊ณ , ์ถ”์ฒœ ์ŠคํŠธ๋ฆผ์—์„œ ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๊ฒŒ ํ•ฉ์‹œ๋‹ค.

suggestion1Stream.subscribe(function(suggestion) {
  // render the 1st suggestion to the DOM
});

"์ƒˆ๋กœ๊ณ ์นจ ํ• ๋•Œ, ์ถ”์ฒœ์„ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค" ๋ถ€๋ถ„์œผ๋กœ ๋Œ์•„๊ฐ€๋ฉด, ์šฐ๋ฆฌ๋Š” ์ƒˆ๋กœ๊ณ ์นจ ํด๋ฆญ์„ null ์ถ”์ฒœ ๋ฐ์ดํ„ฐ๋กœ ๋งคํ•‘์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ณ , ์ด๊ฒƒ์„ suggesion1Stream์— ํ•ฉ์ณ๋ฒ„๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ง์ด์ฃ .

var suggestion1Stream = responseStream
  .map(function(listUsers) {
    // get one random user from the list
    return listUsers[Math.floor(Math.random()*listUsers.length)];
  })
  .merge(
    refreshClickStream.map(function(){ return null; })
  );

๊ทธ๋ฆฌ๊ณ  ๋ Œ๋”๋ง์„ ํ•  ๋•Œ, null์„ "๋ฐ์ดํ„ฐ ์—†์Œ"์œผ๋กœ ์ทจ๊ธ‰ํ•˜๋ฉด์„œ, UI ์š”์†Œ๋ฅผ ๊ฐ์ถฐ๋ฒ„๋ฆฌ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

suggestion1Stream.subscribe(function(suggestion) {
  if (suggestion === null) {
    // hide the first suggestion DOM element
  }
  else {
    // show the first suggestion DOM element
    // and render the data
  }
});

์ด ๊ณผ์ •๋“ค์„ ๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ ค๋ณด๋ฉด ์ด๋ ‡๊ฒŒ๋ฉ๋‹ˆ๋‹ค

refreshClickStream: ----------o--------o---->
     requestStream: -r--------r--------r---->
    responseStream: ----R---------R------R-->   
 suggestion1Stream: ----s-----N---s----N-s-->
 suggestion2Stream: ----q-----N---q----N-q-->
 suggestion3Stream: ----t-----N---t----N-t-->

 N stands for null

์ถ”๊ฐ€์ ์œผ๋กœ, ์šฐ๋ฆฌ๋Š” ์›นํŽ˜์ด์ง€ ์‹œ์ž‘๋‹จ๊ณ„์—์„œ "๋น„์–ด์žˆ๋Š”" ์ถ”์ฒœ์„ ๋ณด์—ฌ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฑด startWith(null) ์„ ์ถ”์ฒœ ์ŠคํŠธ๋ฆผ์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

var suggestion1Stream = responseStream
  .map(function(listUsers) {
    // get one random user from the list
    return listUsers[Math.floor(Math.random()*listUsers.length)];
  })
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);

๊ทธ๋Ÿฌ๋ฉด ์ด๋ ‡๊ฒŒ ๋˜๊ฒ ์ฃ 

refreshClickStream: ----------o---------o---->
     requestStream: -r--------r---------r---->
    responseStream: ----R----------R------R-->   
 suggestion1Stream: -N--s-----N----s----N-s-->
 suggestion2Stream: -N--q-----N----q----N-q-->
 suggestion3Stream: -N--t-----N----t----N-t-->

์ถ”์ฒœ ํ•œ๊ฐœ๋งŒ ๋‹ซ์•„๋ฒ„๋ฆฌ๊ณ , ์บ์‹œ๋œ ์‘๋‹ต์„ ์‚ฌ์šฉํ•˜๊ธฐ

์•„์ง ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ํ•œ๊ฐ€์ง€ ๊ธฐ๋Šฅ์ด ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์ถ”์ฒœ์€ 'x' ๋ฒ„ํŠผ์„ ๊ฐ–๊ณ  ์žˆ์–ด์•ผํ•˜๊ณ , ์ด ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ์„ ๋•Œ ๋‹ค๋ฅธ ์ถ”์ฒœ์„ ๋กœ๋“œํ•ด์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ผ๋‹จ ๋ณด์ž๋งˆ์ž ๋“œ๋Š” ์ƒ๊ฐ์€, 'x' ๋ฒ„ํŠผ์ด ํด๋ฆญ ๋˜์—ˆ์„ ๋•Œ ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋งŒ๋“ค๋ฉด ๋  ๊ฒƒ ๊ฐ™์•„ ๋ณด์ž…๋‹ˆ๋‹ค.

var close1Button = document.querySelector('.close1');
var close1ClickStream = Rx.Observable.fromEvent(close1Button, 'click');
// and the same for close2Button and close3Button

var requestStream = refreshClickStream.startWith('startup click')
  .merge(close1ClickStream) // we added this
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

์ด๊ฑด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒํ•˜๋ฉด, ๋ชจ๋“  ์ถ”์ฒœ์„ ๋น„์›Œ๋ฒ„๋ฆฌ๊ณ  ๋ชจ๋“  ์ถ”์ฒœ์„ ๋‹ค์‹œ ๋กœ๋“œํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. (์ฃผ์„: refresh ๋ฒ„ํŠผ ํด๋ฆญ ์ŠคํŠธ๋ฆผ๊ณผ close1 ๋ฒ„ํŠผ ํด๋ฆญ ์ŠคํŠธ๋ฆผ์ด ๋‹จ์ˆœํžˆ merge๋˜์—ˆ์œผ๋ฏ€๋กœ, ์ด ๋‘๊ฐœ์˜ ๋ฒ„ํŠผ ํด๋ฆญ์€ ๋™์ผํ•œ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค) ์ด๊ฑธ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ์š”, ์šฐ๋ฆฌ๋Š” ์ด์ „์˜ ์‘๋‹ต์„ ์žฌ์‚ฌ์šฉํ•˜๋ฉด์„œ ํ•ด๊ฒฐํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. API๊ฐ€ ์‘๋‹ตํ•œ ํŽ˜์ด์ง€ ํฌ๊ธฐ๋Š” 100๋ช…์˜ ์œ ์ €์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฐ ๊ทธ์ค‘์—์„œ 3๊ฐœ๋งŒ ํ•„์š”ํ•  ๋ฟ์ด์—์š”. ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ƒˆ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ์„ ๋‹ค์‹œ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†์–ด์š”.

์ด๋ฒˆ์—๋„, ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค. 'close1' ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ์ƒ๊ฒจ๋‚ฌ์„ ๋•Œ, ์šฐ๋ฆฌ๋Š” ์‘๋‹ต ์ŠคํŠธ๋ฆผ ์ค‘์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋ฐœ์ƒํ•œ ์‘๋‹ต์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ด ์‘๋‹ต์ด ๊ฐ–๊ณ  ์žˆ๋Š” ๋ฆฌ์ŠคํŠธ์—์„œ ํ•œ๋ช…์˜ ๋žœ๋คํ•œ ์œ ์ €๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ๋˜๋Š”๊ฒ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ์š”

    requestStream: --r--------------->
   responseStream: ------R----------->
close1ClickStream: ------------c----->
suggestion1Stream: ------s-----s----->

Rx*์—์„œ ์ด๋Ÿฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋กœ๋Š”, combineLatest ๋ผ๋Š” ์กฐํ•ฉ ํ•จ์ˆ˜(combinator function)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š”, ๋‘๊ฐœ์˜ ์ŠคํŠธ๋ฆผ์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์€ ๋’ค, ๊ฐ๊ฐ์˜ ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์ด ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฐœ์ƒ(emit)์‹œํ‚ฌ ๋•Œ, combineLastestํ•จ์ˆ˜๋Š” ๊ฐ๊ฐ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ€์žฅ ์ตœ๊ทผ ๊ฐ’์„ ์ด์šฉํ•ด์„œ ๋งŒ๋“  ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. ๊ฐ๊ฐ ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ€์žฅ ์ตœ๊ทผ ๊ฐ’์„ a, b๋ผ๊ณ  ํ•˜๊ณ , combineLatest์—์„œ ์ •์˜ํ•œ project function์„ f๋ผ๊ณ  ํ•˜๋ฉด, ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฐ’์€ c=f(a,b) ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์•„์š”

combineLatest

(์ฃผ์„: ์›๋ฌธ์— ๋‚˜์™€์žˆ๋Š” diagram์ด ์˜คํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ์–ด์„œ ๊ณต์‹ ๋ ˆํผ๋Ÿฐ์Šค์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค)

์šฐ๋ฆฌ๋Š” combineLatest()ํ•จ์ˆ˜๋ฅผ close1ClickStream๊ณผ responseStream์— ์ ์šฉ์‹œ์ผœ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. close1 ๋ฒ„ํŠผ์ด ํด๋ฆญ๋  ๋•Œ๋งˆ๋‹ค, ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐœ์ƒํ•œ ์‘๋‹ต์„ ์‚ฌ์šฉํ•ด์„œ suggestion1Stream์˜ ๋“ค์–ด๊ฐˆ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๊ฒ๋‹ˆ๋‹ค. ํ•œํŽธ, combineLatest()๋Š” ๋Œ€์นญ์ ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜์ž๋ฉด, responseStream์—์„œ ์ƒˆ๋กœ์šด ๊ฐ’์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด, close1 ํด๋ฆญ๊ณผ ์กฐํ•ฉ๋˜์–ด์„œ ์ƒˆ๋กœ์šด ์ถ”์ฒœ์„ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์†Œ๋ฆฌ์ฃ . ํฅ๋ฏธ๋กญ๊ตฐ์š”. ์ด๊ฑธ ์ž˜ ์ด์šฉํ•˜๋ฉด ์ด์ „์— ๋งŒ๋“  suggestion1Stream์„ ๋‹จ์ˆœํ™” ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ง์ด์ฃ .

var suggestion1Stream = close1ClickStream
  .combineLatest(responseStream,             
    function(click, listUsers) {
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);

์•„์ง ํ•œ๊ฐ€์ง€ ๋‚จ์€ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค. combineLatest()๋Š” ๋‘ input ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ๊ฒƒ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋‘ input ์ค‘์—์„œ ์•„์ง ์•„๋ฌด๊ฒƒ๋„ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์€ ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด, combineLatest() ๋Š” output ์ŠคํŠธ๋ฆผ์œผ๋กœ ์•„๋ฌด๋Ÿฐ ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ๋ชปํ•œ๋‹ค๋Š”๊ฑฐ์ฃ . ์œ„์˜ combineLatest() ์˜ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด๋ฉด, ์ฒซ๋ฒˆ์งธ input ์ŠคํŠธ๋ฆผ์ด a๋ฅผ ๋ฐœ์ƒํ–ˆ์„๋Œ€, ๋‘๋ฒˆ์งธ input์ŠคํŠธ๋ฆผ์€ ์•„๋ฌด ๊ฒƒ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋ผ์„œ, output์œผ๋กœ ๋‚˜์˜ค๋Š” ๊ฐ’์€ ์•„์ง ์—†์Šต๋‹ˆ๋‹ค. ๋‘๋ฒˆ์งธ input์ŠคํŠธ๋ฆผ์ด 1์„ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋น„๋กœ์†Œ output๊ฐ’์ธ a1์ด ๋‚˜์˜ค๊ฒŒ ๋˜์ฃ .

์ด๊ฑธ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ญ์‹œ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ, ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์„ ์จ๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค. ์›นํŽ˜์ด์ง€ ์‹œ์ž‘ ๋‹จ๊ณ„์—์„œ, 'close1' ๋ฒ„ํŠผ์ด ํด๋ฆญ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋งŒ๋“ค์–ด์ฃผ๋Š”๊ฑฐ์ฃ .

var suggestion1Stream = close1ClickStream.startWith('startup click') // we added this
  .combineLatest(responseStream,             
    function(click, listUsers) {l
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);

๋งˆ๋ฌด๋ฆฌ

๋‹ค ํ–ˆ์Šต๋‹ˆ๋‹ค. ์™„์„ฑ๋œ ์ฝ”๋“œ๋Š” ์ด๊ฒ๋‹ˆ๋‹ค.

var refreshButton = document.querySelector('.refresh');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');

var closeButton1 = document.querySelector('.close1');
var close1ClickStream = Rx.Observable.fromEvent(closeButton1, 'click');
// and the same logic for close2 and close3

var requestStream = refreshClickStream.startWith('startup click')
  .map(function() {
    var randomOffset = Math.floor(Math.random()*500);
    return 'https://api.github.com/users?since=' + randomOffset;
  });

var responseStream = requestStream
  .flatMap(function (requestUrl) {
    return Rx.Observable.fromPromise($.ajax({url: requestUrl}));
  });

var suggestion1Stream = close1ClickStream.startWith('startup click')
  .combineLatest(responseStream,             
    function(click, listUsers) {
      return listUsers[Math.floor(Math.random()*listUsers.length)];
    }
  )
  .merge(
    refreshClickStream.map(function(){ return null; })
  )
  .startWith(null);
// and the same logic for suggestion2Stream and suggestion3Stream

suggestion1Stream.subscribe(function(suggestion) {
  if (suggestion === null) {
    // hide the first suggestion DOM element
  }
  else {
    // show the first suggestion DOM element
    // and render the data
  }
});

** http://jsfiddle.net/staltz/8jFJH/48/** ์— ์˜ค์‹œ๋ฉด ์‹ค์ œ ๋™์ž‘ํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ € ์ฝ”๋“œ๋Š” ์งง์ง€๋งŒ ๊ฝค ๋งŽ์€ ๊ฒƒ์„ ํ•จ์ถ•ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ ์ ˆํ•˜๊ฒŒ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•œ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ๋“ค์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๊ณ , ์‹ฌ์ง€์–ด ์‘๋‹ต์„ ์บ์‹ฑ๋„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ์€ ์ฝ”๋“œ๋ฅผ ๋ช…๋ นํ˜•(imperative)๊ฐ€ ์•„๋‹ˆ๋ผ, ์„ ์–ธ์ (declarative)๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” sequence of instruction์„ ์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ด๊ฒŒ ๋ญ”์ง€ ๋งํ•˜๊ณ  ์žˆ์„ ๋ฟ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์šฐ๋ฆฌ๋Š” Rx๋กœ, ์ปดํ“จํ„ฐ์—๊ฒŒ, suggestion1Stream ์€ close1์ŠคํŠธ๋ฆผ๊ณผ, ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ์‘๋‹ต์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ์œ ์ € ํ•œ๋ช…์„ ์กฐํ•ฉํ•˜๋Š” ๊ฒƒ์ด๊ณ , ์ƒˆ๋กœ๊ณ ์นจ์ด๋‚˜ ์›นํŽ˜์ด์ง€๊ฐ€ ์‹œ์ž‘ํ–ˆ์„ ๋• null ์ด๋ผ๊ณ  ๋งํ•˜๊ณ  ๋งํ•˜๊ณ  ์žˆ๋Š”๊ฒ๋‹ˆ๋‹ค.

๋˜ํ•œ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ์ ์€, if, for, while๊ฐ™์€ control flow ์š”์†Œ์™€, JavaScript ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ•ญ์ƒ ๋“ค์–ด๊ฐ”๋˜ ์ฝœ๋ฐฑ ๊ตฌ๋ฌธ์ด ์ „ํ˜€ ๋ณด์ด์ง€ ์•Š๋Š” ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์œ„ ์ฝ”๋“œ์—์„œ subscribe()์— ์žˆ๋Š” if, else๋Š” filter()๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์—†์•จ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹น์‹ ์ด ์—ฐ์Šต์‚ผ์•„ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ฐ€ ๊ตฌํ˜„ํ•ด๋†“์ง„ ์•Š์•˜์Šต๋‹ˆ๋‹ค) Rx์—์„œ, ์šฐ๋ฆฌ๋Š” map, filter, scan, merge, combineLatest, startWith์™ธ์—๋„ ๋” ๋งŽ์€ ์ŠคํŠธ๋ฆผ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋žจ์˜ ํ๋ฆ„์„ ์ œ์–ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋„๊ตฌ๋“ค์€ ์ ์€ ์ฝ”๋“œ๋กœ๋„ ๋” ๋งŽ์€ ์ผ์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค„๊ฒ๋‹ˆ๋‹ค.

๋‹ค์Œ ํ• ์ผ

๋งŒ์•ฝ Reactive Programming์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ Rx*๊ฐ€ ๊ดœ์ฐฎ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ๋А๊ปด์ง„๋‹ค๋ฉด, ์‹œ๊ฐ„์„ ์ข€ ๋‚ด์–ด์„œ big list of functions ์— ์žˆ๋Š” ๋ณ€ํ˜•, ์กฐํ•ฉ, ๊ทธ๋ฆฌ๊ณ  ์ƒ์„ฑ๊ณผ ๊ด€๋ จ๋œ ๋ถ€๋ถ„์„ ์ฝ์–ด๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ํ•จ์ˆ˜๋“ค์„ ์ŠคํŠธ๋ฆผ ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, RxJava's very useful documentation with marble diagrams ์„ ๋ณด๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋ญ”๊ฐ€๋ฅผ ํ•˜๋‹ค ๋ง‰ํžŒ๋‹ค๋ฉด, ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋จผ์ € ๊ทธ๋ฆฐ ๋‹ค์Œ, ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์ƒ๊ฐํ•˜๊ณ , ์ญ‰ ๋‚˜์—ด๋œ ํ•จ์ˆ˜๋“ค์˜ ๋ชฉ๋ก์„ ์ณ๋‹ค๋ณด๋ฉด์„œ, ๋” ์ƒ๊ฐํ•ด๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ œ ๊ฒฝํ—˜์ƒ ์ด๋Ÿฐ ๋ฐฉ์‹์€ ํšจ๊ณผ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹น์‹ ์ด Rx*๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ์ค„ ์•Œ๊ฒŒ ๋˜๋ฉด, Cold vs Hot Observables ์˜ ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋Š”๊ฑด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฑธ ๋ฌด์‹œํ•˜๋ฉด, ์–ธ์  ๊ฐ€ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๋กœ ๋˜๋Œ์•„์˜ฌ๊ฒ๋‹ˆ๋‹ค. ๋ถ„๋ช… ๊ฒฝ๊ณ ํ–ˆ์–ด์š”. ์ง„์งœ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ฐฐ์šฐ๋ฉด์„œ ์‹ค๋ ฅ์„ ๋” ํ‚ค์šฐ์‹œ๊ณ , and getting acquainted with issues such as side effects that affect Rx*.

ํ•˜์ง€๋งŒ Reactive Programming์€ Rx* ๋ฟ๋งŒ์ด ์•„๋‹™๋‹ˆ๋‹ค. Rx* ๋ฅผ ํ•˜๋‹ค๋ณด๋ฉด ๊ฐ€๋” ์ด์ƒํ•œ ์ ๋“ค์„ ๋А๋‚„ ๋•Œ๊ฐ€ ์žˆ์„ ํ…๋ฐ, Bacon.js ์„ ์“ฐ๋ฉด ์ง๊ด€์ ์œผ๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Elm Language ์€, JavaScript + HTML + CSS ์œผ๋กœ ์ปดํŒŒ์ผ๋˜๋Š” Functional Reactive Programming ์–ธ์–ด๋กœ์จ, ๋…์ž์ ์ธ ๋ถ„์•ผ๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Elm์—๋Š” time travelling debugger ์ด๋ž€๊ฒŒ ์žˆ๋Š”๋ฐ, ๊ต‰์žฅํ•œ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

Rx๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋„˜์ณ๋‚˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ์™€ ์•ฑ ๋‹จ์—์„œ ํ›Œ๋ฅญํ•˜๊ฒŒ ์“ฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹จ์ง€ ํด๋ผ์ด์–ธํŠธ ์ชฝ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋ฐฑ์—”๋“œ, DB์™€ ์—ฐ๊ฒฐ๋˜๋Š” ๋ถ€๋ถ„์—์„œ๋„ ํ›Œ๋ฅญํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค RxJava๋Š” Netflix์˜ API์—์„œ ์„œ๋ฒ„์‚ฌ์ด๋“œ concurrency๋ฅผ ๊ฐ€๋Šฅ์ผ€ํ•˜๋Š” ํ•ต์‹ฌ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. Rx๋Š” ํŠน์ • ํƒ€์ž…์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋‚˜ ์–ธ์–ด์— ์“ฐ์ด๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ˆ˜์ค€์— ์ œํ•œ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ด๋ฒคํŠธ-๊ธฐ๋ฐ˜ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•  ๋•Œ ์“ธ ์ˆ˜ ์žˆ๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„ ๊ทธ ์ž์ฒด์ž…๋‹ˆ๋‹ค.

์ด ํŠœํ† ๋ฆฌ์–ผ์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด, ํŠธ์œ„ํ„ฐ๋กœ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”.

keyword: rxjs, rxjs ์ž…๋ฌธ, rxjs ๊ฐ€์ด๋“œ, rxjs ๊ธฐ์ดˆ, rxjs ์†Œ๊ฐœ, rxjs ์„ค๋ช…, rxjs ์ธํŠธ๋กœ, rxjs intro, rxjs ์ด๋ž€

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment