[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱

2024. 12. 19. 13:04·TypeScript
반응형

소개

React Query는 React 애플리케이션에서 서버 상태를 관리하기 위한 강력한 도구입니다. 이번 글에서는 QueryClient의 핵심 기능과 활용 방법을 자세히 알아보겠습니다.

설치

# npm을 사용하는 경우
npm install @tanstack/react-query

# yarn을 사용하는 경우
yarn add @tanstack/react-query

# pnpm을 사용하는 경우
pnpm add @tanstack/react-query

QueryClient 기본 설정

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

// 기본 QueryClient 생성
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5분
      gcTime : 10 * 60 * 1000, // 10분 (v5는 gcTime, v4는 cacheTime)
      retry: 3,
      refetchOnWindowFocus: true,
    },
  },
});

기본 옵션 설명

옵션 설명 기본값
staleTime 데이터가 신선하다고 간주되는 시간 0
cacheTime 데이터가 캐시에 유지되는 시간 5분
retry 실패 시 재시도 횟수 3
refetchOnWindowFocus 창 포커스 시 재조회 여부 true

 

예시

기본적인 쿼리 설정

// api/userApi.ts
interface User {
  id: number;
  name: string;
  email: string;
}

// 쿼리 키 타입 정의
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: string) => [...userKeys.lists(), { filters }] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: number) => [...userKeys.details(), id] as const,
};

// hooks/useUsers.ts
export const useUsers = (filters?: string) => {
  return useQuery({
    queryKey: userKeys.list(filters),
    queryFn: () => fetchUsers(filters),
    select: (data) => {
      return data.sort((a, b) => b.id - a.id);
    },
  });
};

데이터 수동 관리

// 데이터 직접 설정
queryClient.setQueryData(userKeys.detail(1), {
  id: 1,
  name: "John",
  email: "john@example.com"
});

// 데이터 업데이트
queryClient.setQueryData(userKeys.detail(1), (old: User) => ({
  ...old,
  name: "John Doe"
}));

캐시 무효화

// 특정 쿼리 무효화
const invalidateUser = async (userId: number) => {
  await queryClient.invalidateQueries({
    queryKey: userKeys.detail(userId)
  });
};

// 전체 사용자 쿼리 무효화
const invalidateAllUsers = async () => {
  await queryClient.invalidateQueries({
    queryKey: userKeys.all
  });
};

낙관적 업데이트

const useUpdateUser = () => {
  return useMutation({
    mutationFn: updateUser,
    onMutate: async (newUser) => {
      // 이전 쿼리 취소
      await queryClient.cancelQueries({
        queryKey: userKeys.detail(newUser.id)
      });

      // 이전 데이터 저장
      const previousUser = queryClient.getQueryData(userKeys.detail(newUser.id));

      // 낙관적 업데이트
      queryClient.setQueryData(userKeys.detail(newUser.id), newUser);

      return { previousUser };
    },
    onError: (err, newUser, context) => {
      // 에러 발생 시 롤백
      queryClient.setQueryData(
        userKeys.detail(newUser.id),
        context.previousUser
      );
    },
    onSettled: (newUser) => {
      // 쿼리 무효화
      queryClient.invalidateQueries({
        queryKey: userKeys.detail(newUser.id)
      });
    },
  });
};

페이지네이션 처리

export const usePaginatedUsers = (page: number) => {
  return useInfiniteQuery({
    queryKey: ['users', 'infinite'],
    queryFn: ({ pageParam = 1 }) => fetchUserPage(pageParam),
    getNextPageParam: (lastPage) => lastPage.nextPage,
    getPreviousPageParam: (firstPage) => firstPage.prevPage,
    select: (data) => ({
      pages: data.pages,
      pageParams: data.pageParams,
      totalUsers: data.pages.reduce((acc, page) => acc + page.users.length, 0),
    }),
  });
};

실시간 데이터 동기화

// 웹소켓 연결과 함께 사용
useEffect(() => {
  const ws = new WebSocket('ws://api.example.com');

  ws.onmessage = (event) => {
    const data = JSON.parse(event.data);

    // 실시간 데이터로 캐시 업데이트
    queryClient.setQueryData(userKeys.detail(data.userId), (old: User) => ({
      ...old,
      ...data
    }));
  };

  return () => ws.close();
}, []);

전역 에러 처리

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      onError: (error: Error) => {
        // 전역 에러 처리
        if (error.message === 'Unauthorized') {
          // 로그아웃 처리
          logout();
        }
      },
    },
  },
});

QueryClient 고급 기능

캐시 지속성

import { persistQueryClient } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';

const persister = createSyncStoragePersister({
  storage: window.localStorage,
});

persistQueryClient({
  queryClient,
  persister,
  maxAge: 1000 * 60 * 60 * 24, // 24시간
});

커스텀 캐시 시간

// 동적 캐시 시간 설정
const useCustomQuery = (id: string) => {
  return useQuery({
    queryKey: ['custom', id],
    queryFn: () => fetchData(id),
    staleTime: id === 'important' ? Infinity : 1000 * 60,
    gcTime: id === 'important' ? Infinity : 1000 * 60 * 5,
  });
};

성능 최적화

선택적 데이터 업데이트

// 특정 조건에 맞는 데이터만 업데이트
queryClient.setQueriesData(
  { queryKey: userKeys.all, exact: false },
  (old: User[]) => old?.map(user => 
    user.id === updatedUser.id ? updatedUser : user
  )
);

데이터 프리패칭

// 데이터 미리 로드
const prefetchUser = async (userId: number) => {
  await queryClient.prefetchQuery({
    queryKey: userKeys.detail(userId),
    queryFn: () => fetchUser(userId),
  });
};

마치며

QueryClient는 React Query의 핵심 기능을 제어하는 중요한 도구입니다. 적절한 설정과 활용을 통해 효율적인 상태 관리와 최적화된 사용자 경험을 제공할 수 있습니다.

저작자표시 비영리 변경금지 (새창열림)

'TypeScript' 카테고리의 다른 글

[React Query] useMutation과 useQuery  (0) 2024.12.24
[React] Zustand로 배우는 간단하고 강력한 상태 관리  (0) 2024.12.20
프론트엔드와 백엔드 간 Enum 동기화  (0) 2024.12.07
React 환경 변수 : .env  (1) 2024.12.05
회원가입 폼 쉽게 만들기 : react-hook-form  (1) 2024.11.22
'TypeScript' 카테고리의 다른 글
  • [React Query] useMutation과 useQuery
  • [React] Zustand로 배우는 간단하고 강력한 상태 관리
  • 프론트엔드와 백엔드 간 Enum 동기화
  • React 환경 변수 : .env
코샵
코샵
나의 코딩 일기장
    반응형
  • 코샵
    끄적끄적 코딩 공방
    코샵
    • 분류 전체보기 (727)
      • 스마트팜 (1)
      • 상품 추천 (223)
      • DataBase (0)
        • MongoDB (4)
        • PostgreSQL (0)
      • 하드웨어 (18)
      • 일기장 (4)
      • 파이썬 (130)
        • Basic (41)
        • OpenCV (8)
        • Pandas (15)
        • PyQT (3)
        • SBC(Single Board Computer) (1)
        • 크롤링 (14)
        • Fast API (29)
        • Package (6)
      • Unity (138)
        • Tip (41)
        • Project (1)
        • Design Pattern (8)
        • Firebase (6)
        • Asset (2)
      • Linux (4)
      • C# (97)
        • Algorithm (11)
        • Window (7)
      • TypeScript (51)
        • CSS (10)
      • Git (11)
      • SQL (5)
      • Flutter (10)
        • Tip (1)
      • System (1)
      • BaekJoon (6)
      • Portfolio (2)
      • MacOS (1)
      • 유틸리티 (1)
      • 서비스 (6)
      • 자동화 (3)
      • Hobby (10)
        • 물생활 (10)
        • 식집사 (0)
  • 인기 글

  • 태그

    유니티
    라떼우유
    스크립트 실행
    파이썬
    learntocode
    Python
    리뷰이관
    스크립트 실행 순서
    믈레코비타멸균우유
    상품 리뷰 크롤링
    카페24리뷰
    programmerlife
    카페24리뷰이관
    appdevelopment
    programming101
    쇼핑몰리뷰
    devlife
    스마트스토어리뷰
    rtsp
    ipcamera
    리뷰관리
    긴유통기한우유
    list
    unity
    codingcommunity
    codingtips
    리스트
    C#
    셀레니움
    cv2
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
코샵
[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱
상단으로

티스토리툴바