๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ‘“Vue

[Vue]Reactivity System, ๋ทฐ์—์„œ ๊ฐ’์ด ๋ฐ”๋€” ๋•Œ ์ผ์–ด๋‚˜๋Š” ์ผ

by ๋นˆ์„ฑ_ 2021. 12. 26.
๋ฐ˜์‘ํ˜•

Intro

์•ˆ๋…•ํ•˜์„ธ์š”. ๊ฐœ๋ฐœ์ž ์œ„์„ฑ๋นˆ์ž…๋‹ˆ๋‹ค. ์˜ค๋Š˜์€ Dynamic Components์— ์ด์–ด์„œ Vue์˜ Reactivity ์‹œ์Šคํ…œ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ์ € ๋จธ๋ฆฟ์†์—๋งŒ ์žˆ๋Š” ๊ฑฐ๋ณด๋‹จ ๊ธ€๋กœ ๋‚จ๊ธฐ๋Š” ๊ฒŒ ๊ธฐ์–ต๋„ ์ž˜ ๋˜๊ณ , ๊ธ€์„ ์“ฐ๋ฉด์„œ๋„ ๋” ์ฐพ์•„๋ณด๊ฒŒ ๋˜๋‹ˆ ์ฐธ ์ข‹์€ ๊ฑฐ ๊ฐ™์•„์š”. ํ•˜์ง€๋งŒ ์—ญ์‹œ ๊ธ€์„ ์“ฐ๋Š” ๊ฒŒ ์‰ฝ์ง„ ์•Š๋„ค์š”.

Vue Reactivity

Vue์˜ ์žฅ์ ์ด๋ผ๊ณ  ํ•˜๋ฉด ๊ผญ ๋“ฑ์žฅํ•˜๋Š”๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ Data Binding์ž…๋‹ˆ๋‹ค. Vue์˜ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๊ฐ’๊ณผ ๋ฐ์ดํ„ฐ ๊ฐ’์ด ์—ฐ๊ฒฐ์ด ๋˜์–ด ์„œ๋กœ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด ๊ฐ€๋Šฅํ•œ ์ด์œ ๊ฐ€ Vue์˜ Reactivity ์‹œ์Šคํ…œ ๋•๋ถ„์ž…๋‹ˆ๋‹ค.

Vue Guide Reactivity in Depth ์ค‘ ์ผ๋ถ€
Every component instance has a corresponding watcher instance, which records any properties “touched” during the component’s render as dependencies. Later on when a dependency’s setter is triggered, it notifies the watcher, which in turn causes the component to re-render.

Vue ๊ณต์‹ ๊ฐ€์ด๋“œ์—์„œ Reactivity ์‹œ์Šคํ…œ์„ ์„ค๋ช…ํ•ด์ฃผ๋Š” ํŽ˜์ด์ง€์˜ ์ผ๋ถ€๋ฅผ ๊ฐ€์ ธ์™€๋ดค์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ํ•œ๊ธ€ ๋ฒˆ์—ญ๋ณธ๋ณด๋‹จ ์˜์–ด ์ชฝ์ด ์ข€ ๋” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋”๊ตฐ์š”. ์งง์€ ๊ธ€์ด์ง€๋งŒ ์‚ฌ์‹ค Vue์˜ Reactivity ์‹œ์Šคํ…œ์„ ๊ด€ํ†ตํ•˜๋Š” ๋‚ด์šฉ์€ ๋ชจ๋‘ ๋“ค์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๋Š” watcher ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง„๋‹ค.

2. watcher ์ธ์Šคํ„ด์Šค๋Š” touched๋œ properties๋ฅผ ๊ธฐ๋กํ•œ๋‹ค.

3. setter๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ๋˜๋ฉด watcher์— Notify. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋žœ๋”๋ง ๋œ๋‹ค.

 

๊ทธ ๋ฐ‘์—๋Š” ๊ทธ๋ฆผ์œผ๋กœ๋„ Vue์˜ Reactivity ์‹œ์Šคํ…œ์„ ๋ณด์‹ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Vue์˜ Reactivity ๋™์ž‘ ๋ฐฉ์‹

Vue์˜ Reactivity ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์ดํ•ด๋Š” ์งง์€ ๊ธ€๊ณผ ํ•˜๋‚˜์˜ ๊ทธ๋ฆผ์œผ๋กœ๋„ ์‚ฌ์‹ค ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ €๋Š” ์‹ค์ œ๋กœ ์ด ์‹œ์Šคํ…œ์ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ์„๊นŒ๊ฐ€ ํ•œ๋ฒˆ ๋” ๊ถ๊ธˆํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ฐจ๋ก€๋Œ€๋กœ ์ฝ˜์†”์— ์ฐ์–ด๋ณด๊ฑฐ๋‚˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๋ฉฐ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค.

Vue Reactivity ์‹œ์Šคํ…œ ์ฝ”๋“œ ๋ถ„์„ ๊ฒฐ๊ณผ

์ œ๊ฐ€ ๋ถ„์„ํ–ˆ๋˜ ๊ฒฐ๊ณผ๋ฅผ ์›๋ž˜ ๊ทธ๋ฆผ์— ๋…น์—ฌ๋ดค์Šต๋‹ˆ๋‹ค. Data์˜ getter์™€ setter๊ฐ€ reactiveGetter์™€ reactiveSetter๋กœ ๋ฐ”๋€Œ๊ณ (vue/src/core/observer/index.js), Watcher ์•ˆ์—๋Š” deps๋ผ๋Š” ์†์„ฑ์— ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค(vue/src/core/observer/watcher.js). ๊ทธ๋ฆฌ๊ณ  ์›๋ž˜ ๊ทธ๋ฆผ์œผ๋กœ๋Š” ์•Œ ์ˆ˜ ์—†์—ˆ๋˜ Dep์ด๋ž€ ๊ฐ์ฒด์— ๋Œ€ํ•ด ์•Œ๊ฒŒ๋˜์—ˆ์ฃ .(vue/src/core/observer/dep.js)

 

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
export default class Dep {

    // ...

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

 

๊ทธ์ค‘์—์„œ๋„ reactiveGetter(ํ˜น์€ reacitveSetter)์—์„œ Object.defineProperty๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์‹ ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์•Œ๊ณ ๋Š” ์žˆ์—ˆ๋˜ ํ•จ์ˆ˜์˜€์ง€๋งŒ ‘์•„~ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š”๊ตฌ๋‚˜’ํ•˜๊ฒŒ ๋˜๋Š” ๋ถ€๋ถ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.(์ด ๋ถ€๋ถ„์€ ๋˜ ๋‹ค๋ฅธ ๊ธ€์—์„œ ์ ์–ด๋ณผ๊ฒŒ์š”!)

Computed

computed: {
  componentLoader() {
    return () => import(`@/components/${this.currentTab}`)
  },
}

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ „์— ๋ณด์•˜๋˜ Computed๋Š” ์–ด์งธ์„œ ์ด Watcher ๊ฐ์ฒด์— ๊ธฐ๋ก์ด ๋˜์ง€ ์•Š์•˜์„๊นŒ์š”? ์‚ฌ์‹ค ์ด ๋ถ€๋ถ„์˜ ํ•ด๋‹ต์„ ์–ป๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ„์—์„œ ์•Œ์•„๋ƒˆ๋˜ ๋ถ€๋ถ„๋“ค๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ–ˆ์Šต๋‹ˆ๋‹ค. Computed๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑ๋˜๋Š”์ง€๋ฅผ ๋ด์•ผ ๊ฐ€๋Šฅํ–ˆ์ฃ . ๊ฒฐ๋ก ๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•ด๋“œ๋ฆฌ๋ฉด ‘Touch๊ฐ€ ์ผ์–ด๋‚˜๋Š” ์‹œ์ ์˜ ์ฐจ์ด’๊ฐ€ ๊ทธ ์›์ธ์ž…๋‹ˆ๋‹ค.

 

computed: {
  CASE1() {
    return () => this.currentTab
  },
  CASE2() {
    return this.currentTab
  }
}

์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํžˆ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™€๋ดค์Šต๋‹ˆ๋‹ค. CASE 1์€ componentLoader์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ CASE2๋Š” ์ •์ƒ ๋™์ž‘ํ•˜์ฃ . ์ฐจ์ด์ ์€ CASE1์€ ํ•จ์ˆ˜๋ฅผ return ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๋ฐ”๋กœ ์ด ๋ถ€๋ถ„ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ์ ์ด ์ฐจ์ด๊ฐ€ ๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ CASE 1๊ณผ ๊ฐ™์€ coponentLoader๋ฅผ ์ฐ์–ด๋ณด๋ฉด ์ด๋Ÿฐ ๋ชจ์Šต์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. computedLoader๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ์—๋Š” ์ฆ‰, Vue์— Computed๊ฐ€ ๋“ฑ๋ก๋  ๋•Œ์—๋Š” ์•„์ง ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰์ด ๋˜์งˆ ์•Š์€ ๊ฑฐ์ฃ .

Ending

Vue์˜ Reactivity ์‹œ์Šคํ…œ์„ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค๋งŒ ์ด๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋ณต์žกํ•œ ๊ธฐ๋Šฅ๋“ค์ด ์ˆจ์–ด์žˆ๊ฒ ์ง€์š”. ๊ทธ๋ž˜๋„ ์ด๋ ‡๊ฒŒ ๋ถ„์„ํ•˜๋ฉด์„œ ์–ด๋–ป๊ฒŒ? ์™œ?๋ผ๋Š” ์˜๋ฌธ์ ๋„ ํ’€๋ ธ๊ณ , ‘์ด๋Ÿฐ ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ตฌ๋‚˜’๋ผ๋Š” ์ข‹์€ ๊ฒฝํ—˜์„ ์–ป๊ฒŒ ๋œ ์‹œ๊ฐ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

(์ด ๊ธ€์—์„œ ํ™œ์šฉํ–ˆ๋˜ ์†Œ์Šค ์ฝ”๋“œ๋Š” ์ด๊ณณ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•˜์‹ญ๋‹ˆ๋‹ค.)

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€