2025 . 오은

repo

tanstack-query life-cycle

01) Tanstack Query life cycle

(1) (캐시 데이터에 대한) LifeCycle 설명

TanStack Query의 생명주기는 데이터가 캐시되고, 사용되고, 갱신되는 과정을 포함합니다. 아래는 주요 상태들에 대한 설명입니다.

상태설명
fresh데이터를 새로 패칭할 필요가 없는 상태입니다. staleTime이 지나지 않은 상태로, 캐시 데이터를 그대로 사용할 수 있습니다.
stale데이터를 새로 패칭해야 하는 상태입니다. staleTime이 지난 후로, 새로운 데이터를 가져오기 위해 쿼리가 실행됩니다.
active현재 컴포넌트에서 사용 중인 쿼리 상태입니다. 컴포넌트가 마운트되어 쿼리를 사용하고 있을 때를 말합니다.
inactive더 이상 사용되지 않는 쿼리 상태입니다. 컴포넌트가 언마운트되거나 쿼리가 더 이상 필요하지 않을 때를 말합니다.
deleted캐시에서 제거된 쿼리 상태입니다. gcTime 이 지나면 쿼리가 캐시에서 삭제되어 이 상태가 됩니다.
fetching데이터를 서버에서 가져오고 있는 상태입니다. 이 상태에서는 isFetching이 true로 설정됩니다.

(2) default config(기본 설정)

기본설정의미
staleTime: 0useQuery 또는 useInfiniteQuery에 등록된 queryFn 을 통해 fetch 받아온 데이터는 항상 stale data 취급
refetchOnMount: trueuseQuery 또는 useInfiniteQuery 가 있는 컴포넌트가 마운트 시 stale data 를 refetch 자동 실행
refetchOnWindowFocus: true실행중인 브라우저 화면을 focus 할 때 마다 stale data를 refetch 자동 실행
refetchOnReconnect: trueNetwork 가 끊겼다가 재연결 되었을 때 stale data를 refetch 자동 실행
gcTime(cacheTime): 5분 (1000 x 60 x 5 ms)useQuery 또는 useInfiniteQuery가 있는 컴포넌트가 언마운트 되었을 때 inactive query라 부르며, inactive 상태가 5분 경과 후 GC(가비지콜렉터)에 의해 cache data 삭제 처리
retry: 3useQuery 또는 useInfiniteQuery에 등록된 queryFn 이 API 서버에 요청을 보내서 실패하더라도 바로 에러를 띄우지 않고 총 3번까지 재요청을 자동으로 시도

useQuery 는 캐시데이터에 기반하여 동작합니다. 컴포넌트가 새로 마운트되어도 캐시 데이터가 fresh 하다면 fetch 하지 않습니다!

(3) 헷갈리는 개념 정리

  • (3)-1. staleTime vs gcTime

    • staleTime : 얼마의 시간이 흐른 뒤에 stale 취급할 건지 (default: 0)

    • gcTime : inactive 된 이후로 메모리에 얼마만큼 있을건지 (default: 5분, gcTime 0되면 삭제처리)

  • (3)-2. staleTime 과 stale/fresh 의 관계

    • staleTime > 0 이면, fresh data
    • staleTime = 0 이면, stale data
  • (3)-3. isPending vs. isFetching

    Tanstack Query에서 isPending과 isFetching의 차이를 이해하는 것은 중요한데, 특히 캐시된 데이터와 새로운 데이터를 구별하는 데 도움이 됩니다.

    • isPending:
      • 새로운 캐시 데이터를 서버에서 받고 있는 지 여부를 나타냅니다.
      • 기존의 캐시된 데이터가 있는 경우, isPending은 false이고, isFetching은 true입니다.
      • 캐시된 데이터가 없고 새로 데이터를 가져오는 경우, isPending은 true가 됩니다.
    • isFetching:
      • 서버에서 데이터를 받고 있는지 여부를 나타냅니다.
      • 서버에서 데이터를 가져오는 중일 때 항상 true입니다.

    시나리오

    1. 메인 페이지에서 데이터 로드 (첫 번째 마운트):
      • useQuery가 처음 실행되고 서버에서 데이터를 가져옵니다.
      • isPending: true (새로운 캐시 데이터를 서버에서 받고 있는 중)
      • isFetching: true (서버에서 데이터를 받고 있는 중)
    2. 상세 페이지로 이동:
      • 메인 페이지가 언마운트되고, 상세 페이지가 마운트됩니다.
      • 만약 상세 페이지에서도 useQuery("todos", getTodos)를 사용하고 있다면:
        • 이미 캐시된 데이터가 있기 때문에, 상세 페이지의 isPending은 false이고 isFetching은 true입니다.
    3. 메인 페이지로 돌아옴 (두 번째 마운트):
      • 메인 페이지가 다시 마운트됩니다.
      • 캐시된 데이터가 있는 상태로 마운트되므로:
        • isPending: false (캐시된 데이터가 있기 때문에 새로운 데이터를 기다리지 않음)
        • isFetching: true (데이터를 다시 가져오고 있음)

    요약

    • isPending은 새로운 캐시 데이터를 서버에서 받고 있는지 여부를 나타내며, 캐시 데이터가 있는 경우 false입니다.
    • isFetching은 서버에서 데이터를 받고 있는지 여부를 나타내며, 데이터가 로드되는 동안 항상 true입니다.
    • 캐시된 데이터가 있는 경우, 페이지가 다시 마운트될 때 isPending은 false이고 isFetching은 true입니다.

02) Must-know options

✔️ useQuery를 할 때, 반드시 알아야 하는 옵션에 대해 살펴봅니다.

(1) enabled

  • (1)-1. 개념

    enabled 옵션은 쿼리(queryFn) 실행 여부를 제어합니다. 기본값은 true(만약 설정하지 않는다면 자동으로 true)이며, false로 설정하면 쿼리가 자동으로 실행되지 않습니다. 이 옵션을 사용하여 특정 이벤트 발생 시 쿼리를 실행할 수 있습니다.

    기본 사용법은 아래와 같아요.

    useQuery({
    	queryKey: ["todos"],
    	queryFn: getTodos,
    	enabled: true
    })
    
  • (1)-2. 사용 예제

    • (1)-2-1. Disabling/Pausing Queries (이벤트 발생 시에만 수동 실행하고 싶을 때)

      const { data, refetch } = useQuery({
      	queryKey: ["todos"],
      	queryFn: getTodos,
      	enabled: false
      });
      
      return (
      	<div>
          <button onClick={() => refetch()}>데이터 불러오기</button>
        </div>
      );
      
    • (1)-2-2. Dependent Queries(useQuery 2개 이상이며 실행순서 설정 필요할 때)

      // Dependent Query 예제 (순차적 query 실행)
      // Get the user
      const { data: user } = useQuery({
        queryKey: ['user', email],
        queryFn: getUserByEmail,
      })
      
      const userId = user?.id
      
      // Then get the user's projects
      const {
        status,
        fetchStatus,
        data: projects,
      } = useQuery({
        queryKey: ['projects', userId],
        queryFn: getProjectsByUser,
        // 쿼리는 userId가 존재하는 경우에만 실행돼요 :)
        enabled: !!userId
      })
      // 여기서 !!userId 는 Boolean(userId)와 같습니다.
      

(2) select

  • (2)-1. 개념

    select 옵션은 쿼리 함수에서 반환된 데이터를 변형하여 사용할 수 있도록 합니다. 데이터의 특정 부분만 선택하거나, 데이터를 변환하여 사용할 때 유용해요. 단, 캐시 데이터는 원본 데이터를 유지합니다.

  • (2)-2. 사용예제

    import { useQuery } from 'react-query'
    
    function User() {
      const { data } = useQuery({
    	  queryKey: ["user"],
    	  queryFn: fetchUser,
    	  select: user => user.username
      });
      
      return <div>Username: {data}</div>
    }