React-query

김동영·2024년 9월 11일
0

이번 프로젝트에서 react-query를 적용해 보면서 공부한 내용에 대해 적어보려고 합니다.

react-query란?

비동기 데이터를 패칭,캐싱,동기화,업데이트 하는 작업을 쉽게 해주는 라이브러리

react-query의 장점

  • 캐싱리패칭을 통해 최신의상태의 데이터를 참조
  • 고유한 쿼리의 키만 가지고 어디서든 데이터 접근 가능
  • 서버상태의 데이터관리



캐싱이란?

자주 사용하는 데이터나 연산 결과를 임시로 저장해 두고, 필요할 때 빠르게 접근할 수 있도록 하는 기술

  • 캐싱을 통해 동일한 데이터의 반복적인 호출을 줄여 서버의 부하를 낮춤
  • 최신데이터를 사용자에게 보여줄때 단순히 로딩창을 보여주는것이 아닌
    이전상태의 화면을 보여줌으로써 더나은 경험을 제공

그렇다면 언제 캐싱을 통해 데이터를 재사용하고 언제 서버에 요청을 하는것인가??

react-query에서는 stale(오래된)데이터fresh(신선한)데이터로 구분지어 stale한 데이터로 간주되면 필요할때 refetch가 됩니다.

여기서 필요할때란?

  • 쿼리의 새로운 인스턴스가 마운트 될때(refetchOnMount)
  • 창이 refocuse 될때(refetchOnWindowFocus)
  • 네트워크가 다시 연결될때(refetchOnReconnect)

그렇다면 stale데이터와 fresh데이터는 어떻게 구분하는가?

바로 staleTime을 통해 구분합니다.

staleTime이란?

데이터가 fresh라고 간주되는 시간입니다.

staleTime만큼은 앞에서말한 필요할때(위에서 말한refech가 되는 조건)여도 refetch가 일어나지 않습니다(즉 fresh하다는것)

staleTime기본값은 0이기때문에 데이터를 받자마자 stale한 데이터로 간주된다 따라서 따로 설정을 통해 변경해주어야합니다

또한, cacheTime을 통해 데이터가 캐시에 남아 있는 기간을 정의할 수 있습니다.




react-query의 데이터 관리

react-queryContextAPI를 기반으로QueryClientProvider라는 컴포넌트를 통해 전역적으로 Query Client를 제공합니다.

Query Client란?

query(비동기 데이터)를 관리하는 주요 객체입니다. Query Client는 모든 쿼리 상태를 관리하고, 데이터를 가져오고, 캐싱하고, 동기화하며, 데이터를 업데이트하는 등의 작업을 수행합니다.

따라서 react-query는 데이터를 전역상태로 관리하면서 여러 컴포넌트에서 비동기 데이터를 쉽게 공유할 수 있게 설계 되었습니다.

사용 방법

GET 요청일경우에는 useQuery를 사용하고 POST,PUT,DELETE와 같은 요청인 경우에는 useMutation을 사용합니다.

useQuery

useQuery는 쿼리의 unique key값promise 반환하는 함수가 인자로 들어갑니다.

unique key : 애플리케이션 전체에서 쿼리를 다시 가져오고,캐싱하고,공유하는데 내부적으로 사용하는값입니다.

useQuery의 반환값은 API요청에 대한 성공 및 실패 등 다양한 상태와 데이터를 포함하는 객체를 반환합니다.
ex) data,error,isLoading,isError,isSuccess,isFetching,
refetch,status

코드예시

function Todos() {
  const { isPending, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (isPending) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

조건부 실행

또한 useQuery에서 세 번째 인자로enabled에 값을 넣어 특정조건(값이 true가 될때)이 만족할때만 쿼리를 실행시켜 동기적으로 사용 가능합니다.

코드예시

import { useQuery } from 'react-query';

const fetchUser = async (id) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
};

function UserProfile({ userId }) {
  const { data, isLoading } = useQuery(['user', userId], () => fetchUser(userId), {
    enabled: !!userId // userId가 존재할 때만 쿼리를 실행
  });

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>User Profile</h1>
      <p>{data?.name}</p>
    </div>
  );
}

useMutation

  • useQuery와 달리 데이터를 생성/업데이트/삭제하거나 서버 사이드 이펙트를 수행하는 데 사용됩니다.
  • useMutation으로 mutation 객체를 정의하고, mutate메서드를 사용하면 요청 함수를 호출해 요청이 보내집니다.
function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) => {
      return axios.post('/todos', newTodo)
    },
  })

  return (
    <div>
      {mutation.isPending ? (
        'Adding todo...'
      ) : (
        <>
          {mutation.isError ? (
            <div>An error occurred: {mutation.error.message}</div>
          ) : null}

          {mutation.isSuccess ? <div>Todo added!</div> : null}

          <button
            onClick={() => {
              mutation.mutate({ id: new Date(), title: 'Do Laundry' })
            }}
          >
            Create Todo
          </button>
        </>
      )}
    </div>
  )
}

invalidation

  • 쿼리의 데이터가 mutation과 같은 요청으로 인해 서버에서 바뀌었다면 백그라운드에 남은 데이터는 stale상태가 되어 쓸모가 없어지는 상황이 발생할 수 있습니다.
  • invalidateQueries 메소드를 통해 querystale되었다고 처리하여 refetch가 진행됩니다.
  • 쿼리에 해당 키가 공통적으로 들어가있다면 모두 invalidation이 가능합니다.

코드예시

// 캐싱된 쿼리 모두 invalidtate
queryClient.invalidateQueries()
// 키에 todos가 들어간 쿼리 모두 invalidtate
queryClient.invalidateQueries({ queryKey: ['todos'] })



react-query vs SWR

그렇다면 비동기 데이터를 관리해주는 라이브러리인 SWR이 있음에도 react-query를 선택한 이유가 무엇인지 알아보겠습니다.

react-query, SWR

react-query는 서버상태를 받아 캐싱하고, 동기화하고, 업데이트하는 것을 쉽게 해줍니다.

SWR은 먼저 캐시에서 데이터를 반환한 다음, 서버에 데이터를 가져오는 요청을 보내고, 마지막으로 최신 데이터를 제공하는 라이브러리입니다.

문법차이

//react-query
const { data, isLoading, error } = useQuery('myData', fetchMyData, { cacheTime: 10000 });
//SWR
const { data, error } = useSWR('myData', fetchMyData, { dedupingInterval: 5000 });

문법에서는 크게 차이가 없는것을 확인할 수 있습니다.

Devtools

React-Query에서는 공식적으로 react-query/devtools 를 통해 devtool을 지원합니다. 하지만 SWR 또한 devtools를 사용할 수 있지만, 서드 파티 라이브러리를 이용해야합니다.

무한 스크롤 구현

SWRreact-Query 모두 무한 스크롤을 구현하는 데 필요한 기능들을 제공하지만 SWR은 부가적인 코드를 작성해야 합니다. 그에 반해 react-Query에는 getPreviousPageParam, fetchPreviousPage, hasPreviousPage, isFetchingPreviousPage기능을 통해 좀 더 간단하게 구현할 수 있습니다.

Selectors

SWR과 달리 react-Query에서는 select를 통해 원하는 데이터를 추출하여 반환할 수 있습니다.

import { useQuery } from 'react-query'

function User() {
  const { data } = useQuery('user', fetchUser, {
    select: user => user.username,
  })
  return <div>Username: {data}</div>
}

Data Optimization

SWR과 다르게 react-query는 쿼리가 업데이트될 때만 refetch를 진행한다. 또한 여러 컴포넌트에서 동일한 쿼리를 사용하는 경우 한번에 묶어 업데이트합니다.

Garbage Collection

SWR 와 달리 react-Query는 지정된 시간(기본 5분)동안 쿼리가 사용되지 않는다면 자동으로 메모리 해제를 하는 Garbage Collection을 통해 메모리를 관리해줍니다.

느낀점

다음과 같이 react-query를 제대로 사용하게 된다면 SWR보다더 편리하고 좋은 성능으로 사용할 수 있겠다라고 판단되어서 react-query를 사용하게 되었습니다.

profile
안녕하세요 프론트엔드개발자가 되고싶습니다

0개의 댓글