React query - useQuery

YuRim Lee·2024년 10월 18일
0
post-thumbnail

부제 : useQuery 에서 어떻게 캐싱이 될까?

useQuery?

  • useQuery는 React Query에서 제공하는 가장 중요한 훅 중 하나
  • 비동기 데이터 fetching, 캐싱, 업데이트 및 재시도 로직을 매우 효율적으로 처리할 수 있게 해줍니다. 즉, useQuery는 서버에서 데이터를 가져오는 과정을 관리하면서, 개발자가 일일이 처리해야 할 로직을 간편하게 해결해주는 역할을 합니다.

useQuery의 주요 역할:

  1. 데이터 fetching 관리
    useQuery는 지정된 queryFn(쿼리 함수)을 실행하여 서버로부터 데이터를 가져옵니다. 이를 통해 REST API, GraphQL, 또는 다른 비동기 데이터 소스를 쉽게 호출할 수 있습니다.
const { data, isLoading, error } = useQuery(['posts'], fetchPosts)
  1. 캐싱
    useQuery는 가져온 데이터를 자동으로 캐시에 저장합니다. 동일한 쿼리 키를 사용하여 동일한 데이터를 요청하면, 서버로 새로운 요청을 보내지 않고 캐시된 데이터를 즉시 반환합니다. 이를 통해 서버 요청 횟수를 줄이고 성능을 최적화할 수 있습니다.

  2. 데이터의 자동 업데이트
    useQuery는 캐시된 데이터가 일정 시간이 지나거나 데이터가 변경될 때 자동으로 다시 데이터를 가져와 최신 상태로 업데이트합니다. 이를 stale-while-revalidate 패턴이라고 합니다. 예를 들어, staleTime이 지나면 캐시된 데이터가 오래된 것으로 간주되고, 다시 데이터를 가져옵니다.

  3. 자동 재시도
    useQuery는 네트워크 에러 등으로 인해 데이터 가져오기가 실패했을 때, 자동으로 재시도(retry)를 수행합니다. 기본적으로 3번까지 재시도하며, 이 동작은 설정에 따라 조정할 수 있습니다.

const { data, isError } = useQuery(['posts'], fetchPosts, {
  retry: 2 // 최대 2번까지 재시도
})
  1. 로딩 상태 및 에러 관리
    useQuery는 로딩 상태, 에러 상태, 성공 상태 등의 상태를 관리할 수 있습니다. 이를 통해 로딩 스피너를 보여주거나, 에러 발생 시 에러 메시지를 출력하는 등의 처리를 쉽게 할 수 있습니다.
const { data, isLoading, isError } = useQuery(['posts'], fetchPosts)

if (isLoading) return <div>Loading...</div>
if (isError) return <div>Error occurred</div>
  1. 쿼리 키를 통한 캐싱 및 데이터 식별
    queryKey는 각 쿼리의 고유 식별자 역할을 합니다. queryKey에 따라 캐시를 구분하며, 동일한 queryKey를 가진 쿼리는 동일한 캐시 데이터를 공유합니다.
const { data } = useQuery(['posts', userId], () => fetchPosts(userId))
  1. 수동 데이터 갱신 (refetch)
    필요에 따라 데이터를 수동으로 다시 가져올 수 있도록 refetch 함수를 제공합니다. 이 기능은 데이터가 사용자 상호작용에 의해 갱신되어야 할 때 유용합니다.
const { data, refetch } = useQuery(['posts'], fetchPosts)
<button onClick={() => refetch()}>Refetch Data</button>
  1. 의존성 기반의 데이터 fetching (enabled 옵션)
    특정 조건이 충족될 때만 쿼리를 실행하도록 제어할 수 있습니다. 이를 위해 enabled 옵션을 사용하며, 주로 다른 상태 값에 따라 쿼리를 실행하거나 멈추고 싶을 때 사용됩니다.
const { data } = useQuery(['posts'], fetchPosts, {
  enabled: isUserLoggedIn, // isUserLoggedIn이 true일 때만 쿼리 실행
})
  1. 배경 동기화
    useQuery는 백그라운드에서 데이터를 지속적으로 동기화할 수 있습니다. 이를 통해 실시간으로 데이터를 최신 상태로 유지할 수 있습니다.
const { data } = useQuery(['posts'], fetchPosts, {
  refetchInterval: 60000, // 60초마다 데이터를 다시 가져옴
})

useQuery를 사용한 기본 예시:

import { useQuery } from '@tanstack/react-query'

const fetchPosts = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts')
  if (!response.ok) throw new Error('Network response was not ok')
  return response.json()
}

const Posts = () => {
  const { data, isLoading, isError, error } = useQuery(['posts'], fetchPosts)

  if (isLoading) return <div>Loading...</div>
  if (isError) return <div>Error: {error.message}</div>

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

export default Posts

요약

useQuery는 비동기 데이터를 가져오고, 캐시하고, 관리하는 모든 작업을 간편하게 처리해 줍니다.
캐싱과 자동 재시도, 자동 갱신 같은 기능을 제공해 서버 데이터와의 동기화를 쉽게 유지할 수 있습니다.
상태 관리를 통해 로딩, 에러, 성공 상태를 적절히 관리할 수 있습니다.
수동 데이터 갱신, 배경 동기화 등 다양한 옵션을 제공하여 데이터 fetching을 정교하게 제어할 수 있습니다.
이를 통해 효율적으로 비동기 데이터를 관리하고, 코드의 복잡성을 줄일 수 있습니다.

컴포넌트를 호출하면 useQuery 가 호출되서 fetching 된다는 거지?

맞습니다! useQuery 훅을 사용한 컴포넌트를 호출하면 해당 컴포넌트가 렌더링될 때 자동으로 useQuery가 실행되고, 데이터를 fetching합니다. React Query가 비동기 함수(queryFn)를 실행해서 데이터를 가져오고, 그 결과를 컴포넌트 내에서 사용할 수 있게 되는 구조입니다.

useQuery는 언제 실행돼?

useQuery는 다음과 같은 경우에 자동으로 실행됩니다:

  1. 컴포넌트가 처음 마운트될 때
    컴포넌트가 렌더링될 때, 즉 컴포넌트가 DOM에 처음 추가되면 useQuery가 자동으로 실행됩니다.
    이때 지정된 queryFn 함수가 호출되어 데이터를 fetching합니다.
const { data, isLoading } = useQuery(['posts'], fetchPosts)
  1. queryKey가 변경될 때
    queryKey는 React Query에서 쿼리를 구별하고, 데이터를 캐싱하는 데 사용됩니다. queryKey가 변경되면 React Query는 이 변화를 감지하고, 자동으로 데이터를 다시 fetching합니다.
    예를 들어, queryKey에 userId가 포함되어 있을 때, userId가 변경되면 useQuery는 다시 실행됩니다:
const { data, isLoading } = useQuery(['user', userId], () => fetchUser(userId))

userId가 변경되면 React Query는 이 쿼리가 새로운 데이터라고 판단하고 다시 데이터를 가져옵니다.

  1. 캐시된 데이터가 만료(stale)되었을 때
    React Query는 캐시된 데이터를 유지하면서 staleTime이라는 설정을 통해 데이터를 새로 가져올 시점을 결정합니다.
    staleTime이 지나면 캐시된 데이터가 "stale(낡음)" 상태로 간주되고, 데이터가 만료되면 다시 fetching이 자동으로 실행됩니다.
const { data } = useQuery(['posts'], fetchPosts, { staleTime: 10000 })
이 예에서는 10초가 지나면 캐시된 데이터가 만료되고, 쿼리가 다시 실행됩니다.
  1. 페이지를 다시 포커싱할 때
    기본적으로 React Query는 페이지가 다시 포커싱될 때 (즉, 사용자가 페이지를 떠났다가 다시 돌아왔을 때) 자동으로 데이터를 새로 fetching합니다. 이 기능은 최신 데이터를 유지하기 위해 유용합니다.
const { data } = useQuery(['posts'], fetchPosts)

사용자가 탭을 이동했다가 다시 돌아오면 useQuery가 다시 실행됩니다.

  1. 네트워크가 재연결될 때
    네트워크 연결이 끊겼다가 다시 연결될 때에도 useQuery는 자동으로 실행되어 데이터를 다시 fetching합니다.

  2. refetch를 수동으로 호출할 때
    필요에 따라 데이터를 다시 가져오고 싶다면, refetch 함수를 수동으로 호출할 수 있습니다. useQuery는 이 함수가 호출될 때마다 쿼리를 다시 실행합니다.

const { data, refetch } = useQuery(['posts'], fetchPosts)

return <button onClick={() => refetch()}>Refetch Data</button>
  1. 의존성 옵션인 enabled가 true로 변경될 때
    useQuery는 enabled 옵션을 통해 언제 쿼리를 실행할지 제어할 수 있습니다. 이 값이 false였다가 true로 변경되면 쿼리가 실행됩니다.
const { data } = useQuery(['posts'], fetchPosts, { enabled: isUserLoggedIn })

isUserLoggedIn 값이 true가 되면 useQuery가 실행됩니다.

요약:

  • 컴포넌트가 처음 렌더링될 때 (DOM에 추가될 때)
  • queryKey가 변경될 때
  • 캐시된 데이터가 만료(stale) 상태일 때
  • 페이지가 다시 포커싱될 때
  • 네트워크가 다시 연결될 때
  • 수동으로 refetch를 호출할 때
  • enabled 옵션이 true로 변경될 때
    이런 다양한 시점에서 useQuery는 데이터를 다시 가져오며, 이 과정을 통해 최신 데이터를 유지할 수 있습니다.

꼭 파라미터만 query key 에 넣을수있엉 ?

queryKey에 꼭 파라미터만 넣을 필요는 없습니다. queryKey는 React Query에서 캐싱과 데이터를 구별하기 위한 고유 식별자로 사용되며, 단순히 파라미터뿐만 아니라 모든 값을 포함할 수 있습니다. queryKey는 배열이나 문자열, 또는 객체로 구성할 수 있습니다.

ex) query key 가 1 을 호출했어 그러고 2를 호출해 그리고 다시 1을 호출해

그러면 이미 캐싱이 되어있잖아 1 이
근데 db 상에서는 캐시가 1인 데이터가 바껴있어
그러면 1이 이미 캐싱되어잇기때매 재호출안해 ?

  • 좋은 질문입니다! React Query는 캐싱된 데이터를 사용하는데, 이를 최신 상태로 유지하기 위한 여러 가지 방법을 제공합니다. queryKey가 "1"인 데이터를 다시 호출했을 때, 캐시된 데이터를 바로 사용하긴 하지만, 데이터가 업데이트되었을 가능성을 대비해 재호출이 필요한 상황에서는 데이터를 자동으로 다시 fetching할 수 있습니다.

React Query가 캐시된 데이터를 관리하는 방식:

  1. 캐시된 데이터 사용:

queryKey가 "1"인 쿼리는 이미 캐싱된 데이터가 있기 때문에 바로 캐싱된 데이터를 사용합니다. 이렇게 하면 빠르게 UI를 업데이트할 수 있습니다.

  1. staleTime:

React Query는 staleTime이라는 설정을 통해 데이터를 얼마나 오래 "신선한 상태"로 유지할지 결정할 수 있습니다. staleTime이 지나면 데이터는 "stale" 상태가 되고, React Query는 자동으로 다시 데이터를 fetching할 수 있습니다.
기본적으로 staleTime은 0으로 설정되어 있기 때문에, 데이터를 캐시에서 사용하더라도 곧바로 다시 fetching하여 최신 데이터를 가져옵니다.

const { data } = useQuery('1', fetchData, { staleTime: 5000 }) // 5초 동안 

캐시된 데이터가 유효
위 예시에서는 5초 동안 캐시된 데이터가 신선한 것으로 간주되며, 그 후에는 다시 fetching이 이루어집니다.

  1. 페이지 포커스 시 자동 fetching:

React Query는 페이지가 다시 포커스될 때(사용자가 다른 탭으로 갔다가 돌아왔을 때) 캐시된 데이터가 stale 상태가 아니더라도 자동으로 다시 fetching하여 최신 데이터를 가져옵니다. 이 기능은 기본으로 활성화되어 있습니다.

const { data } = useQuery('1', fetchData, { refetchOnWindowFocus: true })
  1. 수동으로 데이터를 다시 fetching:

만약 데이터가 바뀌었다는 것을 알고 있거나, 강제로 다시 fetching하고 싶다면 refetch 함수를 호출할 수 있습니다.

const { data, refetch } = useQuery('1', fetchData)

// 수동으로 refetch 호출
<button onClick={() => refetch()}>다시 불러오기</button>
  1. 데이터가 바뀌었다는 것을 감지하는 전략 (onSuccess나 onSettled):

서버에서 데이터를 가져온 후 onSuccess나 onSettled와 같은 옵션을 통해 데이터를 갱신했는지 확인할 수 있습니다.
onSuccess 콜백은 데이터 fetching이 성공적으로 완료된 후 실행되며, 이를 통해 추가적인 로직을 처리할 수 있습니다.

그럼 staleTime 이걸 설정안하면 사실상 캐싱되는 데이터가 없는거네 ?

  • 아니요, staleTime을 설정하지 않아도 데이터는 여전히 캐싱됩니다. 하지만 기본 설정에서 캐시된 데이터가 즉시 stale 상태가 됩니다. 즉, 데이터는 캐싱되지만, 캐시된 데이터를 바로 stale(낡음)으로 간주하고 최신 데이터를 서버에서 다시 fetching하게 됩니다.

staleTime을 설정하지 않았을 때의 동작:

  1. 데이터 캐싱: useQuery는 기본적으로 모든 데이터를 캐싱합니다. 하지만 staleTime이 기본값인 0일 경우, 캐시된 데이터는 즉시 "stale" 상태로 간주됩니다.
  2. stale 상태: 캐시된 데이터가 stale 상태일 때, React Query는 컴포넌트가 마운트되거나 페이지가 포커스될 때마다 자동으로 최신 데이터를 다시 fetching합니다.
  3. 캐시된 데이터 사용: stale 상태의 캐시 데이터는 여전히 사용됩니다. 즉, 서버로부터 새 데이터를 받아오기 전에 캐시된 데이터를 먼저 화면에 보여주지만, 곧바로 새로운 데이터를 가져오는 요청을 동시에 시작합니다.

그러니까 캐시된 데이터를 화면에 먼저 표시해놓고 서버에서 가져오는게 뭐가 좋은거야 ??

캐시된 데이터를 먼저 화면에 표시하고, 동시에 서버에서 최신 데이터를 가져오는 방식은 사용자 경험(UX)을 개선하고, 성능 최적화 측면에서 여러 가지 이점이 있습니다. 다음은 이러한 방식이 좋은 이유입니다:

  1. 빠른 응답 시간으로 사용자 경험 향상
    캐시된 데이터를 사용하면, 사용자가 페이지를 열었을 때 서버 응답을 기다리지 않고 즉시 화면에 데이터를 표시할 수 있습니다. 서버에서 최신 데이터를 받아오는 동안 사용자에게는 이미 화면에 데이터가 보이기 때문에 앱이 더 빠르고 응답성 있게 느껴집니다.
    서버 요청에 시간이 걸리는 경우에도 사용자는 기다림 없이 기존 데이터를 먼저 확인할 수 있습니다.
    예시:
    쇼핑몰 사이트에서 제품 리스트를 보다가 새로고침을 하면, 이전에 본 제품 리스트가 바로 표시되고, 최신 재고 상태나 가격은 나중에 서버에서 업데이트됩니다.
    사용자 입장에서는 페이지 로딩 시간이 거의 느껴지지 않음.
    최신 정보가 필요할 경우에만 백그라운드에서 자동으로 데이터가 업데이트.

  2. 네트워크 지연에 대한 완화
    네트워크 상태가 불안정하거나 서버 응답 시간이 길어지더라도, 캐시된 데이터를 보여주면 사용자가 빈 화면을 보지 않게 됩니다.
    서버에서 최신 데이터를 받는 데 시간이 걸리더라도, 캐시된 데이터를 통해 중간 경험을 제공함으로써, 사용자가 더 나은 경험을 하게 됩니다.
    예시:
    모바일 데이터가 느리거나 와이파이 연결이 약한 경우, 캐시된 데이터를 먼저 보여주면, 사용자는 일단 화면이 로드되었다고 인식합니다. 최신 데이터가 곧 업데이트되면, 사용자는 성능 문제를 크게 느끼지 않게 됩니다.

  3. 데이터 변동이 적은 경우 캐시가 유용
    만약 데이터가 자주 변하지 않는 경우, 캐시된 데이터를 사용하는 것이 더 효율적입니다.
    자주 변하지 않는 데이터를 서버에서 매번 불러오는 대신, 캐시를 사용하여 네트워크 요청을 줄이고 성능을 최적화할 수 있습니다.
    예시:
    사용자의 프로필 정보나 설정과 같은 데이터는 자주 변하지 않기 때문에, 캐시된 데이터를 먼저 보여주고, 서버에서 최신 정보가 있는지 확인하는 방식이 적합합니다.

  4. 네트워크 요청을 줄여 성능 최적화
    캐시를 사용하면 불필요한 네트워크 요청을 줄일 수 있습니다. 예를 들어, 사용자가 자주 방문하는 페이지나 기능에서 동일한 데이터를 반복적으로 요청하는 대신, 캐시된 데이터를 재사용할 수 있습니다.
    이로 인해 서버 부하가 줄어들고, 앱 성능이 전반적으로 향상됩니다.
    예시:
    사용자가 같은 리스트를 여러 번 확인할 때마다 매번 서버에 요청하지 않고, 캐시된 데이터를 먼저 제공하면, 네트워크 비용을 절감하고 서버의 부하를 줄일 수 있습니다.

  5. 점진적 데이터 업데이트로 사용자 경험 부드럽게 유지
    캐시된 데이터를 먼저 보여준 뒤, 서버에서 최신 데이터를 가져오면, 사용자가 느끼는 변경 사항이 더 부드럽게 이루어집니다. 서버 데이터를 기다리느라 화면이 계속 로딩 상태에 머무르지 않고, 사용자는 화면을 바로 사용할 수 있고, 그동안 백그라운드에서 데이터를 업데이트하면 됩니다.
    데이터가 업데이트되면 화면을 부드럽게 갱신함으로써 사용자 경험을 자연스럽게 유지할 수 있습니다.
    예시:
    뉴스 앱에서 캐시된 기사 목록을 먼저 보여준 후, 최신 기사를 백그라운드에서 가져오는 경우, 사용자는 기다림 없이 앱을 탐색할 수 있으며, 기사가 업데이트될 때 자연스럽게 새로운 정보가 화면에 표시됩니다.

  6. 오프라인 모드 지원
    캐시된 데이터를 활용하면 오프라인 상태에서도 일부 기능을 사용할 수 있습니다. 네트워크 연결이 없을 때도 캐시에 저장된 데이터를 이용해 일시적인 오프라인 경험을 제공할 수 있습니다.
    예시:
    사용자가 비행기 모드로 전환되었을 때도, 이전에 불러왔던 콘텐츠가 캐시에서 보여지므로 오프라인 상태에서도 앱을 사용할 수 있습니다.

요약:

  • 빠른 화면 로딩: 캐시된 데이터를 먼저 보여줌으로써 사용자는 기다림 없이 콘텐츠를 확인할 수 있습니다.
  • 네트워크 문제 완화: 서버 응답이 느리거나 네트워크 연결이 불안정할 때도 캐시를 통해 즉시 응답성 있는 경험을 제공합니다.
  • 성능 최적화: 불필요한 네트워크 요청을 줄여 성능을 개선하고, 서버 부하를 줄입니다.
  • 오프라인 지원: 캐시를 통해 네트워크 연결 없이도 일정 부분 기능을 제공할 수 있습니다.

이런 이유로, 캐시된 데이터를 먼저 사용하고 서버에서 최신 데이터를 가져오는 방식은 사용자 경험을 개선하고 성능을 최적화하는 데 매우 유용합니다.

결론

부제: staleTime 설정 없이 useQuery 는 어떻게 동작하냐면

  1. staleTime 설정이 없다는것은 데이터의 신선시간을 설정하지 않았다라는 것이다.
  2. fetching 후 데이터는 즉시 stale(낡음)처리가 된다.
  3. 컴포넌트가 호출되어서 useQuery 가 호출되면 다시 서버에서 데이터를 fetching 한다.
  4. staleTime 없이도 데이터는 캐싱이 되어서 서버에서 가져오는 동안 캐싱된 데이터를 보여주고 데이터를 가져온 순간 교체된다.
  5. 캐싱이 된 데이터를 쓰는것의 장점은 사용자가 기다리지 않아 사용자의 경험이 향상된다.
profile
성장하는개발자

0개의 댓글