[๋ฒ์ญ] ์๋ฐ์คํฌ๋ฆฝํธ ๋ฐ์์ฑ(Reactivity)์ ๋ํ ๊ฐ์ฅ ์ข์ ์ค๋ช
Gregg Pollack์ The Best Explanation of JavaScript Reactivity ๐๋ฅผ ๋ฒ์ญํ ๊ธ์ด๋ค.
SPA ํ๋ ์์ํฌ์์ ์ปดํฌ๋ํธ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ ๋๋ง์ด ๋ค์ ์คํ๋๋ ๊ฒ์ฒ๋ผ, ๊ฐ์ฒด์ ์์ฑ์ด ๋ณ๊ฒฝ๋ ๋ ์ด๋ค ์ฝ๋๊ฐ ์๋์ผ๋ก ์คํ๋๊ฒ ํ๋ค๋ฉด ๊ทธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฐ์์ ์ด๋ผ๊ณ ํ ์ ์๋ค. ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์ Object.defineProperty
ํจ์๋ฅผ ์ฌ์ฉํด getter, setter ํจ์๋ฅผ ๋ค์ ํ ๋นํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช
ํ๋ ๊ธ์ด๋ค.
๋ง์ ํ๋ก ํธ์๋ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ ์์ํฌ(Angular, React, Vue, etc)๋ ์์ ๋ง์ ๋ฐ์์ฑ(Reactivity) ์์ง์ ๊ฐ์ง๊ณ ์๋ค. ๋ฐ์์ฑ์ด ๋ฌด์์ธ์ง, ์ด๋ป๊ฒ ๋์ํ๋์ง๋ฅผ ์ดํดํจ์ผ๋ก์จ ๋น์ ์ ๊ฐ๋ฐ ๊ธฐ์ ์ ํฅ์ํ ์ ์๊ณ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ ์์ํฌ๋ฅผ ๋ณด๋ค ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค. ์๋์ ์์๊ณผ ๋น๋์ค๋ฅผ ํตํด ์ฐ๋ฆฌ๋ Vue ์์ค ์ฝ๋์์ ํ์ธํ ์ ์๋ ๊ฒ๊ณผ ๊ฐ์ ์ข ๋ฅ์ Reactivity๋ฅผ ๊ตฌํํ ๊ฒ์ด๋ค.
๊ธ์ ์ฝ๋ ๋์ ๋น๋์ค๋ฅผ ์์ฒญํ๊ณ ์ถ๋ค๋ฉด ์ด ์๋ฆฌ์ฆ์ ๋ค์ ์์์ ๋ณด๋ฉด์ Vue๋ฅผ ๋ง๋ Evan You์ ํจ๊ป ๋ฐ์์ฑ๊ณผ ํ๋ก์์ ๋ํด ํ ๋ก ํ๋ ๋ด์ฉ์ ํ์ธํ๊ธธ ๋ฐ๋๋ค.
๋ฐ์์ฑ ์์คํ
Vue์ ๋ฐ์์ฑ ์์คํ ์ ์ฒ์ ๋ณด๋ฉด ๋ง์น ๋ง๋ฒ์ฒ๋ผ ๋๊ปด์ง ์ ์๋ค. ๊ฐ๋จํ Vue ์ฑ์ ํ์ธํด๋ณด์.
Vue๊ฐ ๋ง์ฝ price
๊ฐ์ด ๋ณํ๋ค๋ ์ฌ์ค์ ์๊ฒ ๋๋ค๋ฉด ์๋ 3๊ฐ์ง ์ผ์ ํ ๊ฒ์ด๋ค.
- ์นํ์ด์ง์
price
๊ฐ์ ์ ๋ฐ์ดํธํ๋ค. price * quantity
๋ฅผ ๋ค์ ๊ณ์ฐํ๊ณ ํ์ด์ง๋ฅผ ์ ๋ฐ์ดํธํ๋ค.totalPriceWithTax
ํจ์๋ฅผ ๋ค์ ํธ์ถํ๊ณ ํ์ด์ง๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
ํ์ง๋ง ์ ๊น, ๋น์ ์ ์ด๋ฐ ์๋ฌธ์ ๊ฐ์ง๊ฒ ๋ ๊ฒ์ด๋ค. Vue๋ price
๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฌด์์ ์
๋ฐ์ดํธํด์ผ ํ๋์ง ์ด๋ป๊ฒ ์๋ฉฐ, ์ด๋ป๊ฒ ๋ชจ๋ ๊ฒ์ ์ถ์ ํ๊ณ ์๋๊ฐ?
Vue๊ฐ ํ๋ ์ผ์ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ๋์ํ๋ ๋ฐฉ์์ด ์๋๋ค.
๋ค์ ๋งํด, ์ฐ๋ฆฌ๊ฐ ์๋ฌธ์ ๊ฐ์ง๊ณ ์๋ ๋ฌธ์ ๋ ์ผ๋ฐ์ ์ธ ํ๋ก๊ทธ๋จ ๋์ ๋ฐฉ์์ด ์๋๋ผ๋ ๋ง์ด๋ค. ์๋ฅผ ๋ค์ด ๋ง์ฝ ์๋์ ์ฝ๋๋ฅผ ์คํํ๋ค๋ฉด:
๋ฌด์์ด ์ถ๋ ฅ๋ ๊ฒ์ผ๋ก ์๊ฐํ๋๊ฐ? ๋ณดํต์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ผ์ 10
์ ์ถ๋ ฅํ ๊ฒ์ด๋ค.
Vue์์๋ ์ฐ๋ฆฌ๋ price
๋ quantity
๊ฐ ์
๋ฐ์ดํธ๋๋ฉด total
๋ ์
๋ฐ์ดํธ๋๊ธธ ๋ฐ๋๋ค. ๋ฐ๋ก ๋ค์๊ณผ ๊ฐ์ด ์ถ๋ ฅ๋๊ธธ ์ํ๋ค:
ํ์ง๋ง ๋ถํํ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ ๋ฐ์ํ์ด ์๋๋ผ ์ ์ฐจ์ (procedural) ์ธ์ด๋ผ์ ์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ํ๋ ๊ธฐ๋ฅ์ ๋ฐ๋ก ์ ๊ณตํ์ง ์๋๋ค. total
๋ณ์๊ฐ ๋ฐ์์ ์ด ๋๋๋ก ๋ง๋ค๊ธฐ ์ํด์๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ด ํ์ํ๋ค.
โ ๏ธ ๋ฌธ์
total
๊ฐ์ ๊ณ์ฐํ๋ ์ฝ๋๋ฅผ ์ ์ฅํด ๋ ํ์๊ฐ ์๊ณ , price
๋ quantity
๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ๊ทธ ์ฝ๋๋ฅผ ๋ค์ ์คํํด์ผ ํ๋ค.
โ ํด๊ฒฐ์ฑ
์ฐ์ ์ฐ๋ฆฌ์ ์ ํ๋ฆฌ์ผ์ด์
์๊ฒ ๋ค์์ ๋ฉ์์ง๋ฅผ ์ ๋ฌํ ๋ฐฉ๋ฒ์ด ํ์ํ๋ค, โ๋ด๊ฐ ์คํํ๋ ค๋ ์ฝ๋๊ฐ ํ๋ ์๋๋ฐ, ์ด๊ฑธ ์ ์ฅํด ๋ฌ. ๋์ค์ ๋ค๊ฐ ๋ค์ ์คํํ ํ์๊ฐ ์๊ธธ๊ฑฐ์ผโ. ๊ทธ๋ฐ ์ผ์ด ๊ฐ๋ฅํ๋ค๋ฉด ์ฐ๋ฆฌ๋ ์ฝ๋๋ฅผ ํ๋ฒ ์คํํ ํ์, price
๋ quantity
๊ฐ์ด ๋ฐ๋ ๋ ์์ ์ ์ฅ๋ ์ฝ๋๋ฅผ ๋ค์ ์คํํ๋ฉด ๋๋ค.
์ด๋ ํจ์๋ฅผ ๊ธฐ๋กํ๋ ๋ฐฉ๋ฒ์ ํตํด ๊ตฌํํ ์ ์๋ค.
target
๋ณ์์ ์ต๋ช
ํจ์๋ฅผ ํ ๋นํด์ ์ฌ์คํํ ์ฝ๋๋ฅผ ์ ์ฅํ๊ณ , record
ํจ์๋ฅผ ํธ์ถํ๋ค. ES6 ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ฒ๋ผ ์์ฑํ ์ ์๋ค.
ํจ์ record
๋ ๊ฐ๋จํ ์๋์ฒ๋ผ ์ ์ํ๋ค.
storage ๋ฐฐ์ด์ target ํจ์๋ฅผ ์ ์ฅํ๋ค.
target
(์ฝ๋์์๋ { total = price * quantity }
)์ ์ ์ฅํ์ผ๋ ๋์ค์ ๋ค์ ์คํํ ์ ์๋ค. ์ ์ฅํ ๋ชจ๋ ํจ์๋ฅผ ์คํํ replay
ํจ์๊ฐ ํ์ํ ๊ฒ์ด๋ค.
replay
ํจ์๋ฅผ ์คํํ๋ฉด storage
๋ฐฐ์ด์ ์ ์ฅํ ๋ชจ๋ ์ต๋ช
ํจ์๋ฅผ ์คํํ๋ค.
์ด์ ๊ฐ๋จํ ๋ค์์ฒ๋ผ ํ๋ฉด ๋๋ค.
๊ฐ๋จํ์ง ์์๊ฐ? ์๋์ ์ฝ๋๋ฅผ ํตํด ์ ์ฒด ํ๋ฆ์ ๋ค์ ํ ๋ฒ ์ดํด๋ณด๊ธฐ ๋ฐ๋๋ค.
โ ๏ธ ๋ฌธ์
ํ์ํ ๋งํผ target์ ๊ธฐ๋กํด ๋ ์ ์์ง๋ง, ์ฑ์ ํ์ฅ์ฑ์ ์ํด ๋ณด๋ค ๊ฐ๋ ฅํ ์๋ฃจ์ ์ ๋ง๋๋ ํธ์ด ์ข์ ๊ฒ์ด๋ค. target ๋ชฉ๋ก์ ์ ์งํ๋ฉด์ ์ฐ๋ฆฌ๊ฐ ํ์ํ ๋ ์ฌ์คํํ ์ ์๋๋ก ์๋ ค์ฃผ๋ ํด๋์ค ๊ฐ์ ๊ฒ ๋ง์ด๋ค.
โ ํด๊ฒฐ์ฑ : ์์กด ํด๋์ค
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ ํ๋๋ ์์ ๋์์ ํ์ค ํ๋ก๊ทธ๋๋ฐ ์ต์ ๋ฒ(observer) ํจํด์ ๊ตฌํํ๋ ์์กด ํด๋์ค๋ก ์บก์ํ(encapsulation)ํ๋ ๊ฒ์ด๋ค.
๊ทธ๋์, target ๊ฐ์ ์ข ์๋ฌผ(dependencies)์ ๊ด๋ฆฌํ๊ธฐ ์ํด ์๋ฐ์คํฌ๋ฆฝํธ ํด๋์ค๋ฅผ ๋ง๋ ๋ค๋ฉด ์๋์ ๊ฐ์ ํํ๊ฐ ๋ ๊ฒ์ด๋ค.
class Dep { // dependency์ ์ฝ์
constructor() {
this.subscribers = []
// subscribers๋ ํด๋์ค์ ์ข
์๋์ด ์๋ target ํจ์๋ค์ ์ ์ฅํ๋ ๋ฐฐ์ด.
// notify() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด ๋ฐฐ์ด์ ์ ์ฅ๋ ํจ์๋ค์ด ์คํ๋์ด์ผ ํ๋ค.
}
depend() { // ์์ ์์ฑํ record ํจ์๋ฅผ ๋์ฒดํ๋ค.
if (target && !this.subscribers.includes(target)) {
// target์ด subscribers์ ์ ์ฅ๋์ด ์์ง ์์์ผ ํ๋ค.
this.subscribers.push(target)
}
}
notify() { // ์์ ์์ฑํ replay ํจ์๋ฅผ ๋์ฒดํ๋ค.
this.subscribers.forEach(sub => sub()) // target ๋๋ observer๋ฅผ ์คํํ๋ค.
}
}
์ด์ storage
๋์ subscribers
์ ์ต๋ช
ํจ์๋ฅผ ์ ์ฅํ๋ฉฐ, record
ํจ์ ๋์ depend
ํจ์๋ฅผ ์ฌ์ฉํ๋ค. ๊ทธ๋ฆฌ๊ณ replay
ํจ์ ๋์ notify
ํจ์๋ฅผ ์ฌ์ฉํ๋ค. ์ด ํด๋์ค๋ฅผ ์๋์์ผ ๋ณด์.
const dep = new Dep() // ์ธ์คํด์ค ์์ฑ
let price = 5
let quantity = 2
let total = 0
let target = () => { target = price * quantity }
dep.depend() // subscribers์ target ํจ์๋ฅผ ์ถ๊ฐํ๋ค.
target() // target ํจ์๋ฅผ ์คํํ๋ค.
console.log(total) // => 10 .. ์ํ๋ ๊ฒฐ๊ณผ๊ฐ ๋ง๋ค.
price = 20
console.log(total) // => 10 .. total์ด ์๋์ผ๋ก ์
๋ฐ์ดํธ ๋์ง ์์๋ค.
dep.notify() // subscribers์ ๋ฑ๋ก๋ ํจ์๋ค์ ์คํํ๋ค.
console.log(total) // => 40 .. ์ ๋๋ก ๋ ๊ฒฐ๊ณผ๋ฅผ ์ป์๋ค.
์ ์๋ํ๋ค. ์ด์ ์ฐ๋ฆฌ์ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ ๊ฒ ๊ฐ๋ค. ์ฌ์ ํ ์ด์ํ๊ฒ ๋๊ปด์ง๋ ๊ฑด target
์ ๋ฑ๋กํ๊ณ ์คํํ๋ ๋ฐฉ์์ด๋ค.
โ ๏ธ ๋ฌธ์
๊ฐ๋ฐ์ด ์งํ๋๋ฉด ๋ณ์๋ง๋ค Dep ํด๋์ค๋ฅผ ์์ฑํ๊ฒ ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์
๋ฐ์ดํธ๋ฅผ ๊ฐ์งํ๋ ์ญํ ์ ํ๋ ์ต๋ช
ํจ์(target)๋ฅผ ์์ฑํ๋ ๊ณผ์ ์ ์บก์ํํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค. watcher
ํจ์๊ฐ ์ด๋ฐ ์ญํ ์ ํ ์ ์๋ค.
๊ทธ๋ฌ๋ฉด ์๋์ ์ฝ๋๋ฅผ ์คํํ๋ ๋์ :
์๋์ฒ๋ผ ์คํํ ์ ์๋ค:
โ ํด๊ฒฐ์ฑ : Watcher ํจ์
Watcher ํจ์ ๋ด๋ถ์์ ๋ช๊ฐ์ง ๊ฐ๋จํ ์์ ์ ํ ์ ์๋ค.
function watcher(myFunc) {
target = myFunc // ํ์ฌ ์ฒ๋ฆฌํ๊ณ ์๋ target์ผ๋ก myFunc์ ์ค์ ํ๋ค.
dep.depend() // target์ ์ข
์๋ฌผ(=์ข
์๋ ์ฝ๋)๋ก ์ถ๊ฐํ๋ค.
target() // target์ ์คํํ๋ค.
target = null // target์ ์ด๊ธฐํํ๋ค.
}
ํ์ธํ ์ ์๋ ๊ฒ์ฒ๋ผ watcher
ํจ์๋ myFunc
๋ฅผ ์ธ์๋ก ์ ๋ฌ๋ฐ๊ณ , ์ ์ญ ๋ณ์์ธ target
์ผ๋ก ํ ๋นํ๊ณ , dep.depend()
๋ฅผ ํธ์ถํด์ subscriber์ ์ถ๊ฐํ๊ณ , target
์ ํธ์ถํ๋ค. ๊ทธ๋ฆฌ๊ณ target
๋ฅผ ์ด๊ธฐํํ๋ค.
๋น์ ์ ์๋ง ์ target
์ ํจ์์ ์ง์ ์ ๋ฌํ์ง ์๊ณ ์ ์ญ ๋ณ์๋ก ์ค์ ํ๋์ง ๊ถ๊ธํ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ ๊ตฌํํ ๋ฐ์๋ ์ด์ ๊ฐ ์์ผ๋ฉฐ, ์ด ๊ธ์ ๋ค ์ฝ์ผ๋ฉด ๊ทธ ์ด์ ๊ฐ ๋ช
ํํด์ง๊ณ ์ข์ ๋ฐฉ๋ฒ์ด์๋ค๋ ๊ฒ์ ์๊ฒ ๋ ๊ฒ์ด๋ค.
โ ๏ธ ๋ฌธ์
์ฐ๋ฆฌ๋ ํ๋์ Dep
ํด๋์ค๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ์ ๋ง๋ก ์ํ๋ ๊ฒ์ ๋ณ์๋ง๋ค ๊ทธ๋ง์ Dep ํด๋์ค๋ฅผ ๊ฐ์ง๋ ๊ฒ์ด๋ค. ๋ ์งํํ๊ธฐ ์ ์ ๊ฐ๋ค์ ๊ฐ์ฒด์ ์์ฑ์ผ๋ก ์ฎ๊ธฐ๋๋ก ํ์.
๊ฐ๊ฐ์ ์์ฑ(price
์ quantity
)์ด ๊ฐ๊ฐ Dep ํด๋์ค๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ์ ์๋์ ๊ฐ์ ํด๋ณด์.
์ด์ ๋ ์๋์ ์ฝ๋๋ฅผ ์คํํ๋ค.
watcher์ ์ ๋ฌ๋ ์ต๋ช
ํจ์(target) ๋ด๋ถ์์ data.price
๊ฐ์ด ์ฌ์ฉ๋์๋ค. ์ฌ์คํํ ์ฝ๋์์ data.price
๊ฐ์ด ์ฌ์ฉ๋๊ณ ์์ผ๋ price
์์ฑ์ ์ฐ๊ฒฐ๋ Dep ํด๋์ค์ subscriber ๋ฐฐ์ด(์ ์ฅ์)์ ์ด ์ต๋ช
ํจ์๋ฅผ ์ถ๊ฐ(dep.depend()
)ํ๊ณ ์ถ๋ค. ์ต๋ช
ํจ์ ๋ด๋ถ์์ data.quantity
๊ฐ๋ ์ฌ์ฉ๋์์ผ๋ data.quantity
์ ์ฐ๊ฒฐ๋ Dep ํด๋์ค์๋ ๊ฐ์ ์์
์ ํ๊ณ ์ถ๋ค.
๋ง์ฝ data.price
๋ฅผ ์ฐธ์กฐํ๋ ๋ ๋ค๋ฅธ ์ต๋ช
ํจ์๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด, ๊ทธ ํจ์๋ price
์์ฑ์ Dep ํด๋์ค์๋ง ์ถ๊ฐํ๊ณ ์ถ๋ค.
์ถ๊ฐ๋๋ watcher๋ ์์ฑ๋ค ์ค ํ๋์๋ง ์ ์ฉ๋ ๊ฒ์ด๋ค
์ด๋ ์์ ์ price
์ subscriber์ ์ฐ๊ฒฐ๋ dep.notify()
๊ฐ ํธ์ถ๋์ด์ผ ํ ๊น? ๋๋ ๊ทธ ์๊ฐ์ด price
์์ฑ์ ๊ฐ์ด ํ ๋น๋์์ ๋๊ฐ ๋๊ธธ ๋ฐ๋๋ค. ๋ชจ๋ ๊ฒ์ด ๊ตฌํ๋๋ฉด ์ฝ์์์ ์๋์ ๊ฐ์ ์ถ๋ ฅ์ ๋ณด๊ณ ์ถ๋ค.
์ฐ๋ฆฌ๋ data ์์ฑ(price
๋ quantity
)์ ์ํ๋ ๋์์ ์ฐ๊ฒฐํ (hook into) ์ด๋ค ๋ฐฉ๋ฒ์ด ํ์ํ๋ค. ๊ทธ๋ฌ๋ฉด target ํจ์ ๋ด๋ถ์์ ์ด๋ค ์์ฑ์ด ์ฐธ์กฐ๋๋ฉด ๊ทธ ์์ฑ์ subscriber ๋ฐฐ์ด์ target์ ์ ์ฅํ ์ ์๊ณ , ์์ฑ์ด ๋ณ๊ฒฝ๋๋ฉด subscribers ๋ฐฐ์ด์ ์ ์ฅ๋ ํจ์๋ฅผ ์คํํ๋๋ก ๋ง๋ค ์ ์๋ค.
โ ํด๊ฒฐ์ฑ : Object.defineProperty()
ES5์์ ์ ๊ณตํ๋ Object.defineProperty ํจ์์ ๋ํด ๊ณต๋ถํ ํ์๊ฐ ์๋ค. ์ด ํจ์๋ ์ฐ๋ฆฌ์๊ฒ ๊ฐ์ฒด ์์ฑ์ getter์ setter ํจ์๋ฅผ ์ ์ํ ์ ์๋๋ก ํ๋ค. ์ด ํจ์๋ฅผ Dep ํด๋์ค์ ์ ์ฉํ๊ธฐ ์ ์ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฒ์ ์ดํด๋ณด์.
let data = { price: 5, quantity: 2 }
Object.defineProperty(data, 'price', { // data์ price ์์ฑ์ ๋ํด์๋ง ์ ์ํ๋ค.
get() { // get ํจ์ ์์ฑ. price์ ๊ฐ์ ์ฐธ์กฐํ๋ ์ญํ
console.log('I was accessed')
}
set(newValue) { // set ํจ์ ์์ฑ. price์ ์๋ก์ด ๊ฐ์ ํ ๋นํ๋ ์ญํ
console.log('I was changed')
}
})
๋ณด์ด๋ ๋ฐ์ ๊ฐ์ด ๋จ์ง 2์ค์ด ๋ก๊ทธ์ ํ์๋๋ค. ํ์ง๋ง ์ค์ ๋ก๋ ์ด๋ค ๊ฐ๋ ๊ฐ์ ธ์ค๊ฑฐ๋(get) ํ ๋นํ์ง(set) ์๋๋ค. ์ฐ๋ฆฌ๊ฐ get, set ํจ์๋ฅผ ์๋ก ๋ฎ์ด์์ ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฌ๋ ๋ค์ ํ์ํ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋๋ก ํ์. get()
ํจ์๋ ๊ฐ์ ๋ฐํํด์ผ ํ๊ณ , set()
ํจ์๋ ๊ฐ์ ๊ฐฑ์ ํด์ผ ํ๋ค. ๊ทธ๋ฅผ ์ํด intervalValue
๋ผ๋ ๋ณ์์ ํ์ฌ price
๊ฐ์ ์ ์ฅํ๋ค.
internalValue
์์ ์ค์ ๊ฐ์ ๊ด๋ฆฌํ๋ค.
์ด์ ์ฐ๋ฆฌ์ get, set ํจ์๋ ์ ๋๋ก ๋์ํ๋ค. ์ฝ์์๋ ๋ฌด์์ด ์ถ๋ ฅ๋ ๊น?
์ด๋ ๊ฒ ๊ฐ์ ๊ฐ์ ธ์ค๊ณ (get) ํ ๋นํ ๋(set) ์๋ฆผ์ ๋ฐ์ ์ ์๊ฒ ๋์๋ค. ๊ทธ๋ฆฌ๊ณ ์ฌ๊ท(recursion)๋ฅผ ์กฐ๊ธ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์์ฑ์์ ์ด ๊ธฐ๋ฅ์ด ์๋ํ๋๋ก ํ ์ ์์ ๊ฒ์ด๋ค.
Object.keys ํจ์๋ฅผ ์ฌ์ฉํด์ data ๊ฐ์ฒด์ ์กด์ฌํ๋ ๋ชจ๋ ์์ฑ์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ ์ฉํ๋ค
์ด์ ๋ชจ๋ ์์ฑ์ด getter์ setter ํจ์๋ฅผ ๊ฐ์ง๊ฒ ๋ ๊ฒ์ ์ฝ์์์ ํ์ธํ ์ ์๋ค.
๐ ์ง๊ธ๊น์ง์ ์์ด๋์ด๋ฅผ ํฉ์น๊ธฐ
์์ ์ฝ๋์ฒ๋ผ price
์ ๊ฐ์ ์ฌ์ฉ(get)ํ๋ค๋ฉด, ์์ ์ฝ๋(target)๊ฐ price
๊ฐ์ ์์กดํ๊ณ ์๋ค๋ ์ฌ์ค์ ๊ธฐ๋กํด๋๊ณ ์ถ๋ค. ๊ทธ๋ ๊ฒ ํด ๋๋ค๋ฉด price
๊ฐ์ ๋ณ๊ฒฝ์ด๋ ์๋ก์ด ๊ฐ์ ํ ๋น์ด ์ ์ฅํด๋ ์ฝ๋๋ฅผ ์ฌ์คํํ ์ ์๋ ๊ณ๊ธฐ(trigger)๊ฐ ๋๋๋ก ๋ง๋ค ์ ์๋ค. ์ฐ๋ฆฌ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ด๋ค ์ฝ๋๊ฐ price
์ ์์กดํ๊ณ ์๋์ง ์๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ getter์ setter๋ฅผ ์๋์ฒ๋ผ ๋์ํ๋๋ก ์๊ฐํ ์ ์๋ค.
Get => ์ด ์ต๋ช ํจ์๋ฅผ ๊ธฐ์ตํด๋ผ, ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋ ๋ค์ ์คํํ ๊ฒ์ด๋ค.
Set => ์ ์ฅ๋ ์ต๋ช ํจ์๋ฅผ ์คํํด๋ผ. ๊ฐ์ ๋ฐฉ๊ธ ๋ณ๊ฒฝ๋์๋ค.
Dep Class์ ๊ฒฝ์ฐ์๋
Price๊ฐ ์ฌ์ฉ๋์๋ค (get) => dep.depend()
ํจ์๋ฅผ ํธ์ถํด์ ํ์ฌ target
์ ์ ์ฅํด๋ผ.
Price๊ฐ ํ ๋น๋์๋ค (set) => price์ dep.notify()
๋ฅผ ํด์ ๋ชจ๋ targets
๋ฅผ ์ฌ์คํํด๋ผ.
์ด ๋๊ฐ์ง ์์ด๋์ด๋ฅผ ์กฐํฉํ ์ต์ข ์ฝ๋๋ฅผ ์ดํด๋ณด์.
let data = { price: 5, quantity: 2 }
let target = null
// ์์์ ์ ์ธํ Dep ํด๋์ค์ ๋์ผํ๋ค.
class Dep {
constructor() {
this.subscribers = []
}
depend() {
if (target && !this.subscribers.includes(target)) {
// target์ด ์กด์ฌํ๋ฉฐ subscribers์ ํฌํจ๋์ด ์์ง ์์ ๋๋ง ์ถ๊ฐํ๋ค.
this.subscribers.push(target)
}
}
notify() {
this.subscribers.forEach(sub => sub())
}
}
// data ๊ฐ์ฒด๊ฐ ๊ฐ์ง๊ณ ์๋ ๋ชจ๋ ์์ฑ์ ์ ์ฉํ๋ค
Object.keys(data).forEach(key => {
let internalValue = data[key]
// ๊ฐ๊ฐ์ ์์ฑ์ ์๊ธฐ๋ง์ Dep ํด๋์ค ์ธ์คํด์ค๋ฅผ ๊ฐ์ง๋ค.
const dep = new Dep()
// data ๊ฐ์ฒด ์์ฑ์ getter, setter๋ฅผ ์ฌ์ค์ ํ๋ค.
Object.defineProperty(data, key, {
get() {
// getter๊ฐ ํธ์ถ๋ ์์ ์ target์ ๊ธฐ์ตํ๋๋ก ํ๋ค.
dep.depend()
return internalValue
},
set(newVal) {
internalValue = newVal
dep.notify() // ์ ์ฅ๋ ํจ์(target)๋ฅผ ์ฌ์คํ
}
})
})
// watcher๋ ๋ ์ด์ dep.depend๋ฅผ ํธ์ถํ์ง ์๋๋ค.
// ์๋ํ๋ฉด get ๋ฉ์๋ ์์์ ์๋์ผ๋ก ํธ์ถ๋๊ธฐ ๋๋ฌธ์ด๋ค.
function watcher(myFunc) {
target = myFunc
target() // ํ ๋น๋ ์ฝ๋๋ ๋ฌด์กฐ๊ฑด 1๋ฒ์ ์คํ๋์ด์ผ ํ๋ค.
target = null
}
// watcher์ ์ ๋ฌ๋ ์ด ์ต๋ช
ํจ์์์ price์ quantity์ getter ํจ์๊ฐ ์คํ๋๋ค.
// watcher๊ฐ ์คํ๋ ์์ ์์ ์ ์ญ ๋ณ์ target์๋ watcher์ ์ธ์๋ก ์ ๋ฌ๋ ์ต๋ช
ํจ์๊ฐ ํ ๋น๋๋ค
watcher(() => {
data.total = data.price * data.quantity
})
์ด์ ์ฝ๋๋ฅผ ์คํํ๋ฉด ์ฝ์์ ๋ฌด์์ด ์ถ๋ ฅ๋๋์ง ํ์ธํด๋ณด์.
์ฐ๋ฆฌ๊ฐ ํ๊ณ ์ถ์๋ ๊ฒ๊ณผ ์ ํํ ์ผ์นํ๋ค! price
์ quantity
๋ชจ๋ ํ์คํ ๋ฐ์ํ๋ค! watcher ํจ์์ ์ ๋ฌํ ์ต๋ช
ํจ์๋ price
๋๋ quantity
๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ฌ์คํ๋๊ณ ์๋ค.
Vue ๋ฌธ์์์ ๊ฐ์ ธ์จ ์ด ๊ทธ๋ฆผ์ ์ด์ ์ดํด๊ฐ ๋ ๊ฒ์ด๋ค.
Data์ getter, setter๋ฅผ ๊ฐ์ง๊ณ ์๋ ์๋ฆ๋ค์ด ๋ณด๋ผ์ ๋๊ทธ๋ผ๋ฏธ๊ฐ ๋ณด์ด๋๊ฐ? ์๋ง ์ต์ํ๊ฒ ๋ณด์ผ ๊ฒ์ด๋ค! ๋ชจ๋ ์ปดํฌ๋ํธ ์ธ์คํด์ค๋ getter๋ฅผ ํตํด ์ข
์๋ฌผ์ ์์งํ๋(๋ถ์ ์ ์ ) watcher
์ธ์คํด์ค(ํ๋์)๋ฅผ ๊ฐ์ง๊ณ ์๋ค. setter๊ฐ ํธ์ถ๋๋ฉด Data๋ watcher์๊ฒ ์๋ฆผ(Notify)์ ๋ณด๋ด์ ์ปดํฌ๋ํธ re-render๋ก ์ด์ด์ง๊ฒ ํ๋ค. ์๋๋ ๋ด๊ฐ ์ฃผ์์ ์ถ๊ฐํ ๊ทธ๋ฆผ์ด๋ค.
์ด๋ค๊ฐ, ์ด์ชฝ์ด ์ฐ๋ฆฌ๊ฐ ์ง๊ธ๊น์ง ์ดํด๋ณธ ์ฝ๋๋ฅผ ๋ ์ฌ๋ฆฌ๊ฒ ํ๋ฉด์ ๋ ์ ์๋ฟ์ง ์๋๊ฐ??
์ฌ์ค Vue์ ๋ฐ์์ฑ ์์ง์ ์ด๊ฒ๋ณด๋ค ํจ์ฌ ๋ ๋ณต์กํ๋ค. ํ์ง๋ง ๋น์ ์ ์ด์ ๊ธฐ์ด๋ฅผ ์๊ฒ ๋์๋ค.
โช ์ง๊ธ๊น์ง ํ์ตํ ๋ด์ฉ
- ์ข
์๋ฌผ(์ข
์๋ ์ฝ๋, dependencies)๋ฅผ ์์งํ๋(
depend
) Dep class๋ฅผ ์ด๋ป๊ฒ ์ ์ธํ๊ณ , ์ด๋ป๊ฒ ์์กด ์ฝ๋๋ฅผ ์ฌ์คํํ๋์ง(notify
) - ์ข
์๋ฌผ๋ก ์ถ๊ฐ๋ ์ ์๋ ์คํ ์ฝ๋(
target
)๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด watcher๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๋์ง. - getter์ setter๋ฅผ ์์ฑํ๊ธฐ ์ํด Object.defineProperty()๋ฅผ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง
์ด ๋ค์์?
์ด ๊ธ์ ํตํด ๋ฐฐ์ด ๋ด์ฉ์ ํฅ๋ฏธ๋ฅผ ๋๊ผ๋ค๋ฉด ๋ค์ ๋จ๊ณ๋ Reactivity with Proxies๋ค. ๊ทธ๋ฆฌ๊ณ ๋ด๊ฐ Vue ๊ฐ๋ฐ์์ธ Evan You์ ํจ๊ป ์ด ์ฃผ์ ์ ๋ํด์ ์ด์ผ๊ธฐํ๋ ๋ฌด๋ฃ ๋์์๋ VueMastery.com์์ ๊ผญ ํ์ธํด ๋ณด๊ธธ ๋ฐ๋๋ค.