Vue3의 Composition API에서 ref와 reactive는 반응형 데이터를 생성하는 가장 기본적인 도구입니다. 하지만 실제 프로젝트에서는 reactive 객체를 구조 분해할 때 반응형이 손실되거나, ref와 일반 값을 일관되게 처리해야 하는 등 다양한 엣지 케이스에 직면하게 됩니다. 이러한 문제를 해결하고 반응형 시스템의 안정성을 높이기 위해 toRef, toRefs, unref, isRef API의 동작 원리와 올바른 사용법을 상세히 살펴봅니다.
1. toRef: 반응형 객체의 단일 속성을 참조로 추출
핵심 개념
toRef는 reactive로 생성된 객체의 특정 속성을 기반으로 새로운 ref를 생성합니다. 생성된 ref는 원본 객체의 속성과 동기화되어 있어, 한쪽의 값을 변경하면 다른 쪽도 자동으로 업데이트됩니다.
구현 예시
import { reactive, toRef } from 'vue'
const inventory = reactive({
itemId: 'A100',
stock: 50,
warehouse: 'Seoul'
})
// inventory 객체의 stock 속성을 참조하는 ref 생성
const stockLevel = toRef(inventory, 'stock')
// ref 값 변경 시 원본 객체도 업데이트됨
stockLevel.value = 30
console.log(inventory.stock) // 30
// 원본 객체 변경 시 ref 값도 업데이트됨
inventory.stock = 10
console.log(stockLevel.value) // 10
toRef와 ref의 차이점
| 특징 | toRef | ref |
|---|---|---|
| 의존성 | 반드시 reactive 객체의 속성에 의존 |
독립적인 원시 값이나 객체를 감쌈 |
| 데이터 동기화 | 원본 속성과 양방향 바인딩 | 원시 값의 경우 원본과 단절됨 |
| 주요 용도 | 반응형 객체의 속성을 개별적으로 전달할 때 | 새로운 독립적인 반응형 상태 생성 |
주의할 점
- 일반 객체(비반응형)의 속성에
toRef를 사용하면 값은 변경되지만 뷰가 다시 렌더링되지 않습니다. - 기존 반응형 객체와의 연결을 유지하면서 특정 속성만 Props로 전달하거나 분리하고 싶을 때 사용해야 합니다.
2. toRefs: 반응형 객체의 모든 속성을 참조로 변환
핵심 개념
toRefs는 reactive 객체의 모든 속성을 toRef로 변환하여 일반 객체로 반환합니다. 이 API의 가장 큰 존재 이유는 reactive 객체를 구조 분해(Destructuring)할 때 발생하는 반응형 손실 문제를 방지하기 위함입니다.
구조 분해 시 반응형 유지
import { reactive, toRefs } from 'vue'
const appConfig = reactive({
theme: 'dark',
locale: 'ko-KR',
autoSave: true
})
// 잘못된 방식: 반응형이 끊어짐
// const { theme, locale } = appConfig
// 올바른 방식: 각 속성이 ref로 변환되어 반응형 유지
const { theme: currentTheme, locale: currentLocale, autoSave } = toRefs(appConfig)
currentTheme.value = 'light'
console.log(appConfig.theme) // 'light'
appConfig.autoSave = false
console.log(autoSave.value) // false
컴포넌트 반환 값으로 활용
setup 함수에서 reactive 객체를 반환하면 템플릿에서 appConfig.theme처럼 접근해야 합니다. 하지만 toRefs를 사용하면 템플릿에서 직접 theme으로 접근할 수 있어 코드가 간결해집니다.
3. unref: 참조 자동 언래핑을 통한 값 추출
핵심 개념
unref는 인자가 ref인지 확인한 후, ref라면 .value를 반환하고, 아니면 인자를 그대로 반환하는 유틸리티입니다. 삼항 연산자 val = isRef(val) ? val.value : val의 축약형으로 이해할 수 있습니다.
유틸리티 함수 작성 시 활용
import { ref, unref } from 'vue'
// ref와 일반 값 모두 처리 가능한 포맷팅 함수
function formatCurrency(amount) {
const rawValue = unref(amount)
return `₩${rawValue.toLocaleString()}`
}
const fixedPrice = 15000
const dynamicPrice = ref(25000)
console.log(formatCurrency(fixedPrice)) // "₩15,000"
console.log(formatCurrency(dynamicPrice)) // "₩25,000"
주의할 점
unref는 값을 읽기 위한 용도이며, 원본ref의 값을 수정하지는 않습니다.- 템플릿 내부에서는 Vue가 자동으로
ref를 언래핑하므로unref를 사용할 필요가 없습니다.
4. isRef와 isReactive: 반응형 타입 검증
핵심 개념
런타임 환경에서 변수가 ref인지 reactive인지 확인해야 할 때가 있습니다. isRef와 isReactive는 불리언 값을 반환하여 타입을 안전하게 검증할 수 있게 해줍니다.
타입 검증 및 조건부 로직
import { ref, reactive, isRef, isReactive, toRef } from 'vue'
const count = ref(0)
const state = reactive({ status: 'active' })
const plainData = { id: 1 }
// isRef 검증
console.log(isRef(count)) // true
console.log(isRef(toRef(state, 'status'))) // true (toRef의 결과물은 ref)
console.log(isRef(plainData)) // false
// isReactive 검증
console.log(isReactive(state)) // true
// 타입에 따른 데이터 처리 로직
function processState(data) {
if (isRef(data)) {
console.log('Ref 데이터 접근:', data.value)
} else if (isReactive(data)) {
console.log('Reactive 데이터 접근:', data)
} else {
console.log('일반 데이터:', data)
}
}
객체를 감싼 ref의 반응형 검증
Vue3에서 ref에 객체를 할당하면 내부적으로 reactive를 사용하여 깊게 반응형으로 만듭니다. 따라서 ref로 감싼 객체의 .value는 isReactive 검증에서 true를 반환합니다.
const objRef = ref({ name: 'Vue' })
// ref로 감싼 객체의 내부 값은 reactive 프록시임
console.log(isReactive(objRef.value)) // true
검증 시 주의사항
isRef는toRef나toRefs로 생성된 참조에 대해서도true를 반환합니다.- 반응형 데이터를 처리하는 범용 컴포저블(Composable)을 작성할 때, 인자의 타입을 정확히 알 수 없는 경우
isRef와isReactive를 사용하여 안전하게 언래핑 로직을 분기해야 합니다.