Skip to content

Latest commit

ย 

History

History
608 lines (506 loc) ยท 43.9 KB

File metadata and controls

608 lines (506 loc) ยท 43.9 KB

์ด๊ธ€์€ @andrestaltz์˜ "The introduction to Reactive Programming you've been missing" ( https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 )๋ฅผ ๋ฒˆ์—ญํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค. (๋ฒˆ์—ญ ํ—ˆ๋ฝ์„ ๋ฐ›์€ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ, ๋„์›€์ด ํ•„์š”ํ•˜์‹  ๋ถ„๋“ค์„ ์œ„ํ•ด์„œ ์˜ฌ๋ ค๋ด…๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋  ๊ฒฝ์šฐ ๋ฐ”๋กœ ์‚ญ์ œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ใ…กใ…œ)

์—ฌ๋Ÿฌ๋ถ„์ด ๋†“์ณ์™”๋˜ Reactive Programming์— ๋Œ€ํ•œ ์†Œ๊ฐœ (@andrestaltz ์ž‘์„ฑ)

๋™์˜์ƒ ํŠœํ„ฐ๋ฆฌ์–ผ

๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ๋ผ์ด๋ธŒ ์ฝ”๋”ฉ์„ ํฌํ•จํ•œ ๋™์˜์ƒ ํŠœํ„ฐ๋ฆฌ์–ผ์„ ๋ณด๋Š” ๊ฒƒ์„ ๋” ์„ ํ˜ธํ•œ๋‹ค๋ฉด, ์ด ๋™์˜์ƒ ์‹œ๋ฆฌ์ฆˆ๋ฅผ ํ™•์ธํ•ด๋ณด์„ธ์š”. ์ด ๊ธ€๊ณผ ๋™์ผํ•œ ๋‚ด์šฉ์œผ๋กœ ๋…นํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ( https://egghead.io/series/introduction-to-reactive-programming )

์—ฌ๋Ÿฌ๋ถ„์€ Reactive Programming ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ด ์ƒˆ๋กœ์šด ๊ฒƒ์„ ๋ฐฐ์šฐ๋Š”๋ฐ ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ Reactive Programming ๋ณ€ํ˜•๋“ค์€ Rx, Bacon.js, RAC ๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ๊ตฌ์„ฑํ•˜๊ณ  ์žˆ์ฃ .

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

Rx.Observable.prototype.flatMapLatest(selector, [thisArg])
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 ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ๋ฒ•์œผ๋กœ ๊ณง์žฅ ๋›ฐ์–ด๋“œ๋Š” ๊ทธ๋Ÿฐ ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.์ €๋Š” ๊ฒฐ๊ตญ ๊ณ ์ƒํ•ด์„œ Reactive Programming ํ•™์Šต์„ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค. ๋งŒ๋“ค๋ฉด์„œ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. Futurice์˜ ์ง์žฅ์—์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— Reactive Programming์„ ์‚ฌ์šฉํ–ˆ๊ณ , ๊ณค๊ฒฝ์— ์ฒ˜ํ•  ๋• ๋™๋ฃŒ์˜ ๋„์›€( http://blog.futurice.com/top-7-tips-for-rxjava-on-android )๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

"Reactive Programming์€ ๋ฌด์—‡์ธ๊ฐ€์š”?"

์ธํ„ฐ๋„ท์— ๋‚˜์œ ์„ค๋ช…๊ณผ ์ •์˜๊ฐ€ ๋งค์šฐ ๋งŽ์Šต๋‹ˆ๋‹ค. Wikipedia( https://en.wikipedia.org/wiki/Reactive_programming )๋Š” ๋ณดํ†ต ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ด๊ณ  ์ด๋ก ์ ์ž…๋‹ˆ๋‹ค. Stackoverflow( http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming )์˜ ๊ทœ๋ฒ”์ ์ธ ๋‹ต๋ณ€์€ ๋ช…๋ฐฑํžˆ ์ดˆ์‹ฌ์ž๋“ค์—๊ฒŒ๋Š” ์ ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Reactive Manifesto( http://www.reactivemanifesto.org/ )๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ ๋งค๋‹ˆ์ € ํ˜น์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ํšŒ์‚ฌ์— ๊ฒฝ์˜์ž ์—๊ฒŒ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๊ฒƒ๋“ค ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ์˜ Rx ์šฉ์–ด( https://rx.codeplex.com/ ) "Rx = Observables + LINQ + Schedulers"๋Š” ๋„ˆ๋ฌด ๋ฌด๊ฒ๊ณ  ๋„ˆ๋ฌด ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ์Šค๋Ÿฌ์›Œ์„œ ์šฐ๋ฆฌ ๋Œ€๋ถ€๋ถ„์„ ํ˜ผ๋ž€ ์Šค๋Ÿฝ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. "reative" ์™€ "๋ณ€๊ฒฝ์‚ฌํ•ญ์˜ ์ „ํŒŒ"("propagation of change")๊ฐ™์€ ์šฉ์–ด๋Š” ์ „ํ˜•์ ์ธ MV* ์™€ ์ธ๊ธฐ์žˆ๋Š” ์–ธ์–ด๋“ค์ด ์ด๋ฏธ ์ „ํŒŒํ–ˆ๋˜ ๊ฒƒ๋“ค๊ณผ๋Š” ํŠน๋ณ„ํžˆ ๋‹ค๋ฅธ ๊ฒƒ์„ ์ „๋‹ฌํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์ €์˜ framework view๋“ค์€ model๋“ค์— ๋ฐ˜์‘(react)ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด, ์•„๋ฌด๊ฒƒ๋„ ๊ทธ๋ ค์ง€์ง€ ์•Š์„ ๊ฒ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ—›์†Œ๋ฆฌ ์ง‘์–ด์น˜์›์‹œ๋‹ค.

Reactive programming์€ ๋น„๋™๊ธฐ data ํ๋ฆ„๋“ค(asynchronous data streams)๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค.

ํ•œํŽธ์œผ๋กœ๋Š” Reactive programming์€ ์ƒˆ๋กœ์šด ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ๋ฒ„์Šค๋“ค ํ˜น์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ „ํ˜•์ ์ธ ํด๋ฆญ ์ด๋ฒคํŠธ๋“ค์€ ์‚ฌ์‹ค์ƒ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ํ๋ฆ„(stream)์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์ด ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๊ณ , ๋ช‡๊ฐ€์ง€ Side effect(ํ•จ์ˆ˜์˜ ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ์‚ฌ์ด ์—ฐ๊ด€ ๊ด€๊ณ„ ์—†์ด ๋™์ž‘ํ•˜๋Š” ๊ฒƒ)๋“ค์„ ํ•  ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ stream์ž…๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์€ ๋‹จ์ˆœํžˆ clickํ•˜๊ณ  hoverํ•˜๋Š” ์ด๋ฒคํŠธ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์–ด๋–ค ๊ฒƒ์ด๋ผ๋„ data stream์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. stream๋“ค์€ ์—ฐ์‚ฐ ๋น„์šฉ์ด ์ €๋ ดํ•˜๊ณ  ์–ด๋””์—๋‚˜ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒƒ์ด๋“  stream์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.:๋ณ€์ˆ˜, ์‚ฌ์šฉ์ž ์ž…๋ ฅ, property๋“ค, cache๋“ค, ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋“ค ๊ธฐํƒ€๋“ฑ๋“ฑ. ์˜ˆ๋ฅผ ๋“ค์–ด, click ์ด๋ฒคํŠธ๋“ค๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ Twitter ํ”ผ๋“œ๋„ data stream์ผ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์€ ๊ทธ stream์„ ๋“ฃ๊ณ , ๊ทธ์— ๋”ฐ๋ผ ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์— ๋”๋ถˆ์–ด, ๊ทธ๋Ÿฐ stream๋“ค์„ ๊ฒฐํ•ฉํ•˜๊ณ (combine), ์ƒ์„ฑํ•˜๊ณ , ๊ฑฐ๋ฅด๋Š”(filter) ํ•จ์ˆ˜๋“ค์˜ ๋†€๋ผ์šด ๋„๊ตฌ์ƒ์ž๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ์ฃผ์–ด์กŒ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ "functional"์˜ ๋งˆ์ˆ ์ด ํšจ๊ณผ๋ฅผ ๋ฐœํœ˜ํ•ฉ๋‹ˆ๋‹ค. stream์€ ๋‹ค๋ฅธ stream์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด ์—ฌ๋Ÿฌ๊ฐœ์˜ stream๋“ค์ด ๋‹ค๋ฅธ stream์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ๋‘๊ฐœ์˜ stream๋“ค์„ ํ•ฉ์น ์ˆ˜(merge)๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ๊ด€์‹ฌ์žˆ๋Š” ์ด๋ฒคํŠธ๋งŒ์„ ๊ฐ€์ง€๋Š” stream์„ ์–ป๊ธฐ ์œ„ํ•ด์„œ stream์„ ๊ฑฐ๋ฅผ์ˆ˜(filter) ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ํ•œ stream์œผ๋กœ ๋ถ€ํ„ฐ์˜ data ๊ฐ’์„ ๋‹ค๋ฅธ ์ƒˆ๋กœ์šด stream์œผ๋กœ ์‚ฌ์ƒํ• (map - ์–ด๋–ค ๊ฐ’(์ด๋ฒคํŠธ)๋ฅผ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜)์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

stream์€ ์‹œ๊ฐ„ ์ˆœ์„œ๋Œ€๋กœ ์ง„ํ–‰์ค‘์ธ ์ด๋ฒคํŠธ๋“ค์˜ ์ˆœ์„œ๋ฐฐ์—ด(sequence)์ž…๋‹ˆ๋‹ค. stream์€ ์„ธ๊ฐ€์ง€ ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ๋ฐœ์ƒ์‹œํ‚ฌ(emit) ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.: (์–ด๋–ค ํƒ€์ž…์˜) ๊ฐ’, ์˜ค๋ฅ˜(error), "์™„๋ฃŒ๋จ" ์‹ ํ˜ธ("completed" signal). "completed" ์‹ ํ˜ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฒ„ํŠผ์„ ํฌํ•จํ•œ ํ˜„์žฌ ์œˆ๋„์šฐ ํ˜น์€ view๊ฐ€ ๋‹ซํ˜”์„๋•Œ(closed)๋ฅผ ๊ฐ€์ •ํ•ด ๋ณด์„ธ์š”.

์šฐ๋ฆฌ๋Š” ์ด ๋ฐœ์ƒํ•œ(emitted) ์ด๋ฒคํŠธ๋“ค์„ ๋‹จ์ง€ ๋น„๋™๊ธฐ๋กœ(asynchronously) ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ’์ด ๋ฐœ์ƒํ• (emitted) ๋•Œ ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜, ์˜ค๋ฅ˜(error)๊ฐ€ ๋ฐœ์ƒํ• (emitted) ๋•Œ ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜, "completed" ์‹ ํ˜ธ๊ฐ€ ๋ฐœ์ƒํ• (emitted) ๋•Œ ์‹คํ–‰๋˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋“ค์„ ์ •์˜ ํ•จ์œผ๋กœ์จ ์ด๋ฒคํŠธ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰ ๋‘ ๊ฐ€์ง€(error, "completed" signal)์€ ๊ฐ€๋” ๋ฐœ์ƒํ•˜๊ธฐ(emitted) ๋•Œ๋ฌธ์—, ์—ฌ๋Ÿฌ๋ถ„์€ ๊ฐ’๋“ค์— ๋Œ€ํ•œ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š”๋ฐ๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. stream์„ ๋“ฃ๋Š”("listening") ๊ฒƒ์„ subscribing ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ํ•จ์ˆ˜๋“ค์€ observer๋“ค์ž…๋‹ˆ๋‹ค. stream์€ ๊ด€์ฐฐ๋˜๋Š” subject(ํ˜น์€ "observable")์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ํ™•์‹คํžˆ Observer Design Pattern( https://en.wikipedia.org/wiki/Observer_pattern )์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ฆฌ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ ASCII๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ด ํŠœํ„ฐ๋ฆฌ์–ผ์˜ ๋ช‡ ๋ถ€๋ถ„์—์„œ ASCII๋ฅผ ์‚ฌ์šฉํ•  ๊ฒ๋‹ˆ๋‹ค.

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

a, b, c, d ๋Š” ๋ฐœ์ƒํ•œ(emitted) ๋œ ๊ฐ’๋“ค์ž…๋‹ˆ๋‹ค.
X ๋Š” ์˜ค๋ฅ˜(error)์ž…๋‹ˆ๋‹ค.
| ๋Š” "completed" ์‹ ํ˜ธ์ž…๋‹ˆ๋‹ค.
---> ๋Š” "timeline" ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ฒƒ๋“ค์€ ์ด๋ฏธ ์ถฉ๋ถ„ํžˆ ์นœ์ˆ™ํ•˜๊ณ , ์ง€๋ฃจํ•ด์ง€๊ณ  ์‹ถ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ƒˆ๋กœ์šด ๊ฒƒ์„ ํ•ด๋ด…์‹œ๋‹ค. ์›๋ž˜์˜ click ์ด๋ฒคํŠธ stream ๋กœ๋ถ€ํ„ฐ ๋ณ€ํ˜•๋œ ์ƒˆ๋กœ์šด click ์ด๋ฒคํŠธ stream๋“ค์„ ์ƒ์„ฑํ•  ๊ฒ๋‹ˆ๋‹ค.

์ฒซ๋ฒˆ์งธ๋กœ, counter stream์„ ๋งŒ๋“ญ์‹œ๋‹ค. counter stream์€ ๋ฒ„ํŠผ์ด ๋ช‡๋ฒˆ click๋˜์—ˆ๋Š”์ง€ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๋ณดํ†ต์˜ Reactive ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ๊ฐ๊ฐ์˜ stream์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋งŽ์€ ํ•จ์ˆ˜๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. map, filter, scan, ๊ธฐํƒ€๋“ฑ๋“ฑ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์ด ์ด ํ•จ์ˆ˜๋“ค ์ค‘ ํ•˜๋‚˜(clickStream.map(f)๊ฐ™์€ ํ•จ์ˆ˜)๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ๊ทธ ํ•จ์ˆ˜๋Š” ๊ทธ click stream์— ๊ธฐ๋ฐ˜ํ•œ ์ƒˆ๋กœ์šด stream์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ•จ์ˆ˜๋“ค์€ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋“ ์ง€ ๊ฐ„์— ์›๋ž˜ click stream์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ถˆ๋ณ€์„ฑ(immutability)๋ผ๊ณ  ๋ถˆ๋ฆฌ์šฐ๋Š” ์†์„ฑ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ถˆ๋ณ€์„ฑ์€ ํŒฌ์ผ€์ดํฌ์™€ ์‹œ๋Ÿฝ์ด ์ž˜ ์–ด์šธ๋ฆฌ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ Reactive stream๋“ค๊ณผ ํ•จ๊ป˜ ์ž˜ ์–ด์šธ๋ฆฝ๋‹ˆ๋‹ค. ์›๋ž˜ stream์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์ƒˆ๋กœ์šด stream์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์€ 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) ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด ์ œ๊ณตํ•œ f ๋ผ๋Š” ํ•จ์ˆ˜์— ๋”ฐ๋ผ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ๊ฐ์˜ ๊ฐ’์„ ์น˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ์—๋Š” ๊ฐ click ์— ๋Œ€ํ•ด์„œ ์ˆซ์ž 1์ด ์‚ฌ์ƒ(map)๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. scan(g) ํ•จ์ˆ˜๋Š” stream์˜ ๋ชจ๋“  ์ด์ „์˜ ๊ฐ’๋“ค์„ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. x = g(accumulated, current) ์ด๋ผ๋Š” ๊ฐ’์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. g๋Š” ์ด ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ„๋‹จํžˆ ๋ง์…ˆ ํ•จ์ˆ˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋‹ค์Œ counterStream ์€ click ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค click ํšŒ์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Reactive ์˜ ํž˜์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„œ, "double click" ์ด๋ฒคํŠธ๋“ค์˜ stream์„ ๊ฐ€์ง€๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ๋” ์žฌ๋ฏธ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ, triple click์„ double click์œผ๋กœ ๊ฐ„์ฃผํ•˜๋Š” ์ƒˆ๋กœ์šด stream์„ ์›ํ•œ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ํ˜น์€ ์ผ๋ฐ˜์ ์œผ๋กœ multiple click๋“ค(๋‘๋ฒˆํ˜น์€ ๊ทธ ์ด์ƒ)์„ double click์œผ๋กœ ๊ฐ„์ฃผํ•œ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ์‹ฌํ˜ธํก์„ ํ•˜๊ณ  ์ „ํ†ต์ ์ธ ๋ช…๋ นํ˜•์— ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ทธ๋Ÿฐ ๊ฒƒ๋“ค์„ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ์ง€ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ๊ทธ๊ฒƒ์€ ๊ฝค ๋ถˆ์พŒํ•˜๊ฒŒ ๋“ค๋ฆด ๊ฒ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡๊ฐœ์˜ ๋ณ€์ˆ˜์™€ time interval๋“ค์„ ์ด์šฉํ•œ ๋ช‡๊ฐ€์ง€ ์†์ž„์ˆ˜๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

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

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

RP๋ฅผ ์™œ ๋„์ž…ํ•ด์•ผ ํ•˜๋‚˜์š”?

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

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

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

์˜ˆ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  Reactive Programming์œผ๋กœ ์ƒ๊ฐํ•˜๊ธฐ

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

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

"follow ํ•  ์‚ฌ๋žŒ" ์ถ”์ฒœ ์ƒ์ž ๊ตฌํ˜„ํ•˜๊ธฐ

ํŠธ์œ—ํ„ฐ์—๋Š” ์—ฌ๋Ÿฌ๋ถ„์ด followํ•  ๊ณ„์ •์„ ์ถ”์ฒœํ•˜๋Š” ์ด๋Ÿฐ UI ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์˜ ์ฃผ ๊ธฐ๋Šฅ์„ ๋ชจ๋ฐฉํ•˜๋Š”๋ฐ ์ง‘์ค‘ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฃผ ๊ธฐ๋Šฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

๋งˆ์ด๋„ˆํ•œ ๋ฒ„ํŠผ๊ณผ ๊ธฐ๋Šฅ์€ ๋‚จ๊ฒจ๋†“๊ธฐ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ตœ๊ทผ ๋ฏธ์Šน์ธ๋œ ์ผ๋ฐ˜์— ๋Œ€ํ•ด API๋ฅผ ๋‹ซ์€ ํŠธ์œ—ํ„ฐ ๋Œ€์‹ ์— ๊นƒํ—™์— followํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์— ๋Œ€ํ•œ UI๋ฅผ ๋งŒ๋“ญ์‹œ๋‹ค. ์—ฌ๊ธฐ ์œ ์ €๋“ค์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๊นƒํ—™ API( https://developer.github.com/v3/users/#get-all-users )๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๋ฉด, http://jsfiddle.net/staltz/8jFJH/48/ ์— ์™„์ „ํ•œ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Request ์™€ Response

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

์‹œ์ž‘ํ•  ๋•Œ ์šฐ๋ฆฌ๋Š” ๋‹จ์ง€ ํ•˜๋‚˜์˜ ์š”์ฒญ(request)์„ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ data stream ์œผ๋กœ ๋ชจ๋ธ๋งํ–ˆ๋‹ค๋ฉด, ๊ทธ๊ฒƒ์€ ๋‹จ์ง€ ํ•˜๋‚˜์˜ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์„ ๊ฐ€์ง€๋Š” stream ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚˜์ค‘์—, ์šฐ๋ฆฌ๋Š” ๋งŽ์€ ์š”์ฒญ(request)๋“ค์„ ๊ฐ€์ง€๊ฒŒ ๋  ๊ฒƒ์„ ์•Œ์ง€๋งŒ ์ง€๊ธˆ์€ ๊ทธ๋ƒฅ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

--a------|->
a ๋Š” 'https://api.github.com/users' ๋ฌธ์ž์—ด์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์š”์ฒญ(request)ํ•˜๊ธฐ ์›ํ•˜๋Š” URL๋“ค์˜ stream ์ž…๋‹ˆ๋‹ค. ์š”์ฒญ(request)์ด ๋ฐœ์ƒํ•  ๋•Œ ๋งˆ๋‹ค, ์š”์ฒญ(request)๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ๋‘๊ฐ€์ง€๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.:๋•Œ(when) ์™€ ๋Œ€์ƒ(what) ย  ย  ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ• (emitted) ๋•Œ๊ฐ€ ์š”์ฒญ(request)์ด ์‹คํ–‰๋˜์–ด์•ผ ํ•  ๋•Œ(when)์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์š”์ฒญ๋˜์–ด์•ผ ํ•  ๊ฒƒ(what)์€ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์ž…๋‹ˆ๋‹ค.:URL ์„ ํฌํ•จํ•˜๋Š” ๋ฌธ์ž์—ด ย 

Rx* ์—์„œ ํ•˜๋‚˜์˜ ๊ฐ’์„ ๊ฐ€์ง€๋Š” ๊ทธ๋Ÿฐ stream ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. stream ์— ๋Œ€ํ•œ ๊ณต์‹ ์šฉ์–ด๋Š” "Observable" ์ž…๋‹ˆ๋‹ค, ๊ด€์ฐฐ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค ๋•Œ๋ฌธ์— "Observable" ์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ €๋Š” ๊ทธ๊ฒƒ์ด ๋ฐ”๋ณด ๊ฐ™์€ ์ด๋ฆ„์ด๋ผ๋Š” ๊ฒƒ์„ ์••๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ €๋Š” stream ์ด๋ผ๊ณ  ๋ถ€๋ฅผ ๊ฒ๋‹ˆ๋‹ค.

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

๊ทธ๋Ÿฌ๋‚˜ ์ง€๊ธˆ์€ ๊ทธ๊ฑด ๋‹จ์ง€ ๋ฌธ์ž์—ด๋“ค์˜ stream์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ์•„๋ฌด๋Ÿฐ ๋‹ค๋ฅธ ๋™์ž‘์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๊ฐ’์ด ๋ฐœ์ƒํ• (emitted) ๋•Œ, ์–ด์จ‹๋“  ๋ญ”๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒƒ์€ stream ์„ subscribe( https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md#rxobservableprototypesubscribeobserver--onnext-onerror-oncompleted ) ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

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

์šฐ๋ฆฌ๋Š” ์š”์ฒญ(request) ์—ฐ์‚ฐ์˜ ๋น„๋™๊ธฐ์„ฑ์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•ด์„œ jQuery Ajax callback(์—ฌ๋Ÿฌ๋ถ„์ด ์ด๋ฏธ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.) ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹ฌํ•˜์„ธ์š”. ํ•˜์ง€๋งŒ ์ž ์‹œ๋งŒ ๊ธฐ๋‹ค๋ฆฌ์„ธ์š”. Rx ๋Š” ๋น„๋™๊ธฐ data stream ์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ๊ฒ๋‹ˆ๋‹ค.

๊ทธ ์š”์ฒญ(request)์— ๋Œ€ํ•œ ์‘๋‹ต(response)์€ ๋ฏธ๋ž˜์˜ ์–ด๋–ค ์‹œ์ ์— ๋„์ฐฉํ•  ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” stream ์ด ๋  ์ˆ˜ ์—†์„๊นŒ์š”? ๊ธ€์Ž„, ๊ฐœ๋…์ ์œผ๋กœ๋Š”, ๊ทธ๋ ‡๊ฒŒ ๋  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ๊ทธ๊ฑธ ์‹œ๋„ ํ•ด๋ด…์‹œ๋‹ค.

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์—๊ฒŒ ์•Œ๋ฆผ์œผ๋กœ์จ(ํ˜น์€ ๋‹ค๋ฅธ ๋ง๋กœ, "subscriber") data ์ด๋ฒคํŠธ๋“ค( onNext() ํ˜น์€ ์˜ค๋ฅ˜(onError() )์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ๋ถ„์˜ custom stream ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ•œ ๊ฒƒ์€ ๋‹จ์ง€ jQuery Ajax Promise ๋ฅผ ๊ฐ์‹ธ๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์‹ค๋ก€์ง€๋งŒ, ์ด๊ฑด Promise ๋Š” Observable ์ด๋ผ๋Š” ๋ง์ž…๋‹ˆ๊นŒ? (*Promise ๋Š” ๋ถˆ๋ณ€(immutable) ๊ฐ’์„ ์บก์Аํ™”ํ•˜๋Š” thread-safe ๊ฐ์ฒด๋กœ, ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ๋กœ ๊ฐ’์ด ์ƒ์„ฑ๋  ๋•Œ๊นŒ์ง€ ๊ทธ๊ฒƒ์„ ์ฐธ์กฐํ•˜๋Š” ๋ถ€๋ถ„์€ block๋˜๊ณ  ๊ฐ’์ด ์ƒ๊ธฐ๋ฉด, block์ด ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค.)

๋„ค, ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

Observable ์€ Promise++ ์ž…๋‹ˆ๋‹ค. Rx์—์„œ ์—ฌ๋Ÿฌ๋ถ„์€ var stream = Rx.Observable.fromPromise(promise)๋ฅผ ํ•จ์œผ๋กœ์จ Promise๋ฅผ ์‰ฝ๊ฒŒ Observale๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•ฉ์‹œ๋‹ค. ์œ ์ผํ•œ ์ฐจ์ด์ ์€ Observale ๋“ค์€ Promises/A+( https://promisesaplus.com/ ) ์ˆœ์‘(complicant)์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์ด๋‹ค. ํ•˜์ง€๋งŒ ๊ฐœ๋…์ ์œผ๋กœ ์ถฉ๋Œ์€ ์—†์Šต๋‹ˆ๋‹ค. Promise ๋Š” ๋‹จ์ˆœํžˆ ํ•˜๋‚˜์˜ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์„ ๊ฐ€์ง€๋Š” Observable ์ž…๋‹ˆ๋‹ค. Rx stream ๋“ค์€ ๋งŽ์€ ๋ฆฌํ„ด๋œ ๊ฐ’๋“ค์„ ํ—ˆ์šฉํ•จ์œผ๋กœ์จ promise ๋“ค์„ ๋Šฅ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฑด ๊ฝค ์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ Observable๋“ค์ด ์ตœ์†Œํ•œ Promise๋“ค ๋งŒํผ ๊ฐ•๋ ฅํ•œ์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด Promise ์„ ์ „ ๊ด‘๊ณ ๋ฅผ ๋ฏฟ๋Š”๋‹ค๋ฉด, Rx Observable๋“ค์ด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค์— ๋Œ€ํ•ด์„œ ๊ด€์‹ฌ์„ ๊ฐ€์ ธ๋ณด์„ธ์š”.

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

์ง€๊ธˆ์ฏค ์—ฌ๋Ÿฌ๋ถ„์ด ์•Œ์•„์•ผ ํ•˜๋Š” ๊ธฐ๋ณธ ํ•จ์ˆ˜ ํ•˜๋‚˜๊ฐ€ map(f) ์ž…๋‹ˆ๋‹ค. map(f) ๋Š” stream A ์˜ ๊ฐ๊ฐ์˜ ๊ฐ’๋“ค์„ ์ทจํ•ด์„œ f()๋ฅผ ์ ์šฉํ•˜๊ณ  stream B ์— ๋Œ€ํ•œ ๊ฐ’์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ map(f)๋ฅผ ์šฐ๋ฆฌ์˜ ์š”์ฒญ(request) stream๋“ค์— ์ ์šฉํ•œ๋‹ค๋ฉด, ์š”์ฒญ(request) URL๋“ค์„ ์‘๋‹ต(response) Promise ๋“ค๋กœ ์‚ฌ์ƒํ• (map) ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.( stream ๋“ค๋กœ ์œ„์žฅ๋ฉ๋‹ˆ๋‹ค.)

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

๊ทธ๋Ÿฐ ๋‹ค์Œ ์šฐ๋ฆฌ๋Š” "mainstream" ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ƒ์„ฑ๋œ ๊ดด๋ฌผ์„ ๊ฐ€์ง€๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.: stream๋“ค ์ค‘์— stream์ž…๋‹ˆ๋‹ค. ์•„์ง ๋‹นํ™ฉํ•˜์ง€ ๋งˆ์„ธ์š”. mainstream ์€ ๊ฐ๊ฐ์˜ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์ด ์•„์ง ๋‹ค๋ฅธ stream ์ธ stream ์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ๊ทธ๊ฒƒ์„ ํฌ์ธํ„ฐ( https://en.wikipedia.org/wiki/Pointer_(computer_programming) )๋“ค๋กœ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. : ๊ฐ๊ฐ์˜ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์€ ๋‹ค๋ฅธ stream์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ์˜ˆ์ œ์—์„œ๋Š”, ๊ฐ๊ฐ์˜ ์š”์ฒญ(request) URL ์€ ๊ด€๊ณ„๋œ ์‘๋‹ต(response)์„ ํฌํ•จํ•˜๋Š” promise stream ์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ๋กœ ์‚ฌ์ƒ(map)๋ฉ๋‹ˆ๋‹ค.

์‘๋‹ต(response)๋“ค์— ๋Œ€ํ•œ mainstream ์€ ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ์—๊ฒŒ ์ „ํ˜€ ๋„์›€์ด ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‹จ์ง€ ์‘๋‹ต(response)๋“ค์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ stream ์„ ์›ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ๋ฐœ์ƒํ•œ(emitted) ๊ฐ’์€ JSON ๊ฐ์ฒด์ด๊ณ , JSON ์˜ 'Promise'๊ฐ€ ์•„๋‹Œ stream์ž…๋‹ˆ๋‹ค. Flatmap์”จ์—๊ฒŒ ์ธ์‚ฌํ•ด๋ณด์„ธ์š”. ๊ฐ€์ง€("branch") stream๋“ค์— ๋ฐœ์ƒํ•œ(emitted) ๋ชจ๋“  ๊ฒƒ์„ "trunk" stream ์— ๋ฐœ์ƒ์‹œํ‚ด(emitting)์œผ๋กœ์จ mainstream ์„ ๋„“๊ฒŒ ํŽด๋Š”("flattens") map์˜ ํ•œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. Flatmap์€ ์ˆ˜์ •("fix")์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  mainstream๋“ค์€ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒƒ๋“ค์€ Rx์—์„œ ์‚ฌ์‹ค์ƒ ๋น„๋™๊ธฐ ์‘๋‹ต๋“ค์„ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๋“ค์ž…๋‹ˆ๋‹ค.

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

์ข‹์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‘๋‹ต(response) stream ์€ ์š”์ฒญ(request) stream์— ๋”ฐ๋ผ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‚˜์ค‘์— ์š”์ฒญ(request) stream ์— ๋” ๋งŽ์€ ์ด๋ฒคํŠธ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด, ๊ธฐ๋Œ€ํ–ˆ๋˜ ๋Œ€๋กœ, ์‘๋‹ต(response) stream์— ๋ฐœ์ƒํ•˜๋Š” ๊ด€๋ จ๋œ ์‘๋‹ต(response) ์ด๋ฒคํŠธ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.

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

(์†Œ๋ฌธ์ž๋Š” ์š”์ฒญ(request)์ด๊ณ , ๋Œ€๋ฌธ์ž๋Š” ๊ทธ๊ฒƒ์˜ ์‘๋‹ต(response)์ž…๋‹ˆ๋‹ค.)

์ด์ œ ์šฐ๋ฆฌ๋Š” ๋งˆ์นจ๋‚ด ์‘๋‹ต(response) stream์„ ๊ฐ€์ง€๊ฒŒ ๋˜์—ˆ๊ณ , ์šฐ๋ฆฌ๊ฐ€ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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
});

Refresh ๋ฒ„ํŠผ

์ €๋Š” ์•„์ง ์‘๋‹ต JSON์ด 100๋ช…์˜ ์œ ์ €๋ฅผ ๊ฐ€์ง€๋Š” ๋ฆฌ์ŠคํŠธ๋ผ๊ณ  ์ด์•ผ๊ธฐ ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. API๋Š” ์šฐ๋ฆฌ๊ฐ€ ํŽ˜์ด์ง€ ์˜คํ”„์…‹๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ํ—ˆ์šฉํ•˜๊ณ , ํŽ˜์ด์ง€ ์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” 3๊ฐœ์˜ data object๋“ค์€ ์‚ฌ์šฉํ•˜๊ณ  97๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋Š” ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ์‘๋‹ต(response)๋“ค์„ cacheํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—,์ง€๊ธˆ์€ ๊ทธ ๋ฌธ์ œ๋Š” ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.

refresh button ์ด click๋  ๋•Œ๋งˆ๋‹ค, ์š”์ฒญ(request) stream์€ ์ƒˆ๋กœ์šด URL์„ ๋ฐœ์ƒ์‹œ์ผœ์•ผ(emit) ํ•˜๊ณ  ๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ ์ƒˆ๋กœ์šด ์‘๋‹ต(response)๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋‘๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.: click ์ด๋ฒคํŠธ๋“ค์˜ stream(์ฃผ๋ฌธ(mantra): ์–ด๋–ค ๊ฒƒ์ด๋“  ๊ฐ„์— stream์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.) ๊ทธ๋ฆฌ๊ณ  ์‘๋‹ต(request) stream์ด refresh click stream์„ ์˜์กดํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์˜๊ฒŒ๋„, RxJS์—๋Š” Observables๋ฅผ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋“ค๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๋“ค์ด ๋”ธ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค.

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

refresh click ์ด๋ฒคํŠธ ๊ทธ ์ž์ฒด๋กœ๋Š” ์–ด๋–ค API URL์„ ์šด๋ฐ˜ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์šฐ๋ฆฌ๋Š” ๊ฐ๊ฐ์˜ click ์„ ์‹ค์ œ URL ๋กœ ์‚ฌ์ƒ(map)ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ๋Š” ์š”์ฒญ(request) stream๋ฅผ ๋งค๋ฒˆ ๋žœ๋ค ์˜คํ”„์…‹ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง€๋Š” API endpoint ์— ์‚ฌ์ƒ(map)๋œ refresh click stream์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

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

์ €๋Š” ์•„๋‘”ํ•˜๊ณ , ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ด์ „์— ๋งŒ๋“  ๊ธฐ๋Šฅ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ง๊ฐ€๋œจ๋ ธ์Šต๋‹ˆ๋‹ค. ์‹œ์ž‘ํ•  ๋•Œ ์š”์ฒญ(request)๋Š” ๋”์ด์ƒ ์•„๋ฌด๊ฒƒ๋„ ๋ฐœ์ƒ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. refresh ๊ฐ€ click ๋  ๋•Œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์•„์•„, ์ €๋Š” ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ๋™์ž‘์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.: refresh ๊ฐ€ click ๋˜๊ฑฐ๋‚˜ ์›นํŽ˜์ด์ง€๊ฐ€ ์—ด๋ฆด ๋•Œ ์š”์ฒญ(request)

์šฐ๋ฆฌ๋Š” ๋‹ค๋ฅธ ๊ฒฝ์šฐ์˜ ๊ฐ๊ฐ์˜ stream ์œ„ํ•œ ๊ฐœ๋ณ„ stream ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.:

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');

ํ•˜์ง€๋งŒ ์–ด๋–ป๊ฒŒ ์ด๋“ค ๋‘๊ฐ€์ง€ stream์„ ํ•˜๋‚˜์˜ stream ์œผ๋กœ ํ•ฉ์น  ์ˆ˜ ์žˆ์„๊นŒ์š”? ๊ธ€์Ž„, 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
);

intermediate stream๋“ค์„ ์ œ๊ฑฐํ•œ ๋” ๊น”๋”ํ•œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ž‘์„ฑํ•œ ๊ฒƒ์ด ์—ฌ๊ธฐ ์žˆ์Šต๋‹ˆ๋‹ค.

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 ํ•จ์ˆ˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐํ•˜๋Š” ๊ทธ๊ฒƒ์ด ์ •ํ™•ํžˆ ๋งž์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์˜ ์ž…๋ ฅ stream ์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์ƒ๊ด€์—†์ด, startWith(x)์˜ ๊ฒฐ๊ณผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” output stream์€ ์ฒ˜์Œ์— x ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋ ๊ฒ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ €๋Š” ์ถฉ๋ถ„ํžˆ DRY( https://en.wikipedia.org/wiki/Don't_repeat_yourself )๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, API endpoint ๋ฌธ์ž์—ด์„ ๋ฐ˜๋ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๊ณ ์น˜๊ธฐ ์œ„ํ•œ ํ•œ ๋ฐฉ๋ฒ•์€ ๊ทผ๋ณธ์ ์œผ๋กœ ์‹œ์ž‘์‹œ์— refresh click์„ "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;
  });

์ข‹์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์ด "์ €๋Š” ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ๋ง๊ฐ€๋œจ๋ ธ์Šต๋‹ˆ๋‹ค."์˜ ์ง€์ ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์€ ์ด ๋งˆ์ง€๋ง‰ ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋‹จ์ง€ ์ œ๊ฐ€ startWith()๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋˜ ๊ฒƒ๋งŒ์ด ์ฐจ์ด๊ฐ€ ๋‚œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

์ง€๊ธˆ๊นŒ์ง€, ์šฐ๋ฆฌ๋Š” responseStream์˜ subscribe()์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ทธ๋ฆฌ๋Š” ๋‹จ๊ณ„์˜ ์ถ”์ฒœ UI element ๋ฅผ ๋‹ค๋ค„์™”์Šต๋‹ˆ๋‹ค. ์ด์ œ๋Š” refresh button๊ณผ ๊ด€๋ จํ•ด์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.:์—ฌ๋Ÿฌ๋ถ„์ด 'refresh'๋ฅผ ํด๋ฆญํ•˜์ง€ ๋งˆ์ž, ํ˜„์žฌ์˜ 3๊ฐ€์ง€ ์ถ”์ฒœ์ด ์ง€์›Œ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‘๋‹ต(response)์ด ๋„์ฐฉํ•œ ์ดํ›„์— ์ƒˆ๋กœ์šด ์ถ”์ฒœ์ด ๋“ค์–ด ์˜ต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ UI๋ฅผ ๋ฉ‹์ง€๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ, ์šฐ๋ฆฌ๋Š” refresh ๊ฐ€ click ๋ ๋•Œ ํ˜„์žฌ ์ถ”์ฒœ๋“ค์„ ๋ชจ๋‘ ์ง€์šธ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

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

์•„๋‹ˆ์š”, ์—ฌ๋Ÿฌ๋ถ„ ๋„ˆ๋ฌด ๋น ๋ฅด์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ข‹์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ถ”์ฒœ DOM ์š”์†Œ๋“ค์— ์˜ํ–ฅ์„ ์ฃผ๋Š”(ํ•˜๋‚˜๋Š” responseStream.subscribe()) ๋‘ ๊ฐœ์˜ subscriber๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์€ Seperation of concerns( https://en.wikipedia.org/wiki/Separation_of_concerns ) ์ฒ˜๋Ÿผ ๋“ค๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Reactive ์ฃผ๋ฌธ(mantra)๋ฅผ ๊ธฐ์–ตํ•˜์‹œ๋‚˜์š”?

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

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() ์—์„œ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ(rendering)์ด ๋ฐœ์ƒํ•˜๋Š” ๋Œ€์‹ ์— ์šฐ๋ฆฌ๋Š” ์—ฌ๊ธฐ์„œ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

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

'๋ฆฌํ”„๋ ˆ์‰ฌ ๋  ๋•Œ, ์ถ”์ฒœ์„ ์ง€์šด๋‹ค'๋กœ ๋Œ์•„์™€์„œ, ์šฐ๋ฆฌ๋Š” ๊ฐ„๋‹จํžˆ refresh click ์„ null ์ถ”์ฒœ ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์ƒ(map)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  suggestion1Stream ์— ์ด๋ ‡๊ฒŒ ๊ทธ๊ฑธ ํฌํ•จ์‹œํ‚ต๋‹ˆ๋‹ค.

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; })
  );

๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ฆด(rendering) ๋•Œ, ์šฐ๋ฆฌ๋Š” null "๋ฐ์ดํ„ฐ ์—†์Œ"("no data")์œผ๋กœ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค์‚ฌ 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 ์€ null ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋ณด๋„ˆ์Šค๋กœ, ์šฐ๋ฆฌ๋Š” ์‹œ์ž‘์‹œ "empty" ์ถ”์ฒœ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์ถ”์ฒœ stream ์— 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' ๋ฒ„ํŠผ์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ƒ๊ฐํ–ˆ์„ ๋•Œ, ์—ฌ๋Ÿฌ๋ถ„์€ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์ด click ๋˜์—ˆ์„ ๋•Œ ์ƒˆ๋กœ์šด ์š”์ฒญ(request)๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.:

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;
  });

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

๋‹ค์‹œ, stream๋“ค์„ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค. 'close1' click ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ, ์šฐ๋ฆฌ๋Š” ์‘๋‹ต(response) ๋ฆฌ์ŠคํŠธ์—์„œ ๋žœ๋ค ์‚ฌ์šฉ์ž ํ•˜๋‚˜๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ์ตœ๊ทผ์— responseStream ์— ๋ฐœ์ƒํ•œ(emitted) ์‘๋‹ต(response)์„ ์‚ฌ์šฉํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค.

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

Rx ์—๋Š” combineLatest ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์„ ํ•ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋Š” ๊ฒฐํ•ฉ ํ•จ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‘๊ฐœ์˜ stream A, B๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‘˜์ค‘ ํ•˜๋‚˜์˜ stream ์ด ๊ฐ’์„ ๋ฐœ์ƒ์‹œํ‚ฌ(emit) ๋•Œ ๋งˆ๋‹ค, combineLatest ๋Š” ๋‘ stream ๋“ค๋กœ ๋ถ€ํ„ฐ ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋ฐœ์ƒํ•œ(emitted) ๋‘๊ฐœ์˜ ๊ฐ’ a, b ๋ฅผ ํ•ฉ์นฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  c = f(x, y) ๊ฐ’์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. f๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋‹ค์ด์–ด ๊ทธ๋žจ์œผ๋กœ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

stream A: --a-----------e--------i-------->
stream B: -----b----c--------d-------q---->
          vvvvvvvv combineLatest(f) vvvvvvv
          ----AB---AC--EC---ED--ID--IQ---->

where f is the uppercase function

์šฐ๋ฆฌ๋Š” close1ClickStream ๊ณผ responseStream ์— combineLatest() ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ close1 ๋ฒ„ํŠผ์ด click๋  ๋•Œ๋งˆ๋‹ค, ์šฐ๋ฆฌ๋Š” ๋ฐœ์ƒํ•œ(emitted) ์ตœ์‹  ์‘๋‹ต(response)๋ฅผ ์–ป๊ณ , suggestion1Stream ์— ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ƒ์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์—, combineLatest()๋Š” ๋Œ€์นญ(symmetric)์ž…๋‹ˆ๋‹ค.: responseStream ์— ์ƒˆ๋กœ์šด ์‘๋‹ต(response)๊ฐ€ ๋ฐœ์ƒํ• (emitted) ๋•Œ๋งˆ๋‹ค, ๊ทธ ์‘๋‹ต(response)๋Š” ๊ฐ€์žฅ ์ตœ์‹ ์˜ 'close 1' click ๊ณผ ๊ฒฐํ•ฉ๋˜์–ด ์ƒˆ๋กœ์šด ์ถ”์ฒœ์„ ์ƒ์‚ฐํ•ฉ๋‹ˆ๋‹ค.

sugestion1Stream์— ๋Œ€ํ•œ ์šฐ๋ฆฌ์˜ ์ด์ „ ์ฝ”๋“œ๋ฅผ ์ด์™€ ๊ฐ™์ด ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— combineLatest() ๋Š” ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค.

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()๋Š” ๋‘ source๋“ค๋กœ ๋ถ€ํ„ฐ ๊ฐ€์žฅ ์ตœ์‹ ์˜ ๊ฒƒ๋“ค์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ๋‘๊ฐœ์˜ source๋“ค ์ค‘์—์„œ ํ•˜๋‚˜๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ๋ฐœ์ƒํ•˜์ง€(emitted) ์•Š์•˜๋‹ค๋ฉด, combineLatest() ๋Š” output stream ์— data ์ด๋ฒคํŠธ๋ฅผ ์ƒ์‚ฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์ด ์œ„์˜ ASCII ๋‹ค์ด์–ด ๊ทธ๋žจ์„ ๋ณธ๋‹ค๋ฉด, ์ฒซ stream์ด a ๋ฅผ ๋ฐœ์ƒ์‹œ์ผฐ(emit)์„๋•Œ ์•„๋ฌด๊ฒƒ๋„ ์ถœ๋ ฅ๋˜์ง€ ์•Š์•˜์Œ์„ ์•Œ ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. ๋‘๋ฒˆ์งธ stream์ด ๊ฐ’ b ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ(emit) ๋•Œ, ์ถœ๋ ฅ๊ฐ’์„ ์ƒ์‚ฐํ•  ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ์‹œ์ž‘ํ•  ๋•Œ 'close1' ๋ฒ„ํŠผ์— ๋Œ€ํ•œ click์„ ํ‰๋‚ด๋‚ด๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ๋จธ๋ฌด๋ฅผ ๊ฒ๋‹ˆ๋‹ค.

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/ )์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ € ์ฝ”๋“œ ์กฐ์ž‘์€ ์ž‘๊ณ  ๊ฒฌ๊ณ ํ•ฉ๋‹ˆ๋‹ค.: ์ € ์ฝ”๋“œ๋“ค์€ ์ ์ ˆํ•œ ๊ณ ๋ ค์˜ ๋ถ„๋ฆฌ(seperation of concerns)๋กœ ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ
๊ฐ๊ฐ์˜ ์‘๋‹ต(response)๋“ค์„ ์บ์‰ฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ํŠน์ง•์„ ์ด๋ฃน๋‹ˆ๋‹ค. functional ์Šคํƒ€์ผ์€ ์ข€ ๋” ๋ช…๋ นํ˜•๋ณด๋‹ค๋Š” ์„ ์–ธ์ ์œผ๋กœ ๋ณด์ด๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.: ์šฐ๋ฆฌ๋Š” ์‹คํ–‰ํ•  ๋ช…๋ น์˜ ์ˆœ์„œ๋ฐฐ์—ด์„ ์ฃผ์ง€ ์•Š๊ณ , stream๋“ค๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ •์˜ํ•จ์œผ๋กœ์จ ์–ด๋–ค ๊ฒƒ์ด ๋ฌด์—‡์ธ์ง€๋ฅผ ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์šฐ๋ฆฌ๋Š” Rx๋ฅผ ๊ฐ€์ง€๊ณ  ์ปดํ“จํ„ฐ์—๊ฒŒ suggestion1Stream ์€ ์ตœ์‹  ์‘๋‹ต(response)์œผ๋กœ ๋ถ€ํ„ฐ ํ•œ๋ช…์˜ ์‚ฌ์šฉ์ž์™€ ๊ฒฐํ•ฉ์‹œํ‚จ 'close 1' stream ์ด๋ผ๊ณ  ์ด์•ผ๊ธฐ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ทธ ์ตœ์‹  ์‘๋‹ต(response)์€ ํ”„๋กœ๊ทธ๋žจ์ด ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ refresh ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ null ์ด ๋œ๋‹ค๊ณ  ์ด์•ผ๊ธฐ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

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

๋‹ค์Œ์— ์˜ค๋Š” ๊ฒƒ

๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด Reactive Programming ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ Rx* ๋ฅผ ์ƒ๊ฐํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, tranform, combine, Observable๋“ค ์ƒ์„ฑ์— ๋Œ€ํ•œ ๋งŽ์€ ํ•จ์ˆ˜( https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md ) ์— ์ต์ˆ™ํ•ด์ง€๊ธฐ ์œ„ํ•ด์„œ ์‹œ๊ฐ„์„ ๋‚ด์„ธ์š”. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ์ด๋“ค ํ•จ์ˆ˜๋ฅผ stream ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, 'RxJava's very useful documentation with marble diagrams'( https://github.com/ReactiveX/RxJava/wiki/Creating-Observables )๋ฅผ ๋ณด์„ธ์š”. ๋ญ”๊ฐ€๋ฅผ ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์„ ๋•Œ๋งˆ๋‹ค, ๋‹ค์ด์–ด ๊ทธ๋žจ์„ ๊ทธ๋ฆฌ๊ณ  ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ๊ฐํ•˜๊ณ , ํ•จ์ˆ˜๋“ค์˜ ๊ธด ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด๊ณ , ๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ ํ•œ๋ฒˆ๋” ์ƒ๊ฐํ•ด ๋ณด์„ธ์š”. ์ด๋Ÿฐ ์ž‘์—…ํ๋ฆ„์ด ์ œ ๊ฒฝํ—˜์ƒ ํšจ๊ณผ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

Rx* ๋ฅผ ์ด์šฉํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์‚ฌ์šฉ๋ฒ•์„ ์‹œ์ž‘ํ•˜๋ฉด, Cold vs Hot Observales ( https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables )์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ ˆ๋Œ€์ ์œผ๋กœ ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ์ด๊ฑธ ๋ฌด์‹œํ•œ๋‹ค๋ฉด ๋‚˜์ค‘์— ํ›„ํšŒํ•˜๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค. ๊ฒฝ๊ณ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  Rx* ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” Side effect๋“ค๊ณผ ๊ฐ™์€ ์ด์Šˆ์™€ ์นœ์ˆ™ ํ•ด์ง์œผ๋กœ์จ ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ธฐ์ˆ ์„ ๋” ๊ฐˆ๊ณ  ๋‹ฆ์œผ์„ธ์š”.

ํ•˜์ง€๋งŒ, Reactive Programming์€ ๋‹จ์ˆœํžˆ Rx๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๋•Œ๋•Œ๋กœ Rx ์—์„œ ๋งž๋‹ฅ๋œฐ๋ฆฌ๊ฒŒ ๋˜๋Š” ๋ณ€๋•์—†์ด ์ž‘์—…ํ•˜๊ธฐ์— ์ง๊ด€์ ์ธ Bacon.js( http://baconjs.github.io/ )๋„ ์žˆ์Šต๋‹ˆ๋‹ค. Elm Language( http://elm-lang.org/ )๋Š” ๊ทธ ์ž์‹ ์˜ ์นดํ…Œ๊ณ ๋ฆฌ์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.: Functional Reactive Programming ์–ธ์–ด๋ฅผ Javascript + HTML + CSS ๋กœ ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค. time travelling debugger( http://debug.elm-lang.org/ )๋กœ ํŠน์ง•๋ฉ๋‹ˆ๋‹ค. ๊ฝค ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.

Rx ๋Š” event-heavy frontends ์™€ ์•ฑ๋“ค์— ๋Œ€ํ•ด ํ›Œ๋ฅญํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฐ ๊ฑด ๋‹จ์ง€ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ดํŠธ๋งŒ์˜ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ์—์„œ๋„ ํ›Œ๋ฅญํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ๊ทผ์ฒ˜์—์„œ๋„์š”. ์‚ฌ์‹ค RxJava ๋Š” Netflix์˜ API์—์„œ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.( http://techblog.netflix.com/2013/02/rxjava-netflix-api.html ) Rx ๋Š” ํŠน์ • ์–ธ์–ดํ˜น์€ ํ•˜๋‚˜์˜ ์ง€์ •๋œ ํƒ€์ž…์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ตญํ•œ๋œ framework๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์‚ฌ์‹ค event-driven ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐํ•  ๋•Œ ์—ฌ๋Ÿฌ๋ถ„์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ž…๋‹ˆ๋‹ค.

์ด ํŠœํ„ฐ๋ฆฌ์–ผ์ด ์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด, ํŠธ์œ—ํ•ด์ฃผ์„ธ์š”.( https://twitter.com/intent/tweet?original_referer=https%3A%2F%2Fgist.github.com%2Fstaltz%2F868e7e9bc2a7b8c1f754%2F&text=The%20introduction%20to%20Reactive%20Programming%20you%27ve%20been%20missing&tw_p=tweetbutton&url=https%3A%2F%2Fgist.github.com%2Fstaltz%2F868e7e9bc2a7b8c1f754&via=andrestaltz )