[React] TanStack Query 캐시 레벨과 옵저버 레벨

동일한 쿼리 키를 사용하는 useQuery 를 여러 컴포넌트에서 사용할 수 있습니다. 이런 경우 보통 커스텀 훅으로 구현하여 여러 컴포넌트에서 사용합니다.

 

캐시 레벨 (Cache level)

각각의 쿼리 키에 대해서, 캐시 엔트리는 오직 한 개만 존재합니다. 즉, 동일한 쿼리 키를 가진 useQuery 를 여러 곳에서 사용해도 캐시 엔트리는 하나입니다. 이 캐시 엔트리에 대응하는 레벨을 캐시 레벨이라고 합니다.

 

캐시 레벨에서 작동하는 useQuery 의 옵션으로는 staleTimegcTime 이 있습니다. 캐시 엔트리는 하나만 있기 때문에, 해당 옵션들은 엔트리가 오래되는 시점 또는 가비지 컬렉션이 될 수 있는 시점을 특정합니다.

 

 

옵저버 레벨 (On observer level)

React Query의 옵저버는 하나의 캐시 엔트리를 향해 생성된 구독입니다. 옵저버는 캐시 엔트리에서의 변화를 주시하며 변화가 있을 때마다 알림을 받습니다.

 

옵저버를 생성하는 기본적인 방법은 useQuery 를 호출하는 것입니다. 매번 호출할 때마다, 옵저버를 생성하며, 데이터가 변할 때마다 컴포넌트는 렌더링 할 것입니다. 이는 동일한 캐시 엔트리를 주시하는 다수의 옵저버를 만들 수 있음을 의미합니다. 즉, 동일한 쿼리 키를 가진 useQuery 를 여러 곳에서 사용하면 캐시 엔트리는 하나지만 각각의 호출에 대해 다수의 옵저버가 생성됩니다.

 

옵저버 레벨에서 작동하는 useQuery 의 옵션으로는 selectkeepPreviousData 가 있습니다.

 

 

Placeholder Data와 Initial Data

React Query에서는 쿼리에 데이터를 동기적으로 미리 채워 놓은 방법으로 placeholderDatainitialData 옵션을 제공합니다. 쿼리에 데이터를 미리 채워 놓으면 초기 로딩을 생략할 수 있습니다. 쿼리는 loading 상태를 건너뛰고 바로 success 상태가 됩니다.

 

■ 차이점:

  • initialData 는 캐시 레벨에서 작동하며 placeholderData 는 옵저버 레벨에서 작동한다.
  • initialData 는 캐시 레벨에서 작동하기 때문에 오직 하나만 존재할 수 있으며 placeholderData 옵저버 레벨에서 작동하기 때문에 컴포넌트 별로 서로 다른 값을 갖고 있을 수 있다.
  • initialData 는 캐시에 지속적으로 남아있으며 실제 데이터와 비슷한 수준의 데이터로 평가된다. placeholderData 는 캐시에 지속적으로 남아있지 않으며 실제 데이터를 불러오는 동안 대신 보여주는 역할을 한다.

 

placeholderData 를 사용하면 옵저버가 처음 마운트 될 때 항상 백그라운드에서 데이터를 리패치합니다. 리패치하는 동안 useQueryplaceholderData 값을 반환합니다.

 

initialData 는 실제 캐시에 들어갈 만큼 좋은 수준의 데이터이고 또 유효한 데이터이기 때문에, staleTime 을 따릅니다. 만약 staleTime 이 0(기본값)이라면 바로 백그라운드에서 데이터를 리패치합니다. 하지만 쿼리의 staleTime 을 그 이상으로 설정했다면 stale 상태가 되기 전까지 initialData 를 사용합니다.

 

간단하게 initialData 를 실제 데이터, placeholderData 를 초기 로딩시 로딩 스피너 대신 보여주는 데이터라고 생각하면 될 것입니다. 리패치에서 에러 발생 시 initialData 는 그대로 남아있지만, placeholderData 는 사라져 데이터는 undefined 가 됩니다. 즉, initialDatastale-while-revalidate 메커니즘으로 작동합니다.

 

 

Placeholder Data

플레이스홀더 데이터를 사용하면 쿼리에 이미 데이터가 있는 것처럼 동작하지만 데이터가 캐시에 유지되지는 않습니다. 이는 백그라운드에서 실제 데이터를 가져오는 동안 로딩 스피너 대신 UI로 지정한 데이터를 보여주고 싶을 때 사용할 수 있습니다.

 

플레이스홀더 데이터가 있으면 쿼리가 success 상태로 시작합니다. isPlaceholderData 플래그를 통해 해당 data 가 플레이스홀더 데이터인지 확인할 수 있습니다.

 

 

useQueryplaceholderData 옵션을 통해 지정할 수 있으며 값 또는 함수를 전달할 수 있습니다.

 

■ Placeholder Data as a Value

function Todos() {
  const result = useQuery({
    queryKey: ['todos'],
    queryFn: () => fetch('/todos'),
    placeholderData: placeholderTodos,
  })
}

 

■ Placeholder Data as a Function

const result = useQuery({
  queryKey: ['todos', id],
  queryFn: () => fetch(`/todos/${id}`),
  placeholderData: (previousData, previousQuery) => previousData,
})

 

 

Initial Data

Initial Data를 사용하면 쿼리에 이미 데이터가 있는 것처럼 동작하며 해당 값은 캐시에 유지됩니다. 따라서 실제 데이터 수준의 데이터를 전달해야 합니다.

 

Placeholder Data와 마찬가지로 초기 로딩을 생략할 수 있으나 Initial Data는 staleTime 에 영향을 받습니다. staleTime 값이 0이 아니면 마운트되어도 쿼리가 stale 상태가 아니면 바로 리패치되지 않습니다.

 

 

useQueryinitialData 옵션을 통해 지정할 수 있으며 값 또는 함수를 전달할 수 있습니다.

 

■ Initial Data as a Value

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: initialTodos,
})

 

■ Initial Data as a Function

const result = useQuery({
  queryKey: ['todos'],
  queryFn: () => fetch('/todos'),
  initialData: () => getExpensiveTodos(),
})

'React' 카테고리의 다른 글

[React] TanStack Query v5 마이그레이션 준비  (0) 2025.02.26
[React] 폰트 최적화 알아보기  (0) 2024.05.25