Use Cases

TanStack Query: Claude Code 활용 가이드

tanstack query: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.

TanStack Queryで서버状態관리を최적화する

TanStack Query(旧React Query)は서버状態の취득・캐시・동기を宣言的に관리する라이브러리です。Claude Code를 활용하면 複雑な캐시戦略や楽観的업데이트も빠르게구현할 수 있습니다。

기본적인쿼리설계

Claude Code에데이터취득パターンを依頼합시다。

> TanStack Queryで사용자목록の쿼리를 생성해줘。
> 페이지네이션、검색フィルタ、自動リフレッシュ대응で。
import { useQuery, keepPreviousData } from "@tanstack/react-query";

// 쿼리キーファクトリー
export const userKeys = {
  all: ["users"] as const,
  lists: () => [...userKeys.all, "list"] as const,
  list: (filters: UserFilters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, "detail"] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
};

interface UserFilters {
  page: number;
  perPage: number;
  search?: string;
  role?: string;
}

export function useUsers(filters: UserFilters) {
  return useQuery({
    queryKey: userKeys.list(filters),
    queryFn: () => fetchUsers(filters),
    placeholderData: keepPreviousData,
    staleTime: 5 * 60 * 1000, // 5分間は캐시を使用
    refetchOnWindowFocus: true,
  });
}

export function useUser(id: string) {
  return useQuery({
    queryKey: userKeys.detail(id),
    queryFn: () => fetchUser(id),
    enabled: !!id,
    staleTime: 10 * 60 * 1000,
  });
}

楽観的업데이트の구현

ミューテーションと楽観的업데이트のパターンをClaude Code에생성してもらいます。

import { useMutation, useQueryClient } from "@tanstack/react-query";

export function useUpdateUser() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: { id: string; updates: Partial<User> }) =>
      updateUser(data.id, data.updates),

    onMutate: async ({ id, updates }) => {
      // 進行中の쿼리を취소
      await queryClient.cancelQueries({ queryKey: userKeys.detail(id) });

      // 現在の데이터を백업
      const previousUser = queryClient.getQueryData(userKeys.detail(id));

      // 캐시を楽観的に업데이트
      queryClient.setQueryData(userKeys.detail(id), (old: User) => ({
        ...old,
        ...updates,
      }));

      return { previousUser };
    },

    onError: (_err, { id }, context) => {
      // 에러時に롤백
      if (context?.previousUser) {
        queryClient.setQueryData(userKeys.detail(id), context.previousUser);
      }
    },

    onSettled: (_data, _error, { id }) => {
      // 성공・실패に関わらず再취득
      queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
      queryClient.invalidateQueries({ queryKey: userKeys.lists() });
    },
  });
}

無限스크롤の구현

useInfiniteQuery를 사용한無限스크롤もClaude Code로빠르게구현할 수 있습니다。

import { useInfiniteQuery } from "@tanstack/react-query";

export function useInfiniteUsers(search?: string) {
  return useInfiniteQuery({
    queryKey: ["users", "infinite", { search }],
    queryFn: ({ pageParam }) =>
      fetchUsers({ page: pageParam, perPage: 20, search }),
    initialPageParam: 1,
    getNextPageParam: (lastPage) =>
      lastPage.hasNext ? lastPage.page + 1 : undefined,
    getPreviousPageParam: (firstPage) =>
      firstPage.hasPrev ? firstPage.page - 1 : undefined,
  });
}

function UserInfiniteList() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteUsers();

  const users = data?.pages.flatMap((page) => page.users) ?? [];

  return (
    <div>
      {users.map((user) => (
        <UserCard key={user.id} user={user} />
      ))}
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? "Loading..." : "もっと見る"}
        </button>
      )}
    </div>
  );
}

프리페치とSSR대응

Next.jsなどでの서버サイド프리페치も구현할 수 있습니다。

import { QueryClient, dehydrate, HydrationBoundary } from "@tanstack/react-query";

export async function getServerSideProps() {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({
    queryKey: userKeys.list({ page: 1, perPage: 20 }),
    queryFn: () => fetchUsers({ page: 1, perPage: 20 }),
  });

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  };
}

export default function UsersPage({
  dehydratedState,
}: {
  dehydratedState: unknown;
}) {
  return (
    <HydrationBoundary state={dehydratedState}>
      <UserList />
    </HydrationBoundary>
  );
}

カスタム쿼리フックのパターン

実務では再利用可能なカスタムフック로서설계します。

function useOptimisticDelete<T extends { id: string }>(
  queryKey: readonly unknown[],
  deleteFn: (id: string) => Promise<void>
) {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: deleteFn,
    onMutate: async (id) => {
      await queryClient.cancelQueries({ queryKey });
      const previous = queryClient.getQueryData(queryKey);

      queryClient.setQueryData(queryKey, (old: T[] | undefined) =>
        old?.filter((item) => item.id !== id)
      );

      return { previous };
    },
    onError: (_err, _id, context) => {
      queryClient.setQueryData(queryKey, context?.previous);
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey });
    },
  });
}

정리

TanStack Queryは서버状態관리の複雑さを大幅に軽減してくれます。Claude Codeを활용すれば、쿼리キー설계、楽観的업데이트、無限스크롤などの複雑なパターンも短시간で구현가능합니다。

状態관리の全体설계はZustand状態관리가이드を、APIの타입安全性はtRPC타입安全API개발를 참고하세요.TanStack Query공식 문서도 확인해 두세요.

#Claude Code #TanStack Query #React #データ取得 #caching