본문으로 건너뛰기

Vue는 반응성을 처리하고 추적하기 위해 signal 패러다임을 사용합니다. 주요 기능 중 하나는 이 시스템에서는 반응형 시스템이 특별히 감시하는 반응형 속성에서만 업데이트를 트리거합니다. 따라서 쿼리가 사용하는 값이 업데이트될 때 쿼리도 업데이트되도록 해야 합니다.

쿼리의 반응성 유지

쿼리용 컴포저블을 만들 때는 먼저 다음과 같이 작성하는 방식을 고려할 수 있습니다:

export function useUserProjects(userId: string) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId),
);
}

이 composable은 다음과 같이 사용할 수 있습니다:

// Reactive user ID ref.
const userId = ref('1')
// Fetches the user 1's projects.
const { data: projects } = useUserProjects(userId.value)

const onChangeUser = (newUserId: string) => {
// Edits the userId, but the query will not re-fetch.
userId.value = newUserId
}

이 코드는 의도대로 작동하지 않습니다. 이는 userId ref에서 값을 직접 추출하고 있기 때문입니다. Vue-query가 userId ref를 추적하지 않으므로 값이 변경되는 시점을 알 방법이 없습니다.

다행히 이에 대한 수정은 간단합니다. 해당 값을 쿼리 키에서 추적할 수 있게 만들어야 합니다. composable에서 ref를 직접 받아 쿼리 키에 배치하면 됩니다:

export function useUserProjects(userId: Ref<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(userId.value),
);
}

이제 userId가 변경되면 쿼리를 다시 가져옵니다.

const onChangeUser = (newUserId: string) => {
// Query refetches data with new user ID!
userId.value = newUserId
}

vue query에서는 쿼리 키 내의 모든 반응형 속성 변경을 자동으로 추적합니다. 따라서 vue-query는 다음 항목이 변경될 때마다 데이터를 다시 가져올 수 있습니다 주어진 요청의 매개변수가 변경됩니다.

비반응형 쿼리 고려하기

가능성은 훨씬 낮지만 때로는 비반응형 변수를 전달하는 것이 의도적인 경우도 있습니다. 예를 들어 일부 엔터티는 한 번만 가져오면 되어 추적할 필요가 없거나, 뮤테이션 후 뮤테이션의 쿼리 옵션 객체를 무효화합니다. 위에서 정의한 사용자 지정 composable을 사용하면 이 경우의 사용 방식이 다소 어색하게 느껴집니다:

const { data: projects } = useUserProjects(ref('1'))

매개변수의 타입 호환성을 맞추기 위해 중간 ref를 생성해야만 합니다. 여기서는 더 나은 방법을 사용할 수 있습니다. 대신 일반 값과 반응형 값을 모두 허용하도록 composable을 업데이트하겠습니다:

export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
);
}

이제 일반 값과 ref 모두에 composable을 사용할 수 있습니다:

// Fetches the user 1's projects, userId is not expected to change.
const { data: projects } = useUserProjects('1')

// Fetches the user 1's projects, queries will react to changes on userId.
const userId = ref('1')

// Make some changes to userId...

// Query re-fetches based on any changes to userId.
const { data: projects } = useUserProjects(userId)

쿼리 내부에서 파생 상태 사용하기

어떤 반응형 상태의 소스에서 새로운 반응형 상태를 파생하는 일은 매우 흔합니다. 일반적으로 이 문제는 컴포넌트 props를 다루는 상황에서 나타납니다. userId가 컴포넌트에 전달된 prop이라고 가정해 보겠습니다:

<script setup lang="ts">
const props = defineProps<{
userId: string
}>()
</script>

다음과 같이 쿼리에서 prop을 직접 사용하고 싶을 수 있습니다:

// Won't react to changes in props.userId.
const { data: projects } = useUserProjects(props.userId)

하지만 첫 번째 예제와 마찬가지로 이는 반응형이 아닙니다. reactive 변수의 property에 접근하면 반응성을 잃게 됩니다. computed를 통해 이 파생 상태를 반응형으로 만들어 이를 해결할 수 있습니다:

const userId = computed(() => props.userId)

// Reacts to changes in props.userId.
const { data: projects } = useUserProjects(userId)

이는 예상대로 작동하지만, 이 해결책이 항상 가장 최적의 방법은 아닙니다. 중간 변수를 도입할 뿐만 아니라 다소 불필요한 메모이제이션 값도 생성합니다. 단순한 속성 접근처럼 사소한 경우에는 computed가 실질적인 이점이 없는 최적화입니다. 이러한 경우에는 반응형 getter를 사용하는 것이 더 적절한 해결책입니다. 반응형 getter는 computed의 작동 방식과 유사하게 일부 반응형 상태를 기반으로 값을 반환하는 함수일 뿐입니다. computed와 달리 반응형 getter는 값을 메모이제이션하지 않으므로 단순한 속성 접근에 적합합니다.

컴포저블을 다시 한번 리팩터링하되, 이번에는 ref, 일반 값 또는 반응형 getter를 받도록 하겠습니다:

export function useUserProjects(userId: MaybeRefOrGetter<string>) {
...
}

사용 방식을 조정하여 이제 반응형 getter를 사용해 보겠습니다:

// Reacts to changes in props.userId. No `computed` needed!
const { data: projects } = useUserProjects(() => props.userId)

이를 통해 불필요한 메모이제이션 오버헤드 없이 간결한 구문과 필요한 반응성을 얻을 수 있습니다.

추적되는 기타 쿼리 옵션

위에서는 반응형 의존성을 추적하는 쿼리 옵션 하나만 다루었습니다. 하지만 queryKey 외에도 enabled는 다음도 허용합니다 반응형 값의 사용입니다. 일부 파생 상태를 기반으로 쿼리의 가져오기를 제어하려는 상황에서 유용합니다:

export function useUserProjects(userId: MaybeRef<string>) {
return useQuery(
queryKey: ['userProjects', userId],
queryFn: () => api.fetchUserProjects(toValue(userId)),
enabled: () => userId.value === activeUserId.value,
);
}

이 옵션에 대한 자세한 내용은 useQuery 참조 페이지에서 확인할 수 있습니다.

불변성

useQuery의 결과는 항상 불변입니다. 이는 성능과 캐싱을 위해 필요합니다. useQuery에서 반환된 값을 변경해야 한다면 데이터의 복사본을 만들어야 합니다.

이 설계의 한 가지 영향은 useQuery의 값을 v-model 같은 양방향 바인딩에 전달해도 작동하지 않는다는 것입니다. 데이터를 제자리에서 업데이트하기 전에 변경 가능한 복사본을 생성해야 합니다.

핵심 요점

  • enabledqueryKey는 반응형 값을 받을 수 있는 두 가지 쿼리 옵션입니다.
  • refs, 일반 값, 반응형 getter라는 세 가지 값 타입을 모두 허용하는 쿼리 옵션을 Vue에 전달합니다.
  • 쿼리가 사용하는 값의 변경에 따라 반응할 것으로 예상한다면 해당 값이 반응형인지 확인합니다. (즉, refs를 쿼리에 직접 전달하거나 반응형 getter를 사용합니다)
  • 쿼리가 반응형일 필요가 없다면 일반 값을 전달합니다.
  • 속성 접근과 같은 단순한 파생 상태에는 computed 대신 반응형 getter를 사용하는 것을 고려하세요.
  • useQuery의 결과는 항상 불변입니다.