useQuery, 그 이상에 대해

limhi·2024년 1월 26일
0

React Query

목록 보기
3/4
post-thumbnail

useQuery 에 대해 그저 사용만 했을 뿐, 어떻게 하면 더 '잘' 사용할 수 있을 지 공부하던 중 이를 정리하고자 글을 작성하였습니다.

useQuery

React Query 에서 대표적으로 사용하는 hook 으로, 서버에서 데이터를 가져옵니다. 이를 호출하는 최소한의 인자는 두 가지인데 쿼리의 고유키인 query key 와 데이터를 가져올 비동기 함수를 작성합니다.

// axios 라이브러리를 활용한 query 문 작성 예시
function Example() {
  const result = useQuery({
    queryKey: ['todos'],
    queryFn: () =>
      axios
        .get('https://api.github.com/repos/tannerlinsley/react-query')
        .then((res) => res.data),
  })
}

인자로 넣어주는 query key 는 쿼리를 다시 가져오고, 캐싱하고 공유하는 데 내부적으로 사용됩니다.

useQuery 에 의해 반환되는 결과에는 템플릿 작성 및 기타 데이터 사용에 필요한 모든 정보가 포함되어 있지만 그 중 생산성을 높이기 위해 알아둬야 하는 몇가지 상태가 있습니다.

  • isLoading or status === 'loading' : 쿼리에 아직 데이터가 없는 상태

  • isError or status === 'error' : 쿼리에 오류가 발생한 상태

  • isSuccess or status === 'success' : 쿼리가 성공했으며 데이터를 사용할 수 있는 상태

이러한 기본 상태를 이용하여 쿼리가 isError 상태일 경우 error 속성을 통해 오류를 확인할 수 있으며, isSuccess 상태를 확인하여 데이터를 사용함으로써 런타임 중의 불안 요소를 없앨 수 있습니다.

대부분의 쿼리에서는 일반적으로 isLoading 상태를 확인한 다음 isError 상태를 확인, 마지막으로 데이터가 사용 가능하다고 가정하고 성공적인 상태를 랜더링 하는 것으로 충분합니다.

function Todos() {
  const { isLoading, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })
  
  if (isLoading) {
    return <span>Loading...</span>
  }
  
  if (isError) {
    return <span>Error: {error.message}</span>
  }
  
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

refetching

자주 변하지 않는 데이터임에도 불구하고 계속해서 refetch 되는 것은 낭비입니다.
따라서 이를 따로 설정하여 refetch 를 조절할 필요가 있는데요.

대략적으로 react-query 의 QueryClient 에서 설정할 수 있는 refetch 가 필요한 조건은 다음과 같습니다.

  • react-query 가 포함되어 있는 컴포넌트가 재실행 될 때
  • 다른 페이지를 보고 있다가 다시 해당 웹 페이지에 focus 될 때
  • 네트워크가 재연결 될 때
  • stale time 이 지났을 때
  • 설정한 refetchInterval 이 지났을 때

그리고 이를 설정할 수 있는 refetchOnMount, refetchOnWindow, refetchOnReconnect, refetchInterval 등의 default option 을 활용하여 위와 같은 상황을 제어할 수 있습니다.

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 600000, // 10분
      cacheTime: 900000, // 15분
      refetchOnMount: false,
      refetchOnWindowFocus: false,
      refetchOnReconnect: false,
    },
  },
});
  • staleTime: 600,000ms 즉, 10분으로 설정(default: 0) ⇒ 이 데이터는 10분까지는 fresh하다는 의미

  • cacheTime: 900,000ms 즉, 15분으로 설정(default: 5분) ⇒ stale time이 cache time을 넘기면 안된다. 기존에 있던 캐시는 데이터를 refetch 하는 동안 사용자에게 보여주는 데이터인데, 먼저 만료가 되면 refetch하는 동안 사용자에게 보여주는 데이터가 사라져버린다.

  • refetchOnMount: 캐시에 있는 상황에서 refetch 할 지 안할 지 결정하는 옵션. default는 true이고 즉각적인 업데이트가 필요없는 데이터일 때는 false로 설정

  • refetchOnWindowFocus: 페이지를 focus할 때 refetch 할 지 안할 지 결정하는 옵션. default는 true이고 즉각적인 업데이트가 필요없는 데이터일 때는 false로 설정

  • refetchOnReconnect: 네트워크 재연결되었을 때 refetch 할 지 안할 지 결정하는 옵션. default는 true이고 즉각적인 업데이트가 필요없는 데이터일 때는 false로 설정

위와 같이 설정하면 즉각적인 업데이트가 필요하지 않은 상황에서 유용합니다. 그리고 즉각적인 업데이트가 필요한 query 의 경우 아래와 같이 설정해줄 수 있습니다.

const { data : appointments = fallback} = useQuery(queryKeys, queryFn, {
	staleTime: 0, // default값으로 재설정
	cacheTime: 300000, // default값(5분)으로 재설정
	refetchOnMount:true,
	refetchOnReconnect: true,
	refetchOnWindowFocus: true,
	refetchInterval: 1000 // every second refetch
})

여기서 refetchInterval 이란 설정한 시간을 ms 단위로 refetch 해줍니다.

이를 Polling 통신이라고 하는데요.

Polling 통신이란
데이터 통신 방법의 하나로, 하나의 장치(또는 프로그램)가 충돌 회피 또는 동기화 처리 등을 목적으로 다른 장치(또는 프로그램)의 상태를 주기적으로 검사하여 일정한 조건을 만족할 때 송수신 등의 자료 처리를 하는 방식을 말합니다.

쉽게 말하면 데이터 refetch 를 위해 프로그램의 상태를 주기적으로 검사하는 행위이며, 이 작업을 refetchInterval 이 해주고 있는 것입니다. 따라서 위의 예시는 1000ms, 즉 1초마다 polling 통신을 하고 조건이 맞으면 refetch 요청을 하게 됩니다.

placeholderData

useQuery 에서 placeholderData 옵션은 기존의 keepPreviousData 옵션과 그 활용이 동일하며 리액트 쿼리에서 제공하는 함수로 변경되어 모듈에서 불러와 placeholderData 의 값으로 사용할 수 있습니다. 이것은 다음과 같은 상황에서 유용하게 사용됩니다.

  1. 사용자가 이전 페이지로 돌아갈 때, 이전 페이지에서 불러온 데이터를 유지하고 싶을 때

  2. 데이터를 업데이트 하거나 새로 고칠 때, 새로운 데이터와 이전 데이터를 비교하여 변경 사항을 처리하고 싶을 때

아래는 placeholderData 옵션을 활용하는 예제입니다.

const { data, isLoading, isError, isPlaceholderData } = useQuery({
  "todos",
  fetchTodos,
  placeholderData: (previousData, previousQuery) => previousData,
});

if (isPlaceholderData) {
  return (
    <div>
      <h1>Todo List</h1>
      // data mapping
    </div>
  );
}

return (
  <div>
    <h1>Todo List</h1>
    // data mapping
  </div>
);

toggle 이나 select 로 목록에 필터링을 줄 때 새로운 데이터를 불러옴으로서 화면이 깜빡거리게 하는 것보다 위의 옵션을 활용하여 기존 데이터를 유지하도록 하면 사용자 경험을 더욱 높일 수 있을 것으로 예상됩니다.

references

TanStack Query
cloud_oort.log - React Query 공부 (10) Re-fetching
React Query의 useQuery에서 keepPreviousData 옵션 활용하기

profile
null 사랑하지 않아 - 어반자카파

0개의 댓글