λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ“  archive

[Vue.js] computed 속성 μ‚¬μš© μ‹œ μ£Όμ˜ν•  점듀

by HandHand 2022. 2. 23.

Vue computed 속성 μ‚¬μš© μ‹œ μ£Όμ˜ν•  점듀

πŸ’‘ λ³Έ ν¬μŠ€νŠΈλŠ” Vue.js μ½”μ–΄ νŒ€ 일원인 Thorsten Lünborg 이 κΈ°κ³ ν•œ 글에 κ·Όκ±°ν•©λ‹ˆλ‹€.

πŸ“Œ computed μ†μ„±μ΄λž€?

export default {
  name: "HelloWorld",
  data() {
    return {
      todos: [
        { title: "wash dishes", done: true },
        { title: "remove trash", done: false },
      ],
    };
  },
  computed: {
    needTodos() {
      return this.todos.filter((todo) => !todo.done);
    },
  },
};

Vue μ—μ„œ computed 속성은 data 의 λ³€ν™”λ₯Ό κ°μ§€ν•˜μ—¬

λ™μ μœΌλ‘œ κ³„μ‚°λœ 값을 μ΄μš©ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

λ˜ν•œ watch 속성을 μ‚¬μš©ν•˜λ©΄ computed κ°€ λ³€ν™”ν•˜λŠ” 것을 κ°μ§€ν•˜μ—¬

νŠΉμ • ν•¨μˆ˜λ₯Ό μˆ˜ν–‰ν•˜λ„λ‘ ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

 

πŸ“Œ computed μ†μ„±μ˜ μ„±λŠ₯상 이점은?

캐싱 (Caching)

computed μ†μ„±μ˜ κ°€μž₯ 큰 μž₯점은 캐싱인데, μ˜μ‘΄ν•˜λŠ” 값에 λ³€ν™”κ°€ μ—†λ‹€λ©΄

λ‹€μ‹œ κ³„μ‚°ν•˜μ§€ μ•Šκ³  이전 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

μœ„ μ˜ˆμ œμ—μ„œλŠ” todos κ°€ λ³€ν™”ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ 아무리 needTodos λ₯Ό ν˜ΈμΆœν•˜λ”λΌλ„

이전에 κ³„μ‚°λ˜μ–΄ μΊμ‹±λœ 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

μ§€μ—°λœ 평가 (Lazy Evaluation)

computed 속성은 lazy ν•˜κ²Œ 값을 κ³„μ‚°ν•΄μ„œ λ°˜ν™˜ν•©λ‹ˆλ‹€.

즉, computed 속성이 μ°Έμ‘°κ°€ λ˜μ—ˆμ„ λ•Œ (초기 ν˜Ήμ€ μ˜μ‘΄ν•˜κ³  μžˆλŠ” 값이 λ³€ν™”ν•œ 경우) 콜백이 μˆ˜ν–‰λ©λ‹ˆλ‹€.

λ”°λΌμ„œ λ§Žμ€ λ°μ΄ν„°λ‘œ λΉ„μš©μ΄ 많이 λ“œλŠ” 연산을 μˆ˜ν–‰ν•  λ•Œ μ„±λŠ₯상 이점이 μžˆμŠ΅λ‹ˆλ‹€.

 

πŸ“Œ μ§€μ—°λœ ν‰κ°€μ˜ μ„±λŠ₯ν–₯상 μ˜ˆμ‹œ

<template>
  <div>
    <button type="button" @click="showList = !showList">show todos</button>
    <template v-if="showList">
      <template v-if="hasOpenedTodos">
        <li v-for="(todo, idx) in needTodos" :key="idx">
          {{ todo.title }}
        </li>
      </template>
      <span v-else> nothing to show </span>
    </template>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      showList: false,
      todos: [
        { title: "wash dishes", done: true },
        { title: "remove trash", done: false },
      ],
    };
  },
  computed: {
    needTodos() {
      return this.todos.filter((todo) => !todo.done);
    },
    hasOpenedTodos() {
      return this.needTodos.length > 0;
    },
  },
};
</script>

μœ„ μ˜ˆμ œμ—μ„œ showList κ°€ true κ°€ 되기 μ „κΉŒμ§€ needTodos λŠ” μ°Έμ‘°λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—

μ˜μ‘΄ν•˜κ³  μžˆλŠ” todos 에 λ³€ν™”κ°€ 생기더라도 값을 κ³„μ‚°ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

κ°„λ‹¨ν•œ μ˜ˆμ œμ΄μ§€λ§Œ λ§Œμ•½ needTodos μ—μ„œ λΉ„μš©μ΄ 큰 계산을 ν•  κ²½μš°μ—λŠ”

μ§€μ—°λœ ν‰κ°€λ‘œ μΈν•œ μ„±λŠ₯ ν–₯상을 κΈ°λŒ€ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

 

πŸ“Œ μ§€μ—°λœ 평가가 μ„±λŠ₯μ €ν•˜λ₯Ό λΆˆλŸ¬μ˜€λŠ” 경우

λ°˜λŒ€λ‘œ μ§€μ—°λœ ν‰κ°€λ‘œ 인해 μ„±λŠ₯μ €ν•˜λ₯Ό μΌμœΌν‚€λŠ” κ²½μš°λ„ μžˆμŠ΅λ‹ˆλ‹€.

computed 속성이 ν•΄λ‹Ή 값이 μ°Έμ‘°λ˜μ—ˆμ„ λ•Œ 계산을 μ‹œμž‘ν•œλ‹€λŠ” 것은 사싀

ν•΄λ‹Ή 값이 μ½νžˆμ§€ μ•ŠμœΌλ©΄ κ·Έμ „κΉŒμ§€ λ°˜μ‘ν˜• μ‹œμŠ€ν…œμ΄ 이λ₯Ό μΈμ§€ν•˜μ§€ λͺ»ν•œλ‹€λŠ” 것과 κ°™μŠ΅λ‹ˆλ‹€.

κ·Έ λ•Œλ¬Έμ— μ˜μ‘΄ν•˜κ³  μžˆλŠ” 값이 λ³€ν™”ν–ˆμ„ λ•Œ μ‹€μ œλ‘œ computed 속성이 λ°˜ν™˜ν•˜λŠ” 값이

λ‹€λ₯Έμ§€λŠ” μ½œλ°±μ„ μˆ˜ν–‰ν•˜κΈ° μ „κΉŒμ§€ λͺ¨λ₯΄λŠ” κ²ƒμž…λ‹ˆλ‹€.

이게 μ™œ λ¬Έμ œκ°€ λ κΉŒμš”?

ν•΄λ‹Ή computed 속성에 μ˜μ‘΄ν•˜κ³  μžˆλŠ” watch() ν˜Ήμ€ λ‹€λ₯Έ computed 속성이 μžˆλŠ” 경우 λ¬Έμ œκ°€ λ©λ‹ˆλ‹€.

기쀀이 λ˜λŠ” computed 속성이 μ‹€μ œλ‘œ λ‹€λ₯Έ 값을 λ°˜ν™˜ν•˜μ§€ μ•Šλ”λΌλ„

Vue λŠ” ν˜Ήμ‹œ λͺ¨λ₯Ό 상황에 λŒ€λΉ„ν•΄ μ—°κ΄€λœ λ‹€λ₯Έ λͺ¨λ“  속성듀에 λŒ€ν•΄

μ—…λ°μ΄νŠΈκ°€ ν•„μš”ν•˜λ‹€λŠ” ν‘œμ‹œ(dirty)λ₯Ό λ‚¨κ²¨λ†“μŠ΅λ‹ˆλ‹€.

이 λ•Œλ¬Έμ— λΆˆν•„μš”ν•œ μž¬μ—°μ‚°μ΄ λ°œμƒν•  μˆ˜λ„ μžˆλŠ” κ²ƒμž…λ‹ˆλ‹€.

μ˜ˆμ‹œμ½”λ“œ

<template>
  <div>
    <button type="button" @click="increase">click me!</button>
    <ul>
      <li v-for="(item, idx) in someExpensiveLogic" :key="idx">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      count: 0,
    };
  },
  computed: {
    isOver10() {
      return this.count > 10;
    },
    someExpensiveLogic() {
      /** κ³„μ‚°ν•˜λŠ”λ° λ§Žμ€ λΉ„μš©μ΄ ν•„μš”ν•œ computed 라고 κ°€μ •ν•©λ‹ˆλ‹€. */
      return this.isOver10 ? [1, 2, 3] : [4, 5, 6];
    },
  },
  methods: {
    increase() {
      this.count += 1;
    },
  },
  updated() {
    console.log("component updated");
  },
};
</script>

μœ„ μ˜ˆμ œμ—μ„œ λ²„νŠΌμ„ 11번 λˆ„λ₯Όλ•ŒκΉŒμ§€ HelloWorld μ»΄ν¬λ„ŒνŠΈλŠ” λͺ‡λ²ˆμ΄λ‚˜ re-render ν• κΉŒμš”?

 

정닡은 11번 λͺ¨λ‘ μž…λ‹ˆλ‹€.

 

λ²„νŠΌμ„ 클릭할 λ•Œλ§ˆλ‹€ λ°œμƒν•˜λŠ” 과정은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

 

  1. λ²„νŠΌμ„ ν΄λ¦­ν–ˆμ„ λ•Œ count 값이 μ¦κ°€ν•©λ‹ˆλ‹€.
  2. isOver10 은 count 에 μ˜μ‘΄ν•˜κ³  있기 λ•Œλ¬Έμ— dirty μ²˜λ¦¬λ©λ‹ˆλ‹€.
  3. someExpensiveLogic 도 isOver10 에 μ˜μ‘΄ν•˜κ³  있기 λ•Œλ¬Έμ— dirty μ²˜λ¦¬λ©λ‹ˆλ‹€.
  4. μ»΄ν¬λ„ŒνŠΈ ν…œν”Œλ¦Ώμ΄ someExpensiveLogic λ₯Ό μ°Έμ‘°ν•˜κ³  있기 λ•Œλ¬Έμ—
    dirty 처리된 μ†μ„±λ“€μ˜ μž¬μ—°μ‚°μ΄ μˆ˜ν–‰λ©λ‹ˆλ‹€.
    ν•˜μ§€λ§Œ λ°˜ν™˜λ˜λŠ” 결과값은 λͺ¨λ‘ λ™μΌν•©λ‹ˆλ‹€.
  5. μ΄λ‘œμΈν•΄ μƒˆλ‘œμš΄ 가상 DOM κ³Ό template 을 μ΄μš©ν•΄μ„œ
    이전과 λ™μΌν•œ 화면을 κ·Έλ¦¬λŠ” λΆˆν•„μš”ν•œ λ Œλ”λ§μ΄ λ°œμƒν•©λ‹ˆλ‹€.

이 문제의 원인을 μš”μ•½ν•˜λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

 

λ™μΌν•œ 값을 자주 λ°˜ν™˜ν•˜λŠ” computed μ†μ„±μ„ μ°Έμ‘°ν•˜κ³  μžˆλŠ”
μ—°μ‚° λΉ„μš©μ΄ λΉ„μ‹Ό λ‹€λ₯Έ computed 속성 ν˜Ήμ€ watcher  κ°€ 있고 template 을 톡해 참쑰되고 μžˆλ‹€.

 

πŸ“Œ μ΄λŸ¬ν•œ 경우 μ ˆλŒ€ computed 속성을 μ‚¬μš©ν•˜λ©΄ μ•ˆλ˜λ‚˜μš”?

κ·Έλ ‡λ‹€κ³  μ΄λŸ¬ν•œ 경우λ₯Ό λͺ¨λ‘ κ³ λ €ν•΄μ„œ computed 속성 μ‚¬μš©μ„ ν”Όν•  ν•„μš”λŠ” μ—†μŠ΅λ‹ˆλ‹€.

Vue 의 λ°˜μ‘ν˜• μ‹œμŠ€ν…œμ€ 효율적으둜 λ™μž‘ν•˜λ„λ‘ μ΅œμ ν™”λ˜μ–΄μžˆκΈ° λ•Œλ¬Έμ—

λŒ€λΆ€λΆ„μ˜ 경우 μ΅œμƒμ˜ μ„±λŠ₯을 보μž₯ν•©λ‹ˆλ‹€. (특히 Vue 3 μ—μ„œλŠ”!)

 

μ€‘μš”ν•œ 것은 computed 속성을 μ‚¬μš©ν•  λ•Œ μ΄λŸ¬ν•œ νŠΉμ„±μ΄ μžˆλ‹€λŠ” 것을 μ•Œμ•„λ‘κ³ 

정말 규λͺ¨κ°€ 크고 μ—°μ‚° λΉ„μš©μ΄ 큰 κ²½μš°μ— λŒ€ν•΄ μ£Όμ˜κ°€ ν•„μš”ν•˜λ‹€λŠ” 것을 μΈμ§€ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

κ·Έλž˜μ•Ό ν•„μš”μ— 따라 μ„±λŠ₯ μ΅œμ ν™” 포인트λ₯Ό μž‘μ„ 수 μžˆμ„ν…Œλ‹ˆκΉŒμš”.

 

πŸ“Œ μ°Έκ³  자료

Vue: When a computed property can be the wrong tool

λ°˜μ‘ν˜•

πŸ’¬ λŒ“κΈ€