[React Query] useMutation과 useQuery

2024. 12. 24. 10:03·TypeScript
반응형

소개

React Query의 useQuery와 useMutation은 서버 상태 관리를 위한 핵심 훅입니다. 이번 글에서는 두 훅의 상세한 사용법과 실전 예제를 알아보겠습니다.

설치

# npm
npm install @tanstack/react-query

# yarn
yarn add @tanstack/react-query

# pnpm
pnpm add @tanstack/react-query

useQuery 기본 사용법

간단한 데이터 조회

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

// 사용자 조회 훅
function useUser(userId: string) {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    staleTime: 5 * 60 * 1000,  // 5분
    cacheTime: 30 * 60 * 1000, // 30분
  });
}

// 컴포넌트에서 사용
function UserProfile({ userId }: { userId: string }) {
  const { data, isLoading, error } = useUser(userId);

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

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  );
}

의존적 쿼리

function UserPosts({ userId }: { userId: string }) {
  const { data: user } = useUser(userId);

  const { data: posts } = useQuery({
    queryKey: ['posts', userId],
    queryFn: () => fetchUserPosts(userId),
    enabled: !!user, // user 데이터가 있을 때만 실행
  });

  return (
    <div>
      {posts?.map(post => (
        <PostItem key={post.id} post={post} />
      ))}
    </div>
  );
}

useMutation 기본 사용법

데이터 수정

function CreateUserForm() {
  const mutation = useMutation({
    mutationFn: (newUser: User) => {
      return axios.post('/api/users', newUser);
    },
    onSuccess: (data) => {
      console.log('User created:', data);
      // 캐시 무효화 또는 업데이트
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    mutation.mutate({ name: 'John', email: 'john@example.com' });
  };

  return (
    <form onSubmit={handleSubmit}>
      {mutation.isLoading ? (
        'Creating user...'
      ) : (
        <>
          {mutation.isError ? (
            <div>Error: {mutation.error.message}</div>
          ) : null}
          {mutation.isSuccess ? <div>User created!</div> : null}
          <button type="submit">Create User</button>
        </>
      )}
    </form>
  );
}

고급 사용법

낙관적 업데이트

const updateUserMutation = useMutation({
  mutationFn: updateUser,
  onMutate: async (newUserData) => {
    // 진행 중인 쿼리 취소
    await queryClient.cancelQueries(['user', newUserData.id]);

    // 이전 데이터 저장
    const previousUser = queryClient.getQueryData(['user', newUserData.id]);

    // 낙관적 업데이트
    queryClient.setQueryData(['user', newUserData.id], newUserData);

    // 롤백을 위한 컨텍스트 반환
    return { previousUser };
  },
  onError: (err, newUserData, context) => {
    // 에러 발생 시 롤백
    queryClient.setQueryData(
      ['user', newUserData.id],
      context.previousUser
    );
  },
  onSettled: (newUserData) => {
    // 캐시 무효화
    queryClient.invalidateQueries(['user', newUserData.id]);
  },
});

무한 스크롤

function InfinitePostList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: ({ pageParam = 0 }) => fetchPosts(pageParam),
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  });

  return (
    <div>
      {data?.pages.map((page, i) => (
        <React.Fragment key={i}>
          {page.posts.map(post => (
            <PostItem key={post.id} post={post} />
          ))}
        </React.Fragment>
      ))}
      <button
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage || isFetchingNextPage}
      >
        {isFetchingNextPage
          ? 'Loading more...'
          : hasNextPage
          ? 'Load More'
          : 'Nothing more to load'}
      </button>
    </div>
  );
}

실시간 데이터 동기화

function LiveUserData({ userId }: { userId: string }) {
  const { data } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    refetchInterval: 1000, // 1초마다 갱신
  });

  return <div>{data?.name}</div>;
}

성능 최적화

선택적 데이터 변환

const { data } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
  select: (users) => {
    return users
      .filter(user => user.isActive)
      .sort((a, b) => b.lastActiveAt - a.lastActiveAt);
  },
});

병렬 쿼리

function ParallelQueries() {
  const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
  const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });

  return (
    <div>
      {users.data?.length} users
      {posts.data?.length} posts
    </div>
  );
}

에러 처리

function ErrorBoundaryExample() {
  const { data, error, isError } = useQuery({
    queryKey: ['data'],
    queryFn: fetchData,
    retry: 3,
    retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
    onError: (error) => {
      // 에러 로깅
      console.error('Query error:', error);
    },
  });

  if (isError) {
    return <ErrorComponent error={error} />;
  }

  return <div>{data}</div>;
}

마치며

useQuery와 useMutation은 React 애플리케이션에서 서버 상태를 관리하는 강력한 도구입니다. 적절한 설정과 최적화를 통해 효율적인 데이터 관리가 가능합니다.

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

'TypeScript' 카테고리의 다른 글

[React] React.lazy와 Suspense  (2) 2024.12.26
[React] memo를 사용한 컴포넌트 최적화  (0) 2024.12.25
[React] Zustand로 배우는 간단하고 강력한 상태 관리  (0) 2024.12.20
[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱  (0) 2024.12.19
프론트엔드와 백엔드 간 Enum 동기화  (0) 2024.12.07
'TypeScript' 카테고리의 다른 글
  • [React] React.lazy와 Suspense
  • [React] memo를 사용한 컴포넌트 최적화
  • [React] Zustand로 배우는 간단하고 강력한 상태 관리
  • [React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱
코샵
코샵
나의 코딩 일기장
    반응형
  • 코샵
    끄적끄적 코딩 공방
    코샵
    • 분류 전체보기 (725)
      • 스마트팜 (0)
      • 상품 추천 (223)
      • DataBase (0)
        • MongoDB (4)
        • PostgreSQL (0)
      • 하드웨어 (17)
      • 일기장 (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)
  • 인기 글

  • 태그

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

  • hELLO· Designed By정상우.v4.10.3
코샵
[React Query] useMutation과 useQuery
상단으로

티스토리툴바