Tanstack Query

seunghyun·2025년 1월 29일
0

web

목록 보기
3/4

Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue

npm trend

https://tanstack.com/query/latest

https://tanstack.com/query/v4/docs/framework/react/devtools

https://github.com/tanstack/query

비동기로 (서버) 상태 관리를 하기에 좋은 라이브러리로 알려져있는 (구) React Query 에 대해 알아보자.

React Query라는 이름으로 더 유명한 TanStack Query는 v3 까지는 React만 지원했는데, v4부터 React를 포함해서 Vue, Svelte, Solid 에서도 사용이 가능해지면서 TanStack Query로 이름이 변경되었다. 여전히 TanStack Query라는 명칭보다는 React Query라고 지칭되는 경우가 많다.

state

State: A Compoent's Memory

먼저 상태란, 주어진 시간에 대한 시스템을 표현한 것이다. 문자열, 배열, 객체 등 다양한 형태로 application에 저장된 데이터이다.

더 쉽게 말하면

사용자에 의해서, 또는 시간이 흐름에 따라 CRUD될 수 있는 것이다. (개발자마다 팀마다 정의하는 상태가 다를 수 있다) 즉, 페이지가 로드되거나 렌더링된 이후 사용자가 수행한 모든 동작의 결과라고 할 수 있다.

상태는 두 갈래로 나뉜다.

클라이언트 상태와 서버 상태.

서버와 연동이 되는 부분은 서버 상태로 취급해서 관리하는 것이 좋다. (서버 상태는 프론트에서 보관하는 건 지양)

client state

동기적인 상태.
ex) 언어 변경, UI 테마 변경, 모달창 여닫기 등
클라이언트가 자체적으로 생성한 상태.
서버에서 일어나는 일과 관련이 없다.
클라이언트가 제어하고 소유 가능한 상태이다.

클라이언트에서 소유하고 온전히 제어 가능하다.

초기값 설정이나 조작에 제약이 없다.

다른 사람과 공유되지 않고, 클라이언트 내에서 UI/UX 흐름이나 user interaction에 따라 변할 수 있다.

항상 클라이언트 내에서 최신 상태로 관리된다.

server state

비동기적인 상태.
클라이언트에 표시하는데 필요한 서버데이터.
즉, DB에 존재하는 데이터.
클라이언트가 제어하거나 소유할 수 없다.

클라이언트에서 제어하지 않거나 소유하지 않은 위치에서 관리, 유지된다.

fetching 및 updating을 위해 비동기 api가 필요하다.

소유권이 공유되므로 사용자가 모르는 사이에 다른 사용자가 변경할 수 있다.

주의하지 않으면 애플리케이션에서 out of date 상태가 될 수 있다.

대부분의 전통적인 상태 관리 라이브러리는 클라이언트 상태를 다루는 데는 훌륭하지만, 비동기나 서버 상태를 다루는 데는 그리.. 뛰어나지 않다. 서버 상태는 다르다.

필요성

예전에는 AJAX (Asynchronous Javascript And Xml)가 많이 사용되었다.

다만, AJAX는 잦은 재접속을 줄여주는 데 (잦은 DOM update을 지양하는 데) 도움이 되었지만

Callback Hell, 캐싱 중복, 데이터 동기화, 전역 상태 관리 부재 등의 단점으로 상태 관리 라이브러리가 등장한다.

Redux, Recoil, Mobx, SWR, Zustand, Tanstack Query 등등이 있는데

  • 데이터 캐싱 및 중복 요청 방지
  • 같은 데이터에 대한 중복 요청을 단일 요청으로 합치거나
  • 백그라운드에서 오래된 데이터를 업데이트하거나
  • 데이터가 얼마나 오래되었는지를 파악하거나
  • 데이터 업데이트를 가능한 빠르게 반영하거나
  • 페이지네이션 및 데이터 지연 로드 같은 성능을 최적화하거나
  • 서버 상태의 메모리, 가비지 수집 관리를 해야 하거나
  • 구조 공유를 사용하여 쿼리 결과를 메모화하거나...
  • 컴포넌트간 변수(State) 공유는 어렵고 특히 내부 로직, UI/UX 등에서 관리할 상태가 많아지면 더 복잡해지고, React의 경우에는 Props Driling 같은 문제가 생길 만큼 관리가 까다롭고 어렵다.

위 문제를 모두 해결해주는 잘 만들어진 바퀴가 바로 Tanstack Query이다.

공식 깃 소개글이다.

React Context API와 역할 나누기

React의 경우 계층적으로 내려가는 컴포넌트의 구조상 해당 prop이 필요없음에도 자식 컴포넌트에게 전해주기 위해 props driling 문제가 나오는데,

React에서 Context는 컴포넌트에게 Props를 사용하지 않고 필요한 데이터를 넘겨줄 수 있게 하는 기능이다.

많이 사용되는 예로는 테마 설정 (다크모드), 언어 설정, 국가 설정 등 어느 페이지에서든 공통적으로 사용하는 요소인데 이를 모든 prop들이 가지는 대신 context로 묶어주자는 개념이다.

Stale time, GC time

Stale Time

기본 0ms. fresh~stale 되기까지의 시간. 시간이 다 지난다고 캐시에서 데이터가 삭제되는 건 아니다. (이건 GC Time) 서버 확인 전 데이터의 허용된 오래된 정도를 나타낸다.

데이터를 refetch할 시기를 결정하는 기준이 stale time이다.

stale-while-revalidate
HTTP 캐싱에도 사용되는 메커니즘으로, 캐싱된 데이터를 사용자에게 제공하면서, 비동기적으로 콘텐츠를 서버에서 revalidate하는 개념이다.

GC Time (aka Cache Time)

기본 5분. 데이터를 캐시에 유지할 시간. 시간이 다 지나면 캐시에서 데이터가 삭제됨. 캐시 데이터를 얼마나 오래 보관하고 싶은지에 대한 것. inactive 상태일 때 GC가 돌아서 캐시에서 데이터가 사라짐.

example

staleTime은 데이터가 새롭게 간주되는 기간을 설정한다. 이 기간 동안은 추가적인 요청에 대해 데이터를 새로 고침하지 않는다. gcTime은 데이터가 캐시에서 유지되는 기간을 설정하여, 이 시간이 지나면 캐시에서 데이터가 제거된다.

import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';

// QueryClient를 생성하고 구성 옵션을 설정
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // staleTime을 5분으로 설정
      // 이 설정은 데이터가 5분 동안 '신선(fresh)'으로 간주되어,
      // 이 기간 동안에는 새로운 요청에 대해 자동 리페치를 하지 않는다.
      staleTime: 5 * 60 * 1000,

      // gcTime을 10분으로 설정
      // 이 설정은 캐시에서 관찰되지 않는 데이터가 10분 후에 자동으로 삭제된다.
      cacheTime: 10 * 60 * 1000
    }
  }
});

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MyComponent />
    </QueryClientProvider>
  );
}

// 데이터 패칭을 수행하는 컴포넌트
function MyComponent() {
  // useQuery를 사용하여 데이터를 요청
  const { isLoading, error, data } = useQuery(['posts'], fetchPosts);

  if (isLoading) return 'Loading...';
  if (error) return `An error occurred: ${error.message}`;

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

// 데이터를 패칭하는 함수 (예: 서버에서 글 목록을 불러옴)
async function fetchPosts() {
  const response = await fetch('/api/posts');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
}

main concepts

npm i @tanstack/react-query

TanStack Query 의 핵심기능으로는 HTTP의 GET요청과 같이 서버 데이터를 가져오는 useQuery 훅이 있고, POST, PUT, DELETE 요청 등 변이(사이드이펙트)를 유발하는 useMutation 훅이 있다.

https://youtu.be/mPaCnwpFvZY?si=d5JISCMj3HCcq6H1

https://youtu.be/H-r_ao646eQ?si=gC7XyT7xTB9NYeNE (<- 여긴 진짜를 알려준다...)

https://youtu.be/YTDopBR-7Ac?si=U9s8fZWBc4s-faSM

사용

설치

$ npm i @tanstack/react-query

쿼리 클라이언트, 프로바이더 설정

TanStack Query v5를 사용하기 위해 QueryClient 인스턴스를 생성하고, 리액트 컴포넌트 트리의 최상위에 QueryClientProvider를 설정한다. 이 구성은 라이브러리가 상태와 캐시를 관리하는 데 사용된다.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <MyComponent />
    </QueryClientProvider>
  );
}

useQuery 호출

useQuery 훅을 사용하여 서버로부터 데이터를 비동기적으로 가져오고, 자동으로 캐싱 및 상태 관리를 할 수 있다. 이 훅은 데이터 로드, 새로고침, 캐시 관리를 간단하게 만든다.

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

function MyComponent() {
  const { isLoading, error, data } = useQuery(['todos'], fetchTodos);

  if (isLoading) return 'Loading...';
  if (error) return `An error occurred: ${error.message}`;

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

DevTools 설치

TanStack Query DevTools를 설치하여 앱의 쿼리 상태와 캐시 데이터를 시각적으로 확인할 수 있다. DevTools에서 확인할 수 있는 상태들은 각 쿼리의 상태를 나타내며, 이 도구는 개발 중에 매우 유용하다.

$ npm i @tanstack/react-query-devtools@4

역시나 NPM이나 Yarn을 이용해서 설치를 해줘야 한다.

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <>
      <QueryClientProvider client={queryClient}>
        <MyComponent />
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryClientProvider>
    </>
  );
}

리액트 컴포넌트 트리 최상단에 ReactQueryDevtools를 애플리케이션의 QueryClientProvider 내부에 포함시킨다.

  • Fresh
    "Fresh" 상태의 쿼리는 최근에 성공적으로 데이터를 가져왔고, 해당 데이터가 아직 "staleTime"으로 설정된 시간 동안 유효한 상태이다.
    이 시간 동안 쿼리는 추가적인 데이터 패칭 없이 캐시된 데이터를 반환한다.

  • Fetching
    "Fetching" 상태는 쿼리가 현재 데이터를 서버로부터 불러오고 있는 중임을 나타낸다. 이는 새로운 데이터 요청이 시작되었거나, 데이터를 자동 리프레시 중임을 의미할 수 있다.

  • Paused
    "Paused" 상태는 쿼리가 일시적으로 비활성화되었음을 나타낸다. 예를 들어, 쿼리가 의존하는 변수가 아직 설정되지 않거나, 페이지가 비활성화 상태일 때 이 상태가 될 수 있다.

  • Stale
    "Stale" 상태의 쿼리는 더 이상 신선(fresh)하지 않다는 것을 의미한다. 즉, "staleTime"이 경과하여 데이터가 만료되었고, 다음에 해당 쿼리를 참조할 때 자동으로 데이터를 리프레시해야 할 필요가 있음을 나타낸다.

  • Inactive
    "Inactive" 상태는 쿼리가 현재 활성화되지 않았음을 나타낸다. 이 상태는 컴포넌트가 언마운트되어 쿼리에 대한 참조가 더 이상 존재하지 않을 때 발생한다. 설정된 "cacheTime"이 경과하면 쿼리 데이터가 캐시에서 제거된다.

profile
game client programmer

0개의 댓글