본문 바로가기

FrontEnd

리액트 쿼리 : 초기값과 플레이스홀더

반응형

원문 보기 : https://tkdodo.eu/blog/placeholder-and-initial-data-in-react-query

리액트 쿼리

대부분의 경우 우리(및 사용자)는 성가신 로딩 스피너를 싫어합니다. 그들은 때때로 필요하지만 우리는 여전히 가능하면 피하고 싶습니다.
React Query는 이미 많은 상황에서 이를 제거할 수 있는 도구를 제공합니다.
  • 백그라운드 업데이트가 진행되는 동안 캐시에서 오래된 데이터를 가져오고,
  • 나중에 필요하다는 것을 알면 데이터를 미리 가져올 수 있으며 - prefetch data
  • 쿼리 키가 변경되어 하드 로딩 상태를 피하기 위해 이전 데이터를 유지할 수도 있습니다. - keep previous data
또 다른 방법은 우리의 사용 사례에 적합할 것으로 생각되는 데이터로 캐시를 동기식으로 미리 채우는 것입니다.
 
이를 위해 React Query는 두 가지 다르지만 유사한 접근 방식인 Placeholder Data와 Initial Data를 제공합니다.

공통점

둘 다 동기적으로 사용할 수 있는 데이터로 캐시를 미리 채우는 방법을 제공합니다.
또한 이 중 하나가 제공되면 쿼리가 로드 상태가 아니라 성공 상태로 직접 이동한다는 의미입니다.
또한 값을 계산하는 데 비용이 많이 드는 경우 값 또는 값을 반환하는 함수일 수 있습니다.
function Component() {
  // ✅ status will be success even if we have not yet fetched data
  const { data, status } = useQuery(['number'], fetchNumber, {
    placeholderData: 23,
  })

  // ✅ same goes for initialData
  const { data, status } = useQuery(['number'], fetchNumber, {
    initialData: () => 42,
  })
}​
마지막으로 캐시에 이미 데이터가 있는 경우 둘 다 효과가 없습니다.
 
둘의 차이점은 뭔가요?
이를 이해하려면 React Query의 옵션이 어떻게(그리고 어떤 "레벨"에서) 작동하는지 간략하게 살펴봐야 합니다.

On cache level

각 쿼리 키에는 캐시 항목이 하나만 있습니다.
React Query의 장점 중 하나는 애플리케이션에서 동일한 데이터를 선택적으로 "전역적으로" 공유할 수 있다는 것입니다.
useQuery에 제공하는 일부 옵션은 해당 캐시 항목에 영향을 미치며 대표적인 예로는 staleTime 및 cacheTime이 있습니다.

캐시 항목(entry)는 하나만 있기 때문에 이러한 옵션은 해당 항목에만 오래된(stale) 것으로 간주되는 시점

또는 가비지 수집이 가능한 시점을 지정합니다.


On observer level

React Query의 관찰자는 일반적으로 하나의 캐시 항목(entry)에 대해 생성된 구독입니다.
관찰자는 캐시 항목의 변경 사항을 관찰하고 변경 사항이 있을 때마다 알림을 받습니다.
 
관찰자를 만드는 기본 방법은 useQuery를 호출하는 것입니다.
그렇게 할 때마다 관찰자를 만들고 데이터가 변경되면 컴포넌트가 다시 렌더링됩니다.
이것은 물론 동일한 캐시 항목을 보는 여러 관찰자가 있을 수 있음을 의미합니다.
 
React Query Devtools의 쿼리 키 왼쪽에 있는 숫자로 쿼리에 몇 명의 옵저버가 있는지 확인할 수 있습니다.
(이 예에서는 3).

관찰자 수준에서 작동하는 일부 옵션은 select 또는 keepPreviousData입니다.
select가 data transformations에 매우 유용한 이유는,
동일한 캐시 항목을 관찰하면서,
다른 컴포넌트에서 데이터의 다른 조각을 구독하는 기능입니다.

차이점

InitialData는 캐시 수준에서 작동하는 반면 placeholderData는 관찰자 수준에서 작동합니다. 여기에는 몇 가지 의미가 있습니다.

Persistence (영속성)

initialData는 캐시에 유지됩니다.

React Query가 해당 데이터를 백엔드에서 가져온 것처럼 행동하도록 합니다.

 

캐시 수준에서 작동하기 때문에 하나의 initialData만 있을 수 있으며,

해당 데이터는 캐시 항목이 생성되는 즉시(즉, 첫 번째 관찰자가 마운트될 때) 캐시에 저장됩니다.

다른 initialData로 두 번째 관찰자를 마운트하려고 하면 아무 작업도 수행하지 않습니다.


PlaceholderData는 캐시에 유지되지 않습니다.

반면에 PlaceholderData는 캐시에 유지되지 않습니다.
 
해당 데이터는 서버 데이터와 관련 없는 보여주기용 가짜 데이터임이 명백합니다.
React Query는 실제 데이터를 가져오는 동안 해당 데이터를 표시할 수 있도록 해줍니다.
관찰자 수준에서 작동하기 때문에 이론적으로 다른 컴포넌트에 대해 다른 placeholderData를 가질 수도 있습니다.

Background refetches (백그라운드 리페치)

placeholderData를 사용하면 처음으로 관찰자를 탑재할 때 항상 백그라운드에서 다시 가져옵니다.
데이터가 "실제 데이터가 아니므로" React Query가 실제 데이터를 가져옵니다.
이러한 일이 발생하는 동안 useQuery에서 반환된 isPlaceholderData 플래그도 얻게 됩니다.
이 플래그를 사용하여 사용자가 보고 있는 데이터가 실제로는 placeholderData임을 시각적으로 암시할 수 있습니다.
실제 데이터가 들어오는 즉시 해당 플래그는 false로 전환됩니다.

반면에 InitialData는 데이터가 실제로 캐시에 넣은 좋은 유효한 데이터로 간주되기 때문에 staleTime을 존중합니다.

staleTime이 0(기본값)인 경우에도 백그라운드 다시 가져오기를 확인할 수 있습니다.

 

그러나 쿼리에 staleTime(예: 30초)을 설정한 경우 React Query는 initialData를 보고 다음과 같이 판단합니다.
초기 데이터를 동기적으로 얻을 수 있으며, 해당 데이터는 30초 동안 유효하기 때문에 백엔드에 요청할 필요가 없습니다.
— initialData 및 staleTime을 본 후, React Query
위 상황이 원하는 것이 아니라면, 쿼리에 initialDataUpdatedAt를 제공할 수 있습니다.
이렇게 하면 이 initialData가 생성되었을 때 React Query에 알리고 이를 고려하여 백그라운드 다시 가져오기가 트리거됩니다.
기존 캐시 항목에서 dataUpdatedAt 타임스탬프와 initialData를 사용할 때 매우 유용합니다.
const useTodo = (id) => {
  const queryClient = useQueryClient()

  return useQuery(['todo', id], () => fetchTodo(id), {
    staleTime: 30 * 1000,
    initialData: () =>
      queryClient
        .getQueryData(['todo', 'list'])
        ?.find((todo) => todo.id === id),
    initialDataUpdatedAt: () =>
	// ✅ initial data를 채우는데 사용한 쿼리 데이터가 
    // staleTime(30초) 더 오래된 경우 백그라운드에서 다시 가져옵니다.
      queryClient.getQueryState(['todo', 'list'])?.dataUpdatedAt,
  })
}​

Error transitions

initialData 또는 placeholderData를 사용했을 때,
백그라운드 리페치가 트리거된 다음, 쿼리가 실패한다고 가정합니다.
각 상황에서 어떤 일이 일어날까요?
 

initialData

initialData는 캐시에 유지되므로 refetch 오류는 다른 백그라운드 오류와 같이 처리됩니다.

쿼리는 오류 상태이지만 데이터는 여전히 존재합니다.


placeholderData

쿼리는 error 상태가 되고 데이터는 undefined가 됩니다.

언제 무엇을 사용해야 할까요?

저는 개인적으로 다른 쿼리의 데이터로 해당 쿼리를 미리 채울 때 initialData를 사용하는 것을 좋아하고

다른 모든 쿼리에는 placeholderData를 사용합니다.

 

반응형