반응성 API: 고급 
shallowRef() 
ref()의 얕은(shallow) 버전입니다.
- 타입 ts- function shallowRef<T>(value: T): ShallowRef<T> interface ShallowRef<T> { value: T }
- 세부사항 - ref()와 달리, 얕은 ref의 내부 값은 그대로 저장되고 노출되며, 깊은 반응성으로 변환되지 않습니다. 오직- .value접근만 반응성을 가집니다.- shallowRef()는 일반적으로 대용량 데이터 구조의 성능 최적화나 외부 상태 관리 시스템과의 통합에 사용됩니다.
- 예시 js- const state = shallowRef({ count: 1 }) // 변경을 트리거하지 않음 state.value.count = 2 // 변경을 트리거함 state.value = { count: 2 }
- 관련 문서 
triggerRef() 
shallow ref에 의존하는 효과를 강제로 트리거합니다. 이는 일반적으로 shallow ref의 내부 값을 깊게 변경한 후에 사용됩니다.
- 타입 ts- function triggerRef(ref: ShallowRef): void
- 예시 js- const shallow = shallowRef({ greet: 'Hello, world' }) // 첫 실행 시 "Hello, world"를 한 번 출력함 watchEffect(() => { console.log(shallow.value.greet) }) // ref가 shallow이기 때문에 효과를 트리거하지 않음 shallow.value.greet = 'Hello, universe' // "Hello, universe"를 출력함 triggerRef(shallow)
customRef() 
의존성 추적과 업데이트 트리거를 명시적으로 제어할 수 있는 커스텀 ref를 생성합니다.
- 타입 ts- function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (value: T) => void }
- 세부사항 - customRef()는 팩토리 함수를 기대하며, 이 함수는- track과- trigger함수를 인자로 받아- get과- set메서드를 가진 객체를 반환해야 합니다.- 일반적으로 - track()은- get()내부에서,- trigger()는- set()내부에서 호출되어야 합니다. 하지만 언제 호출할지, 혹은 호출하지 않을지에 대한 완전한 제어권이 있습니다.
- 예시 - 마지막 set 호출 이후 일정 시간 후에만 값을 업데이트하는 디바운스 ref를 생성합니다: js- import { customRef } from 'vue' export function useDebouncedRef(value, delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) }- 컴포넌트에서의 사용 예시: vue- <script setup> import { useDebouncedRef } from './debouncedRef' const text = useDebouncedRef('hello') </script> <template> <input v-model="text" /> </template>- 주의해서 사용하세요 - customRef를 사용할 때, getter의 반환값에 주의해야 합니다. 특히 getter가 실행될 때마다 새로운 객체 데이터 타입을 생성하는 경우, 이 customRef가 prop으로 전달된 부모-자식 컴포넌트 관계에 영향을 미칩니다. - 부모 컴포넌트의 렌더 함수는 다른 반응성 상태의 변경에 의해 트리거될 수 있습니다. 리렌더링 중에 customRef의 값이 다시 평가되어 자식 컴포넌트에 prop으로 새로운 객체 데이터 타입이 전달됩니다. 이 prop은 자식 컴포넌트에서 이전 값과 비교되며, 값이 다르기 때문에 customRef의 반응성 의존성이 자식 컴포넌트에서 트리거됩니다. 한편, customRef의 setter가 호출되지 않았으므로 부모 컴포넌트의 반응성 의존성은 실행되지 않습니다. 
shallowReactive() 
reactive()의 얕은(shallow) 버전입니다.
- 타입 ts- function shallowReactive<T extends object>(target: T): T
- 세부사항 - reactive()와 달리, 깊은 변환이 없습니다: 얕은 반응성 객체에서는 루트 레벨 속성만 반응성을 가집니다. 속성 값은 그대로 저장되고 노출됩니다. 즉, ref 값을 가진 속성은 자동으로 언래핑되지 않습니다.- 주의해서 사용하세요 - 얕은 데이터 구조는 컴포넌트의 루트 레벨 상태에만 사용해야 합니다. 깊은 반응성 객체 내부에 중첩해서 사용하는 것은 일관성 없는 반응성 트리 구조를 만들어 이해 및 디버깅이 어려워질 수 있습니다. 
- 예시 js- const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // state의 자체 속성 변경은 반응성을 가짐 state.foo++ // ...하지만 중첩 객체는 변환하지 않음 isReactive(state.nested) // false // 반응성 없음 state.nested.bar++
shallowReadonly() 
readonly()의 얕은(shallow) 버전입니다.
- 타입 ts- function shallowReadonly<T extends object>(target: T): Readonly<T>
- 세부사항 - readonly()와 달리, 깊은 변환이 없습니다: 루트 레벨 속성만 읽기 전용으로 만들어집니다. 속성 값은 그대로 저장되고 노출됩니다. 즉, ref 값을 가진 속성은 자동으로 언래핑되지 않습니다.- 주의해서 사용하세요 - 얕은 데이터 구조는 컴포넌트의 루트 레벨 상태에만 사용해야 합니다. 깊은 반응성 객체 내부에 중첩해서 사용하는 것은 일관성 없는 반응성 트리 구조를 만들어 이해 및 디버깅이 어려워질 수 있습니다. 
- 예시 js- const state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // state의 자체 속성 변경은 실패함 state.foo++ // ...하지만 중첩 객체에는 적용되지 않음 isReadonly(state.nested) // false // 동작함 state.nested.bar++
toRaw() 
Vue에서 생성된 프록시의 원본, 즉 가공되지 않은 객체를 반환합니다.
- 타입 ts- function toRaw<T>(proxy: T): T
- 세부사항 - toRaw()는- reactive(),- readonly(),- shallowReactive(),- shallowReadonly()로 생성된 프록시에서 원본 객체를 반환할 수 있습니다.- 이는 프록시 접근/추적 오버헤드 없이 임시로 읽거나, 변경을 트리거하지 않고 쓸 수 있는 탈출구입니다. 원본 객체에 대한 지속적인 참조를 유지하는 것은 권장되지 않습니다. 주의해서 사용하세요. 
- 예시 js- const foo = {} const reactiveFoo = reactive(foo) console.log(toRaw(reactiveFoo) === foo) // true
markRaw() 
객체가 프록시로 변환되지 않도록 표시합니다. 객체 자체를 반환합니다.
- 타입 ts- function markRaw<T extends object>(value: T): T
- 예시 js- const foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // 다른 반응성 객체 내부에 중첩되어 있어도 동작함 const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false- 주의해서 사용하세요 - markRaw()와- shallowReactive()와 같은 얕은 API는 기본 깊은 반응성/읽기 전용 변환에서 선택적으로 제외하고, 상태 그래프에 가공되지 않은(non-proxied) 객체를 삽입할 수 있게 해줍니다. 다양한 이유로 사용할 수 있습니다:- 일부 값은 반응성으로 만들면 안 됩니다. 예를 들어 복잡한 3rd party 클래스 인스턴스나 Vue 컴포넌트 객체 등입니다. 
- 프록시 변환을 건너뛰면 불변 데이터 소스를 가진 대용량 리스트 렌더링 시 성능 향상을 얻을 수 있습니다. 
 - 이들은 고급 기능으로 간주되는데, raw 제외는 루트 레벨에만 적용되기 때문입니다. 즉, 중첩된, markRaw되지 않은 raw 객체를 반응성 객체에 설정한 후 다시 접근하면 프록시 버전을 얻게 됩니다. 이는 아이덴티티 위험을 초래할 수 있습니다. 즉, 객체 아이덴티티에 의존하는 작업을 수행하면서 동일한 객체의 raw와 프록시 버전을 모두 사용하는 경우입니다: js- const foo = markRaw({ nested: {} }) const bar = reactive({ // `foo`는 raw로 표시되었지만, foo.nested는 그렇지 않음. nested: foo.nested }) console.log(foo.nested === bar.nested) // false- 아이덴티티 위험은 일반적으로 드뭅니다. 하지만 이러한 API를 제대로 활용하면서 아이덴티티 위험을 안전하게 피하려면 반응성 시스템의 동작 원리에 대한 확실한 이해가 필요합니다. 
effectScope() 
효과 스코프 객체를 생성하여, 그 안에서 생성된 반응성 효과(즉, computed와 watcher)를 함께 캡처하고 일괄적으로 해제(dispose)할 수 있습니다. 이 API의 자세한 사용 사례는 해당 RFC를 참고하세요.
- 타입 ts- function effectScope(detached?: boolean): EffectScope interface EffectScope { run<T>(fn: () => T): T | undefined // 스코프가 비활성화된 경우 undefined stop(): void }
- 예시 js- const scope = effectScope() scope.run(() => { const doubled = computed(() => counter.value * 2) watch(doubled, () => console.log(doubled.value)) watchEffect(() => console.log('Count: ', doubled.value)) }) // 스코프 내의 모든 효과를 해제하려면 scope.stop()
getCurrentScope() 
현재 활성화된 effect scope가 있다면 반환합니다.
- 타입 ts- function getCurrentScope(): EffectScope | undefined
onScopeDispose() 
현재 활성화된 effect scope에 dispose 콜백을 등록합니다. 해당 effect scope가 중지될 때 콜백이 호출됩니다.
이 메서드는 재사용 가능한 컴포지션 함수에서 컴포넌트에 종속되지 않는 onUnmounted의 대체로 사용할 수 있습니다. 각 Vue 컴포넌트의 setup() 함수도 effect scope 내에서 호출되기 때문입니다.
활성화된 effect scope 없이 이 함수를 호출하면 경고가 발생합니다. 3.5+에서는 두 번째 인자로 true를 전달하여 이 경고를 억제할 수 있습니다.
- 타입 ts- function onScopeDispose(fn: () => void, failSilently?: boolean): void