이전 글에서 살펴보았듯이 Vue는 상태의 변경을 proxy로 감시를 합니다. Proxy는 객체입니다. 식별자를 가지고 있죠.(자바스크립트에는 value(값)
과 reference(참조)
가 있습니다.) 참조가 없는 원시타입(string,number 등)은 Proxy로 만들 수가 없습니다. 이를 해결하기 위해서 Vue에서는 Ref
를 제공하고 있습니다.
우선 reactive를 살펴보겠습니다. 만약 관리하고자 하는 상태가 object라면 참조를 가지고 있기 때문에 그냥 reactive로도 proxy를 만들어 변화를 추적할 수 있습니다.
원시타입은 변화가 일어나도 반응하지 않습니다. string ""
인 상태를 선언해보겠습니다. input으로 변경을 시켰지만 반영이 되지 않습니다. 왜냐면 proxy는 프로퍼티의 변화를 추적하는데 원시타입은 프로퍼티를 가질 수 없습니다.
<script setup>
import { reactive } from 'vue'
const msg = reactive("")
</script>
<template>
<h1>msg = {{ msg }}</h1>
<input v-model="msg">
</template>
object로 감싸준다면 어떻게 될까요? 이제 참조를 가지기 때문에 reactive로 proxy로 변환하시키자, value
라는 프로퍼티의 변경을 추적하기 시작합니다.
<script setup>
import { reactive } from 'vue'
const msg = reactive({value:""})
</script>
<template>
<h1>msg = {{ msg.value }}</h1>
<input v-model="msg.value">
</template>
그래서 원시타입을 상태로 추적하기위해 Vue는 ref를 제공하고 있습니다. ref가 하는 일은 간단합니다. 저희가 원시타입을 감싸준 것처럼 .value안에 값을 넣어주고 object로 변경해줍니다. Proxy를 사용하기 위해 value에 reference를 부여해주는 것이죠. 실제 ref의 내부도 결국 reactive를 사용하도록 구현이 되어있습니다.
<script setup>
import { ref } from 'vue'
const msg = ref("")
</script>
<template>
<h1>msg = {{ msg }}</h1>
<input v-model="msg">
</template>
직접 object로 감싸주는 것보다 편한 것은 Vue의 template 안에서는 msg.value
로 접근하지 않고 msg
로 사용할 수 있다는 점도 있습니다. ref임을 vue가 인식하고 있는 것이죠.
또한 이전 글에서 array에 filter, map 등을 통해 새 array를 할당하면 proxy가 덮어써져서 추적이 되지 않는 점을 해결하기 위해서도 object로 감싸줌으로서 해결을 했는데요. 이를 ref로도 쉽게 해결할 수 있습니다. object로 감싸주는 것과 같은 효과이니까요.
<script setup>
import { ref } from 'vue'
const list = ref([])
const onClick = () => {
list.value = [...list.value, 'a']
}
</script>
<template>
<h1> List: {{ list.join(', ') }} </h1>
<button @click='onClick'>
add
</button>
</template>
원시타입의 식별자 부재는 object의 프로퍼티를 복사할 때도 문제가 됩니다. 만약에 복사하려는 값이 원시타입이면, 자바스크립트는 바로 value 값만 복사하기 때문에 Proxy의 반응성을 잊어버립니다.
words
의 a
가 string이기 때문에 복사를 하면 value값만 복사해 반응성을 잃어버려서 반영되지 않는 모습입니다.
<script setup>
import { reactive, toRef } from 'vue'
const words = reactive({a:"a",b:"b"})
const msg = words.a
</script>
<template>
<h1>msg = {{ msg }}</h1>
<input v-model="msg">
</template>
이를 해결하기 위해 toRef를 사용할 수 있습니다. toRef는 하나의 property에 대해 부모 object와의 연결성을 유지한채로 reactive를 반환합니다. 이 복사본의 변화는 부모에게도 반영이 되어 추적됩니다.
<script setup>
import { reactive, toRef } from 'vue'
const words = reactive({a:"a",b:"b"})
const msg = toRef(words, 'a')
</script>
<template>
<h1>msg = {{ msg}}</h1>
<input v-model="msg">
</template>
마지막으로 toRefs는 reactive의 모든 프로퍼티 대해 toRef를 적용해 반환합니다. 그래서 destructing을 쓸 수가 있습니다.
<script setup>
import { reactive, toRefs } from 'vue'
const words = reactive({a:"a",b:"b"})
const {a,b} = toRefs(words)
</script>
<template>
<h1>msg = {{ a }}</h1>
<input v-model="a">
</template>
정리하면 ref, toRef, toRefs 모두 원시타입의 value 값도 Proxy를 추적할 수 있도록 reference를 부여한다고 생각하면 되겠습니다.
Vue는 상태를 계속 유지하고 싶어한다고 지난 글에서 설명했는데요. 그러기 위해 Proxy를 사용하고 있고, 또 이를 위해 reference 값이 필요하다는 것을 기억하면 좋을 것 같습니다.
또한 기존의 reactive를 사용하면서 불편했던 array 상태들도 편리하게 사용할 수 있구요.
다음 글에서는 이런 reactive 변경의 예외케이스로 값을 변경시키는 것이 금지되어 있는 Props에 대해 좀 더 알아보겠습니다.
엄청난 설명이네요
이 글을 vue 공식 사이트에서 링크를 걸어야 한다! 고 생각합니다.