본문 바로가기
개발/React, NextJs

React Query에 대해 알아보자

by 쓱싹디벨로퍼 2024. 6. 5.

React Query란?

React Query는 데이터 Fetching, Caching, 동기화, 서버 데이터 업데이트 등을 쉽게 만들어주는 라이브러리이다.

 

React Query의 장점

  • MobX나 Redux를 사용하다 보니 보일러플레이트 형태의 코드가 많이 발생하는데, React Qeury를 비교적 코드의 양이 적고 구조가 단순하여 추후 유지 보수가 용이하다.
  • *보일러 플레이트(BoilerPate)란? 최소한의 변경으로 여러 곳에서 재사용되지만, 반복적인 코드로 인해 많은 양의 코드를 양산하는 것.
  • Caching을 통한 어플리케이션 속도 향상
  • 동일 데이터에 대한 중복 요청 제거
  • 오래된 데이터의 상태를 파악하여 업데이트
  • Garbage Collection(GC)를 이용하여 서버 쪽 데이터 메모리 관리
  • React Hooks와 유사한 인터페이스 제공
  • 비동기 과정을 선언적으로 관리할 수 있음

 

React Query 설치 및 세팅

import { QueryClient, QueryClientProvider } from 'react-query'
    const queryClient = new QueryClient()
    function App() {
      return
        <QueryClientProvider client={queryClient}>
        ... //root 컴포넌트
        </QueryClientProvider>
    }
  1. QueryClient 인스턴스를 생성한다.
  2. 컴포넌트가 QueryClient 인스턴스에 접근할 수 있도록 root가 되는 컴포넌트를 QueryClientProvider로 감싸준다.
  3. client prop으로 Query client를 넘겨준다.

 

React Query에서 자주 사용하는 Hook

useQuery()

  • GET 요청과 같이 서버로부터 데이터 조회 시 사용됨.
  • const {data, error, isError, isSuccess} = useQuery(queryKey, queryFn)
  • queryKey란? 문자열로 사용되기도 하고 배열의 형태로 사용될 수도 있지만, React Query v4부터 모든 queryKey는 배열로 선언되어야 한다.
    // String => 자동으로 길이가 1인 배열로 인식
    const { data } = useQuery('users', queryFn);
    
    // Array1
    const { data } = useQuery(['users'], queryFn);
    
    // Array2
    const { data } = useQuery(['users1', 'users2'], queryFn);
    
  • 이 queryKey를 통해 다른 곳에서도 해당 쿼리의 결과를 꺼내오는것이 가능하다.
  • useQuery마다 부여되는 고유한 Key값이다.
  • React-query는 queryKey로 캐싱을 관리한다.
    1. data1과 data2의 queryFn 서로 다르나, 동일한 queryKey로 응답을 요청하고 있다. 이럴 경우 서버에 한 개의 요청만 전달된다.
    2. 따라서 해당 코드는 queryFn에 상관없이 data1에서 요청했던 결과를 그대로 data2에 저장하는 꼴이 된다.
    3. 즉, data1에서 서버 응답을 요청하게 되면 data2에는 이미 동일한 queryKey에 대한 결과값이 있기 때문에, data2는 추가적으로 응답을 요청하지 않고 data1의 결과를 그대로 사용하게 된다.
  • const { data: data1 } = useQuery(['users1'], queryFn1); const { data: data2 } = useQuery(['users1'], queryFn2);
  • queryFn이란?
    import { useQuery } from 'react-query'
    
    //queryFn
    const fetchUserList = () => {
      return axios.get("<https://jsonplaceholder.typicode.com/users>");
    }
    
    function App() {
     const { data } = useQuery(['users'], () => fetchUserList);
    }
    
  • Promise 처리가 이루어지는 함수로써, axios와 같이 서버에 API를 요청하는 코드이다.
  • useQuery()는 비동기로 작동한다.이와 같이 다수의 useQuery가 존재할 경우 Query Options에서 “enabled”를 사용하면 useQuery를 동기적으로 사용할 수 있다. 위 경우에는, usersQuery → todosQuery → postsQuery 순으로 실행된다.
  • //1번째 실행 const usersQuery = useQuery('users', () => fetchUsers); //2번째 실행 const todosQuery = useQuery('todos', () => fetchTodos, { enabled: !!usersQuery //usersQeury가 있을 경우에만 실행. }); //3번째 실 const postsQuery = useQuery('posts', () => fetchPosts, { enabled: !!todosQuery });
  • 한 컴포넌트에 다수의 useQuery가 존재 한다면, 하나의 useQeury가 실행된 후 다음 useQuery가 실행되는 것이 아니라, 다수의 useQuery가 동시에 실행된다.

 

자주 사용되는 Options

  • retry(boolean | number | (failureCount:number, error:TError) ⇒ boolean) : default 3회
    실패한 쿼리를 재시도하는 옵션이다.
    • boolean - true : 쿼리 실패 시 무한으로 재시도
    • boolean - false : 쿼리 실패 시 재시도하지 않음
    • number - 횟수 : 해당 횟수만큼 재시도

 

  • staleTime (number | Infinity) : default 0
    stale의 사전적 의미는 “신선하지 않은”이라는 뜻을 가지고 있다. React Query는 기본적으로 캐싱된 데이터를 stale 한 상태로 여긴다. 
    staleTime이란 데이터가 fresh → stale 상태로 변경되는데 걸리는 시간. 즉, fresh 상태로 유지되는 시간이다. 자주 변경되는 않는 데이터라면 staleTime 시간을 늘리는 것도 좋은 방법이다. 데이터가 한번 fetch 되고 나서 staleTime이 지나지 않았다면(fresh 한 상태) unmount 후 mount 되어도 fetch가 일어나지 않는다.

 

  • cacheTime(number | Infinity) : default 5분
    데이터가 inactive 상태일 때 캐싱된 상태로 남아있는 시간이다.cacheTime이 지나면 캐시 데이터는 Garbage Collector(GC)로 수집된다. 쿼리 인스턴스가 unmount 되면 데이터는 inactive 상태로 변경되며, 캐시는 cacheTime 만큼 유지된다. cacheTime이 지나기 전에 쿼리 인스턴스가 다시 mount 되면, 데이터를 fetch 하는 동안 캐시 데이터를 보여준다. cacheTime이 지나기 전 인스턴스가 다시 mount 되면, 데이터를 fetch 하는 동안 캐시 데이터를 보여준다.

 

  • refetchOnMount (boolean | “always”) : default true
    데이터가 stale 상태일 경우 mount시 마다 refetch 여부를 설정하는 옵션.

 

  • refetchOnWindowFocus (boolean | “always”) : default true
    데이터가 stale 상태일 경우 윈도우 포커싱 될 때마다 refetch 여부를 설정하는 옵션.

 

  • onSuccess ((data) ⇒ void)
    쿼리 성공 시 실행되는 함수로, 매개변수 data 성공 시 서버에서 넘어오는 response 값.

  • onError((errorr) ⇒ void)
    쿼리 실패 시 실행되는 함수로, 매개변수 data는 성공 시 서버에서 response 값.

  • initialData(TData | () ⇒ TData)
    initialData를 설정하면 쿼리가 아직 생성되지 않았거나 캐시 되지 않았을 때 설정한 initialData가 쿼리 캐시의 초기 데이터로 사용된다.
const userDetail = useQuery<any>({
  queryKey: ['userdetail', userId],
  queryFn: () =>
    axios.get(`/users/${userId}`).then((response) => response.data),
  initialData: () => {
    const users = queryClient
      .getQueryData<User[]>(['userList'])
      ?.find((user) => user.id === userId)
  },
})

 

 

 

useMutation

데이터 조회(GET) 외 POST, PUT, DELETE와 같이 데이터 변경 및 삭제 작업을 할 때 사용한다.

데이터를 저장하지 않으므로 queryKey는 필요하지 않다.

import { useMutation } from 'react-query';
const Todo = () => {
  const updatePost = (postId: number) => {
    return axios.put(                      https://jsonplaceholder.typicode.com/postId/${postId},
      ).then((res) => res && res.data);
  }

const updateMutation = useMutation( (postId: number) => updatePost(postId));
  return (
    <div>
      <Button onClick={() => updateMutation.mutate(post.id)}>
        update
      </Button>
    </div>
  );
}

 

 

Query Invalidation

useMutation을 사용해서 데이터를 create, upddate, delete 후 변경된 데이터를 확인하고 싶으면 데이터를 새로 fetch 해야 한다.

이런 상황을 간단하게 해결할 수 있는 방법으로 invalidateQueries를 사용할 수 있다.

useQuery가 가지고 있던 queryKey의 유효성을 제거(무효화)해서 캐싱되어 있던 데이터를 보여주지 않고 새로운 데이터를 받아올 수 있게 한다.

import { useMutation, useQueryClient } from 'react-query';
  const queryClient = useQueryClient();
  const updateMutation = useMutation( (postId: number) => updatePost(postId), {
      onSuccess: async () => {  //요청이 성공한 경우
          await queryClient.invalidateQueries();  //캐시가 있는 모든 쿼리 무효화
          await queryClient.invalidateQueries('posts'); //queryKey가 'posts'로 시작하는 모든 쿼리 무효화
      }
  });

 

 

React Query Devtools

React Query는 강력한 내장 개발 도구인 React Query Devtools과 함께 제공된다.

이 React Query Devtools는 사용 중인 모든 쿼리의 상태를 시각화하며, 이 쿼리가 예상대로 작동하지 않는 경우 문제를 해결하는 데 도움이 될 수도 있다.

개발 도구를 실행하기 위해서는, 앞서 설명한 QueryClientProvider로 ReactQueryDevtools를 감싸줘야 한다.

import { QueryClient, QueryClientProvider } from 'react-query'
  const queryClient = new QueryClient()
  function App() {
    return
      <QueryClientProvider client={queryClient}>
      ... //root 컴포넌트
      //initialIsOpen : open된 채로 시작
      //position : devtools를 열 수 있는 logo 위치 - 우측 하단으로 지정
      <ReactQueryDevtools initialIsOpen={false} position='bottom-right' />
      </QueryClientProvider>
}

 

위와 같이 적용 후, 실행해 보면 우측 하단에 아래와 같은 꽃 모양(React Query 로고) 버튼이 생긴다.

 

로고 버튼을 클릭해 보면, 아래와 같은 개발 도구가 열리게 된다.

 

React Query Devtools는

  • queryKey로 쿼리를 표시해 준다.
  • fresh, fetching, stale, inactive와 같이 쿼리 상태를 보여준다.
  • 해당 API를 요청하는 observer 수 조회가 가능하다.
  • 쿼리가 마지막으로 업데이트된 시간을 볼 수 있다.
  • refetch, invalidate, reset, remove드의 actions를 GUI를 통해 쉽게 적용할 수 있다.
  • Data Explorer 탭에서는 Chrome devtools의 Network 탭에서 존재하는 정보도 확인 가능하다.

 

 

 

출처 : https://www.nextree.io/react-query/

 

728x90