- Published on
vue3에서 props의 reactivity - props에 항상 toRefs를 사용해야 할까?
vue3에서 props로 넘어온 값을 다룰 때, 해당 값을 reactivity(반응성)을 유지하고자 하는 의도로 아래와 같이 습관적으로 toRefs
, toRef
, 혹은 computed
를 사용했습니다.
const { id, tags, content } = toRefs(props)
const id = toRef(props, "id")
const id = computed(() => props.id)
하지만, toRefs
나 reactive
등을 이용하지 않고도 reactive하게 동작하는 경우가 있었습니다. 따라서, vue3가 props를 처리하는 방법에 대해 알아보고 toRefs
가 반드시 필요한 경우에 대해 정리해보았습니다.
다음과 같은 형태의 props를 받는 컴포넌트가 있다고 가정해 봅시다. 그리고 props를 사용하는 몇 가지 케이스를 살펴보겠습니다.
const props = defineProps<{
id: string
tags: string[]
content: {
title: string
body: string
}
}>()
props를 사용하는 예시
1. props의 값을 다른 변수에 재할당하는 경우
다음과 같이 props의 값을 다른 변수에 할당하는 경우가 있을 수 있습니다.
const articleId = props.id
이 경우 articleId
는 reactive하게 동작하지 않습니다. 즉, props의 id
의 값이 변경되어도 articleId
값은 변경되지 않습니다.
따라서, 이 경우 아래 2번과 같이 props.id
형태로 그대로 사용하거나, toRef
를 사용하는 방법, 혹은 computed
를 사용하여 reactivity를 유지하는 방법이 있습니다.
const articleId = toRef(props, "id")
const articleId = computed(() => props.id)
물론 아래와 같이 구조 분해 할당을 이용하는 경우에도 동일하게 reactivity가 유지되지 않습니다.
const { id } = props
위 표현은 const id = props.id
와 동일하기 때문입니다.
2. props의 값을 재할당하지 않고 그대로 사용하는 경우
<div>id: {{ props.id }}</div>
이 경우 articleId
는 reactive하게 동작합니다. 즉, props의 id
의 값이 변경될 때마다 div
에 렌더링 되는 값도 달라집니다.
3. props의 값을 다른 변수에 재할당하는데, 이 값이 객체인 경우
위에서 설정한 props 중, 객체인 content
를 1번과 같이 다른 변수에 할당하는 경우가 있을 수 있습니다.
const articleContent = props.content
이 경우 articleContent
는 reactive하게 동작합니다. 즉, props의 content
의 값이 변경될 때마다 articleContent
값도 변경됩니다.
게다가, articleContent.title
이나 articleContent.body
와 같이 내부의 값도 별도 변수에 또다시 할당하지 않는 이상 reactive하게 동작합니다.
1번과 동일한 방식으로 값을 사용하고 있지만, 결과는 다릅니다.
문제의 원인: vue3가 props를 처리하는 방법
왜 위와 같은 차이가 발생하는 것일까요? 이는 vue3가 reactivity를 처리하는 방식과 관련이 있습니다.
vue3는 내부적으로 자바스크립트 Proxy를 이용해 모든 reactivity를 처리합니다. props
역시 Proxy로 처리되기에, props
내부의 값에 변경이 있을 때마다 vue의 런타임이 이를 알 수 있습니다.
하지만 여기서 한 가지 특이한 동작은, vue는 재귀적으로 객체 내부의 모든 객체를 Proxy로 처리한다는 점입니다. 즉, Proxy 객체 내부의 값 중 또다른 객체가 있다면 (위 props의 예시 중 props.content
) 이 또한 Proxy로 처리됩니다.
이 동작 방식에 따라 위 1~3번을 다시 살펴보면 다음과 같습니다.
1. props의 값을 다른 변수에 재할당하는 경우
const articleId = props.id
위 상황에서, props
는 Proxy이지만 props.id
는 원시 값인 string입니다. 따라서 articleId
에는 단순 string 값이 복사될 뿐입니다. 따라서 props
에 변화가 일어나도 articleId
는 이를 알 수 없습니다.
2. props의 값을 재할당하지 않고 그대로 사용하는 경우
따라서, props.id
와 같이 props
객체를 직접 사용하여 내부의 값에 접근하는 경우에는 reactivity를 유지할 수 있습니다. vue가 props
가 변경되었음을 알 수 있기 때문입니다.
3. props의 값을 다른 변수에 재할당하는데, 이 값이 객체인 경우
vue3에서 reactivity를 처리하는 방식 때문에, props
의 값 중 하나인 content
역시 재귀적으로 Proxy로 처리됩니다.
따라서, 아래와 같이 변수를 설정할 때에는 articleContent
역시 props.content
와 같은 Proxy 객체를 가리킵니다.
따라서 props.content
의 값이 변경되면 당연히 articleContent
도 이를 알 수 있습니다.
const articleContent = props.content
따라서 articleContent.title
이나 articleContent.body
와 같이 사용해도, reactivity를 유지할 수 있습니다.
물론 1번 사례와 같이 const title = articleContent.title
과 같이 내부의 값을 또다시 다른 변수에 할당하는 경우에는 reactivity를 유지할 수 없을 것입니다.
요약
- props나 다른 reactive한 객체를 다룰 때, 이 객체가 가지고 있는 원시 값을 다른 변수에 할당한다면 reactivity를 잃게 되므로, 다른 방식(
toRef
,computed
등)을 사용해야 합니다. - 하지만, 한 reactive한 객체 내부의 객체를 다른 변수에 할당한다면, 이 객체 또한 Proxy로 처리되어 reactivity를 유지할 수 있습니다.