# [Vue 03] computed(를 보다가 vue 반응형으로 새버린 글)

- [ ] computed란 무엇인가

- [ ] 어떤 데이터에 반응하는가 [의존성]

- [ ] 어떤 데이터를 반응시키는가 [다시 렌더링 되는 범위]

- [ ] 그냥 ref와의 차이점

- [ ] computed 사용시 주의점

- [ ] setter를 이용한

## computed란

computed 속성은 템플릿의 데이터 표현을 더 직관적이고 간결하게 도와주는 속성이다.

### 사용법

인자는 계산결과를 리턴해주고 매개변수가 없는 콜백함수 한개이다. 리턴된 값은 vue 반응형으로 감싸졌기 때문에 

x.value()처럼 value() 메소드를 이용해서 값에 접근할 수 있다.

### 특징

1. **반응형 계산**: `**computed**` 속성은 의존하는 반응형 데이터가 변경될 때 자동으로 다시 계산된다.

2. **캐싱**: `**computed**` 속성은 그 결과값을 캐싱한다. 의존하는 데이터가 변경되지 않으면, `**computed**` 속성은 이전에 계산된 값을 재사용하여 성능을 향상시킨다.

3. **선언적 접근**: `**computed**` 속성을 사용하면 복잡한 로직을 간결하고 선언적인 방식으로 표현할 수 있습니다. 이는 코드의 가독성과 유지보수성을 높인다.

4. **반환값**: `**computed**` 속성은 항상 값을 반환해야 합니다. 이 반환값은 템플릿이나 다른 반응형 속성에서 사용될 수 있다.

5. **함수 형태**: `**computed**` 속성은 함수로 정의된다. 이 함수는 반응형 데이터를 기반으로 계산을 수행하고 결과를 반환한다.

## 의존하는 반응형 데이터의 범위

여기서 **_"의존하는 반응형 데이터"_** 가 어디까지를 포함하는지 알아보자.

1. **Reactive References (**`**ref**`**)**: `**ref**` 함수로 생성된 반응형 참조 변수들입니다. 이들은 `**.value**` 속성을 통해 접근되며, 이 속성의 값이 변경될 때 자동으로 업데이트를 트리거합니다.

2. **Reactive Objects (**`**reactive**`**)**: `**reactive**` 함수를 통해 생성된 반응형 객체들입니다. 이 객체들의 속성이 변경되면 자동으로 업데이트가 일어납니다.

3. **Computed Properties**: 다른 반응형 데이터에 의존하는 계산된 속성들입니다. 의존하는 데이터가 변경될 때마다 자동으로 재계산됩니다.

4. **Props**: 부모 컴포넌트로부터 전달받은 속성들입니다. 이 속성들이 변경되면 자식 컴포넌트는 자동으로 반응하여 업데이트됩니다.

## 왜필요하지?

```javascript
const a= ref(3);

let b = a.value; // 반응성이 깨짐
const c = a;
const d = computed(() => a.value);
```

vue에 대해 잘 몰랐을 때는 b 에도 반응형이 유지될 줄 알았다. vue의 반응형을 너무 과대평가한 나의 섣부른 판단이었고, vue의 반응형을 더 알아보게 되었다. vue는 반응형 값들을 template 에서 평가되는 반응형 값들, computed와 watch같은 vue 반응형 콜백함수들에서만  재렌더링 하고, 로컬 변수에서의 읽기 쓰기는 추적하지 못한다.

그렇기에 원본 ref와는 다르지만, 원본의 변화에 반응해야할 값이 필요하다면,  computed가 가장 간편할 것 같다. 물론 새로운 ref를 만들고 watch로 원본 ref의 변화가 생긴다면 새로운 ref로 수정하는 것도 가능하긴하다.

## vue의 반응형에 대해 더 알아보자.

아까부터 vue가 '이건 반응형을 추적하고, 이건 안하고, 이건 재렌더링 되고, 어떤건 안된다' 이런게 너무 모호하게 느껴졌다. 그래서 vue가 어떤식으로 내부적으로 반응형을 동작시키는지 조금 더 알아봤다.

![https://prod-files-secure.s3.us-west-2.amazonaws.com/3070383b-218e-480b-8a26-22e9050cd6ca/047b288e-832a-41e1-be12-8c5bcb0205fe/IMG_0298.jpeg](https://prod-files-secure.s3.us-west-2.amazonaws.com/3070383b-218e-480b-8a26-22e9050cd6ca/047b288e-832a-41e1-be12-8c5bcb0205fe/IMG_0298.jpeg)

옵져버 패턴으로 반응형 변수를 사용하는 곳에서는 해당 컨텍스트(사이드이펙트)를 전역set에 추가하고(구독), 반응형변수가 변경 될때(value에 대한 setter가 호출 될때) 의존성 set에 존재하는 각각의 이펙트들을 실행시켜준다. 

여기서  현재 실행중인 sideEffect 함수가 무엇인지 아는 방법은 다음과 같다. 정확히 이해한지는 모르겠지만, a라는 함수를 그냥 실행하는게 아니라, 프록시 같은 느낌으로 래핑해서 실행한다. 이렇게 하면 전역적으로 현재 실행되고 있는 이펙트가 무엇인지 알 수 있다.

```javascript
function whenDepsChange(update) {
  const effect = () => {
    activeEffect = effect
    update()
    activeEffect = null
  }
  effect()
}
```

위 내용은 vue3문서를 참고했다.

[반응형 심화 | Vue.js](https://ko.vuejs.org/guide/extras/reactivity-in-depth)

## 객체나 배열

computed는 객체의 깊은 변화도 감지 할 수 있을까? 여러 상황에 대해 test 해봤다.

1. 간단한 객체의 깊은 속성

```javascript
import {ref, reactive,computed } from 'vue'
const obj = reactive({name:'천에준', attr:{age:21, height: 181}});

const myComputed = computed(() =>  obj); 
obj.attr.age++;
console.log(myComputed.value);
```

1. 객체에 없던 속성의 추가 감지

```javascript
import { reactive,computed } from 'vue'
const obj = reactive({});

const myComputed = computed(() => {
  return obj.newProp; // 동적으로 추가된 속성에 대한 변화를 추적 가능
});

obj.newProp = 'new property';
console.log(myComputed.value); // 'new property'
```

1. 배열의 요소 추가시 다른 속성들

```javascript
import {ref, reactive,computed } from 'vue'
const obj = reactive([]);

const myComputed = computed(() => {
  return obj.length; // 동적으로 추가된 속성에 대한 변화를 추적 가능
//return obj.x();
});

obj.push(3,4,5);
console.log(myComputed.value);
```

1. 배열의 특정 인덱스 수정

```javascript
import { ref,reactive,computed } from 'vue'
const obj = ref([1,2,3]);

const myComputed = computed(() => {
  return obj.value; // 동적으로 추가된 속성에 대한 변화를 추적 가능
});
//obj.value[0] = 10;
obj.value = [10,...obj.value.slice(1)]
console.log(myComputed.value); // 'new property'
```

gpt가 이건 위험할 수 있다고 스프레드 연산으로 새로운 배열로 하라고 했는데 조금 당황스럽다.

왠만한건 다 감지하는거 같은데… 아닌게 있다면 언제든 이야기해주세요.

## computed 이것만은 주의하자.

### 캐싱이 핵심이다.

```javascript
const org = ref(3);
// computed
const a = computed(() => org.value * 2);
// 메소드
const doubleValue = (x) => x*2 ;
<p> {{doubleValue(org)}}</p>
```

물론 템플릿에서만 사용하는 값이라면, 그냥 함수 만들어놓고 템플릿에서 수행하면 된다. 함수에 들어가는 매개변수가 바뀔때마다 template부니까 다시 계산되니까 사실상 computed와 비슷한 효과를 낼 수 있다. 하지만 캐싱에 차이가 있다. computed는 의존하는 반응형 데이터의 변화가 없다면 다시 계산하지 않는다. 실행시간이 오래걸리는 계산의 경우 유의미한 차이가 존재할 수 있다.

### getter는 순수함수여야한다.

computed가 계산하는 콜백함수는 사이드 이펙트를 일으키면 안된다. 설계 자체가 새로운 값을 반환하게 되어있기에 그 의도를 존중해주자.(그런건 watch로 하라고!!)

### 왠만하면 readOnly로 사용하자.

물론 computed 값도 setter를 따로 지정하여, 참조하는 값을 직접 변경하고, 다시 계산되어 읽기/쓰기 둘다 가능하게 보이게 할 수 있다. 하지만, setter에 의해 쓰여진 값도 의존성이 변경되면 무시되고 다시 덮여씌워질 것이다. 가능하면 읽기전용값으로 사용하자.

## computed setter의 활용법

```javascript
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 참고: 분해 할당 문법을 사용함.
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

```

js의 접근자 프로퍼티와 비슷하지만 특정 프로퍼티에 종속된 접근자가 아니라 vue의 computed 자체 문법이다. getter는 computed의 사용법과 동일하게 내부의 종속성의 다시 계산하고, 캐시된 값을 리턴한다. setter는 캐시된 값 자체를 바꾸는 방법이 아니라, 연결된 의존성의 값을 변경하는 방식이다. setter가 존재하는 computed를 이용해서 v-bind와 유사하게 읽기, 쓰기가 가능한 props를 만들 수 있다. getter에 props값 가져오기, setter에 부모에 값을 update하라는 emit을 보내면 부모 자식이 동일한 상태를 유지 할 수있다.

For the site tree, see the [root Markdown](https://slashpage.com/yejun-cheon.md).
