React Query

psi·2024년 12월 18일

React Query

React Query는 서버 상태 관리를 위한 라이브러리이다.
기존의 Redux, recoil, zustand 등 전역상태관리 도구들이 client 상태관리에 중점을 뒀다면,
React query는 서버데이터를 효율적으로 관리하는데 특화되어 있다.

React Query의 주요 hooks

useQuery

  • 서버의 데이터를 가져오고 , 캐싱 및 상태를 관리하는 훅
    CRUD의 (R, READ)

useMutation

  • 서버 데이터를 생성, 업데이트, 삭제하는 변형 작업을 위한 훅 (CRUD의 CUD)

useInfiniteQuery

  • 무한스크롤 또는 페이지내이션을 위해 사용됨

QueryClient, QueryClientProvider

  • react query의 전역상태를 관리하는 클라이언트 설정

주요 쿼리 옵션

  1. staleTime
    데이터를 신선한(fresh) 상태로 간주하는 시간
    이 시간이 지나기전에는 데이터를 가져오지 않음
    기본값은 0, 데이터를 가져온 직후 stale 상태가 됨
    사용처
  • 자주변하지 않는 데이터 ( 사용자 프로필, 고정 설정값)
  • 서버 호출을 최소화 하는경우
const { data } = useQuery(['user'], fetchUser, {
    staleTime: 1000 * 60 * 5, // 5분 동안 데이터를 신선한 상태로 유지
});
  1. cacheTime
    설명: 캐시된 데이터를 메모리에 유지하는 시간(밀리초 단위).
    이 시간이 지나면 캐시가 삭제됩니다.
    기본값은 5분(300,000ms).
    사용처
  • 자주 사용되지 않지만 일정 시간 동안 캐싱해두고 싶은 데이터(예: 페이지 전환 후 다시 돌아올 때 데이터를 재사용).
const { data } = useQuery(['posts'], fetchPosts, {
    cacheTime: 1000 * 60 * 10, // 10분 동안 캐시 유지
});
  1. refetchOnWindowFocus
    설명: 브라우저 창의 포커스가 변경되었을 때 데이터를 다시 가져올지 여부.
    true(기본값): 창 포커스 시 데이터를 다시 가져옴.
    false: 창 포커스 변경 시 데이터를 가져오지 않음.
    "always": 데이터를 무조건 다시 가져옵니다.
    사용처
  • 실시간으로 업데이트되는 데이터(예: 알림, 대시보드 데이터).
  • 사용자 경험 최적화(다시 브라우저로 돌아왔을 때 최신 데이터 제공).
const { data } = useQuery(['notifications'], fetchNotifications, {
    refetchOnWindowFocus: true, // 기본값, 창 포커스 시 리페치
});
  1. refetchInterval
    설명: 특정 간격마다 데이터를 다시 가져오는 옵션(밀리초 단위).
    실시간 데이터를 가져올 때 유용.
    0 또는 false: 리페치 비활성화(기본값).
    사용처
  • 실시간 데이터: 주식 차트, 코인 가격,
  • 데이터 변동이 많은 경우, 데이터를 자동으로 갱신.
const { data } = useQuery(['cryptoPrice'], fetchCryptoPrice, {
    refetchInterval: 1000 * 5, // 5초마다 리페치
});

주요 장점

  1. 서버 데이터 캐싱
  2. 백그라운드에서 자동 데이터 갱신
  3. 데이터 동기화 및 업데이트 관리
  4. 페이지네이션 무한 스크롤 지원
  5. 에러 핸들링 간소화
// todo에서의 사용 예
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// 1. Todo 데이터를 가져오는 함수
const fetchTodos = async () => {
    const res = await fetch('/api/todos');
    if (!res.ok) throw new Error('Failed to fetch todos');
    return res.json();
};

// 2. Todo를 추가하는 함수
const addTodo = async (newTodo) => {
    const res = await fetch('/api/todos', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newTodo),
    });
    if (!res.ok) throw new Error('Failed to add todo');
    return res.json();
};

function TodoList() {
    // QueryClient를 가져옴 (캐시 관리용).
    const queryClient = useQueryClient();

    // 3. Todo 리스트를 가져오기 위한 useQuery
    const { data, isLoading, isError, error } = useQuery('todos', fetchTodos);

    // 4. Todo를 추가하기 위한 useMutation
    const { mutate } = useMutation(addTodo, {
        onSuccess: () => {
            // 성공 시 todos 데이터를 무효화하여 새 데이터를 가져옵니다.
            queryClient.invalidateQueries('todos');
        },
    });

    // 5. 버튼 클릭 이벤트로 새로운 Todo 추가
    const handleAddTodo = () => {
        const newTodo = {
            title: `New Todo ${Date.now()}`,
            completed: false,
        };
        mutate(newTodo); // 새로운 Todo 추가
    };

    if (isLoading) return '로딩중...';
    if (isError) return `에러가 발생했습니다: ${error.message}`;

    return (
        <div>
            <h1>Todo List</h1>

            {/* 6. 버튼으로 새로운 Todo 추가 */}
            <button onClick={handleAddTodo}>Add New Todo</button>

            {/* 7. Todo 리스트 출력 */}
            <ul>
                {data.map((todo) => (
                    <li key={todo.id}>{todo.title}</li>
                ))}
            </ul>
        </div>
    );
}

export default TodoList;

에러 핸들링 간소화

  • React Query는 API 요청의 상태(isLoading, isError, isSuccess, status)를 자동으로 제공하므로, 상태 관리를 별도로 구현하지 않아도 됨.

  • Retry 메커니즘 제공
    React Query는 기본적으로 요청 실패 시 최대 3회 재시도를 수행하며, 이는 retry 옵션으로 간단히 조정 가능합니다.

const { isError, error } = useQuery(['data'], fetchData, {
    retry: 2, // 실패 시 최대 2번 재시도
});

if (isError) {
    console.error('Error occurred:', error.message);
}

실제 활용 방법

1. 실시간 데이터 동기화

function RealTimeData() {
  const { data } = useQuery('realtime-data', fetchData, {
    refetchInterval: 1000, // 1초마다 자동 갱신
  });
}

2. 낙관적 업데이트

낙관적 업데이트란, api 요청이 성공할거라고 낙관적으로 예상하고 ui를 먼저 업데이트하는 방식
인스타 좋아요의 경우 사용되며, 실패 시 이전 데이터를 통해 다시 복원할 수 있다.

const queryClient = useQueryClient();

const { mutate } = useMutation(updateTodo, {
  onMutate: async (newTodo) => {
    // 이전 쿼리 데이터 백업
    const previousTodos = queryClient.getQueryData('todos');

    // 낙관적으로 UI 업데이트
    queryClient.setQueryData('todos', old => {
      return old.map(todo => 
        todo.id === newTodo.id ? newTodo : todo
      );
    });

    return { previousTodos };
  },
  onError: (err, newTodo, context) => {
    // 에러시 롤백
    queryClient.setQueryData('todos', context.previousTodos);
  }
});

참고 출처
우아콘 react-query & zustand
낙관적 업데이트 관련

profile
사용자 경험을 최우선하며 논리적 문제 해결을 즐기는 개발자

0개의 댓글