Vue 3.0/02.Essentials

[vue 3] 10.computed

  • -

이번 포스트에서는 Vue의 computed 속성에 대해서 살펴보자.

 

computed

 

computed?

computed란 "계산된"이라는 뜻이다.(너무 뻔한..)

model의 데이터를 template에서 사용하는 과정에 가끔 상황에 따라 다르게 처리해야하는 경우가 발생한다. 이런 상황은 이전에 template에서 간단한 javascript를 통해서 해결할 수 있었다. 

예를 들어 age가 18세 이상부터 투표가 가능하다고 했을 때 아래와 같이 작성할 수 있다.

<li>can vote? {{age >= 18?"yes":"no"}}</li>

하지만 좀 더 복잡한 표현에는 한계가 있기 때문에 computed를 사용한다.

 

computed의 활용

computed는 Vue가 가지는 함수형 속성이다. computed의 파라미터로직을 가지고 값을 계산한 후 리턴하는 함수(getter)이다.

// 읽기 전용
function computed<T>(
  getter: (oldValue: T | undefined) => T,
): Readonly<Ref<Readonly<T>>>

 computed의 반환값은 파라미터 함수가 반환하는 값을 가지는 Proxy 객체의 일종이다. 따라서 연결되는 data가 변경되면 자동으로 새로운 값을 계산하고 화면에 반영한다.

<script>
const { createApp, ref, computed } = Vue;
createApp({
  setup() {
    const age = ref(17);
    const canVote = computed(() => {
      return age.value >= 18 ? "yes" : "no";
    });
    console.log(canVote, canVote.value);
    return { age, canVote };
  },
}).mount("#app");
</script>

 

이때 변수의 이름은 일반적인 data 이름처럼 사용된다. 따라서 위의 age나 canVote 모두 template에서 사용하는 방법은 동일하다.

<div id="app">
  <h2>사용자 정보</h2>
  <ul>
    <li>age: {{age}}</li>
    <li>can vote? {{age >= 18?"yes":"no"}}</li>
    <li>can vote? {{canVote}}</li>
  </ul>

  <button @click="age--">-</button>
  <button @click="age++">+</button>
</div>

 

filter의 역할 수행

컴퓨터에 저장되는 데이터의 가독성이 떨어지는 경우가 있다. 예를 들어 100000000이라는 숫자를 읽으려고 하면 쉽지 않다. 이런 경우 100,000,000 처럼 천 단위마다 구분자를 삽입하면 좀 더 읽기가 수월하다. 이런 과정을 formatting이라고 한다.

Vue 2.x에서는 filter를 이용해서 data를 formating 을 처리했는데 Vue3.x에서는 filter가 없어졌다. 대신 computed를 이용해 이 filter의 역할을 수행한다.

const price = 100000000;

const pretty = computed(() => {
    const option = { style: "currency", currency: "KRW" };
    return new Intl.NumberFormat("ko-KR", option).format(price);
});

 

caching 특성

일반적인 script 실행과 대비해서 computed의 큰 차이점은 값을 caching 한다는 점이다. template에서 script(즉 함수)를 직접 실행하는 경우는 당연히 사용되는 회수만큼 함수가 동작하지만 computed는 한번 동작한 후 그 값을 caching 해서 사용한다. 따라서 종속 대상이 변경되지 않으면 다시 계산하지 않는다.

다음의 코드는 일반 함수인 sumM과 computed인 sumC가 선언되어 있다. 이 둘은 모두 dan이라는 변수에 종속되어있다.

<script>
  const { createApp, ref, computed } = Vue;

  createApp({
    setup() {
      const dan = ref(1);
      const sumM = () => {
        // 단에 대한 합 구하기
        let sum = 0;
        for (let i = 1; i < 10; i++) {
          sum += dan.value * i;
        }
        console.log(`sum: ${sum}`);
        return sum;
      };

      const sumC = computed(sumM);

      return { dan, sumM, sumC };
    },
  }).mount("#app");
</script>

template에서는 sumM과 sumC를 각각 2번씩 사용하고 있다. sumM은 단순 함수 이기 때문에 ()를 붙여서 실행해주고 sumC는 마치 data 처럼 사용되므로 ()가 없다는 점도 주목하자.

<div id="app">
  <input type="text" v-model.number="dan"/>의 합은?<br />  
  <ul>
    <li>method 두 번 호출: {{sumM()}}, {{sumM()}}</li>
    <li>computed 두 번 사용: {{sumC}}, {{sumC}}</li>
  </ul>
</div>

위 코드를 실행시켜 보면 sumM은 2번, sumC는 한번만 동작함을 알 수 있다. 각각의 종속값인 dan을 변경시켜도 결과는 동일하다.

 

수정 가능한 계산된 속성

computed는 "이미 계산된 속성"이기 때문에 대부분 read only 형태로 사용하며 새로운 값을 할당하려고 하면 runtime error가 발생한다.  computed는 readonly가 국룰이다.

const user = ref({
    first: "gil dong",
    last: "hong"
});

const computedName1 = computed(() => {
    return `${user.value.first}(${user.value.last})`;
});

console.log(computedName1.value)

// runtime 오류 - 실제로는 반영되지 못한다.
computedName1.value = "some"

 

하지만 꼭 수정해야 한다면 computed에 get과 set 함수를 가진 객체를 전달하고 set 함수에서 값을 변경할 수는 있다.

// 쓰기 가능
function computed<T>(
  options: {
    get: (oldValue: T | undefined) => T
    set: (value: T) => void
  }
): Ref<T>
const user = ref({
    first: "gil dong",
    last: "hong"
});

const computedName2 = computed({
    get() {
        return `${user.value.first}(${user.value.last})`;
    },
    set(newVal) {
        [user.first, user.last] = newVal;
    }
});
// setter 호출 - 전체적인 값의 변화가 발생한다.
computedName2.value = ["몽룡", "임"]

하지만 computed는 readonly로 사용하자.

'Vue 3.0 > 02.Essentials' 카테고리의 다른 글

[vue 3] 12.Vue 객체의 라이프 사이클  (0) 2022.07.04
[vue 3] 11.watch  (0) 2022.07.03
[vue 3] 09.DOM 요소에 직접 접근  (0) 2022.06.28
[vue 3] 08.v-on  (0) 2022.06.27
[vue 3] 07.v-model  (0) 2022.06.26
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.