react-query

Yun·2024년 4월 9일
1

개인공부

목록 보기
13/28

react-query

react-query는 리액트 애플리케이션에서 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 보다 쉽게 다룰 수 있도록 도와주는 라이브러리이다.

클라이언트 상태와 서버 상태를 명확히 구분하기 위해서 만들어졌다.

기존 상태 관리 라이브러리인 redux, mobX는 클라이언트 상태 작업에는 적합하지만, 비동기 또는 서버 상태 작업에는 좋지 않다고 언급한다.

react-queryredux 모두 훌륭한 상태 관리 도구이지만, 사용되는 목적이 다르다.

  • react-query는 서버 상태 및 캐싱 관리에 중점을 두어 주로 데이터 페칭 및 캐싱에 사용되지만, redux는 클라이언트 측 애플리케이션 상태 관리에 중점을 두어 복잡한 응용 프로그램 상태를 처리할 수 있는 보다 일반적인 상태 관리 라이브러리이다.
  • 서버 데이터 측면에서 redux의 단점을 보완하기 위해 여러 기업에서 redux -> react query로 전환하기도 했다. (ex. kakao)
  • 다만 react-query로 서버 데이터를 관리할 때는, 이외 클라이언트 데이터를 관리하기 위한 별도의 상태 관리 라이브러리를 사용하는 것이 좋다. (주로 recoil 사용)

Server Data

클라이언트 상태와 서버 상태는 완전히 다른 개념이다. 클라이언트 상태는 각각의 input값을, 서버 상태는 데이터베이스에 저장된 데이터를 예시로 들 수 있다.

Redux, Recoil, mobX 같은 전역 상태 라이브러리는 클라이언트 데이터를 효과적으로 관리할 수 있다.

다만, 비동기 함수를 처리하는데 추가적인 로직이 필요하거나 서드 파티 라이브러리를 사용해야 하는 것이 많다. 클라이언트 데이터를 관리하는데 로직이 집중되어 있기 때문에 서버 데이터까지 효율적으로 관리하기 어렵기 때문이다.

Redux toolkit은 기존 redux의 복잡한 로직을 단순화하고, 비동기적인 처리를 훨씬 쉽게 가능하게 한다. react-queryredux toolkit 각각의 장단점이 있다!
비교글: RTK Query VS React-Query 👊🏻


Functions

  • 캐싱
  • 동일한 데이터에 대한 중복 요청을 단일 요청으로 통합
  • 백그라운드에서 오래된 데이터 업데이트
  • 데이터가 얼마나 오래되었는지 확인
  • 데이터 업데이트를 가능한 빠르게 반영
  • 페이지네이션 및 데이터 지연 로드와 같은 성능 최적화
  • 서버 상태의 메모리 및 가비지 수집 관리
  • 구조 공유를 사용해 쿼리 결과를 메모화

Caching

캐싱이란, 특정 데이터의 복사본을 저장하여 이후 동일한 데이터의 재접근 속도를 높이는 것을 말한다.

react-query는 캐싱을 통해 동일한 데이터에 대한 반복적인 비동기 데이터 호출을 방지하고, 불필요한 API 콜을 줄여 서버 부하를 줄인다.

데이터 갱신

react-query에서 제공하는 기본적인 옵션은 다음과 같다.

//기본적인 사용 예시
const {
  data,
  // ...
} = useQuery({
  queryKey: ["super-heroes"], 
  queryFn: getAllSuperHero,
  gcTime: 5 * 60 * 1000,
  staleTime: 1 * 60 * 1000,
  ...
});
  • queryKey: 고유한 배열로서, 쿼리 캐싱을 관리한다.
  • queryFn: 실제 호출하고자 하는 비동기 함수가 들어간다.
refetchOnWindowFocus, //default: true
refetchOnMount, //default: true
refetchOnReconnect, //default: true
staleTime, //default: 0
gcTime, //default: 5분 (60 * 5 * 1000)
  • refetchOnWindowFocus: 윈도우에 포커스된 경우
  • refetchOnMount: 새로운 컴포넌트 마운트가 발생한 경우
  • refetchOnReconnect: 네트워크에 재연결된 경우
  • staleTime
    • 데이터가 fresh에서 stale 상태로 변경되는데 걸리는 시간이다. 이 시간 동안에는 트리거를 충족해도 refresh되지 않는다.
    • 기본값은 0으로, 트리거가 발생했을 때 무조건 refresh된다.
  • gcTime
    • 데이터가 캐싱된 상태로 남아있는 시간이다. 특정 컴포넌트가 화면에서 사라지면(unmount되면) gcTime만큼 데이터가 유지되고, 이후 메모리에서 해제된다.
    • 만일 gcTime이 지나지 않았는데 해당 데이터를 사용하는 컴포넌트가 다시 마운트되면 새로운 데이터를 fetch해오는 동안 임시로 보관한 데이터를 보여준다.

Parallel

const { data: superHeroes } = useQuery({
  queryKey: ["super-heroes"],
  queryFn: getAllSuperHero,
});
const { data: superHeroes } = useQuery({
  queryKey: ["friends"],
  queryFn: getFriends,
});

일반적인 상황에서 쿼리 함수들은 병렬로 요청되어 처리되어, 쿼리 처리의 동시성을 극대화한다.

const queryResults = useQueries({
  queries: [
    {
      queryKey: ["super-hero", 1],
      queryFn: () => getSuperHero(1),
      staleTime: Infinity, // 다음과 같이 option 추가 가능!
    },
    {
      queryKey: ["super-hero", 2],
      queryFn: () => getSuperHero(2),
      staleTime: 0,
    },
    ...
  ],
});

쿼리 여러 개를 수행해야 하고, 렌더링을 거듭되는 사이에 계속 쿼리가 수행되어야 한다면 useQueries를 사용한다.

쿼리가 입력된 순서대로 반환되어 최종적으로 모든 쿼리 결과가 포함된 배열을 반환하게 된다.


Prefetching

비동기 요청은 데이터양이 클수록 받아오는 속도가 느리고, 시간이 오래 걸린다. 사용자 경험을 위해 데이터를 미리 받아와서 캐싱해 놓으면 새로운 데이터를 받기 전에 사용자가 캐싱 된 데이터를 볼 수 있어 UX에 좋은 영향을 줄 수 있다.

예를 들어 페이지네이션을 구현했다고 가정하면, 페이지1에서 페이지2로 이동했을 때 페이지3의 데이터를 미리 받아놓을 수 있다.

const prefetchNextPosts = async (nextPage: number) => {
  const queryClient = useQueryClient();
  await queryClient.prefetchQuery({
    queryKey: ["posts", nextPage],
    queryFn: () => fetchPosts(nextPage),
    // ...options
  });
};

useEffect(() => {
  const nextPage = currentPage + 1;

  if (nextPage < maxPage) {
    prefetchNextPosts(nextPage);
  }
}, [currentPage]);

데이터가 이미 캐싱되어 있으면 데이터를 가져오지 않는다.


useMutation

R(read)에서는 useQuery를, CUD(create, update, delete)는 useMutation을 사용한다.

const mutation = useMutation({
  mutationFn: createTodo,
  onMutate() {
    /* ... */
  },
  onSuccess(data) {
    console.log(data);
  },
  onError(err) {
    console.log(err);
  },
  onSettled() {
    /* ... */
  },
});

const onCreateTodo = (e) => {
  e.preventDefault();
  mutate({ title });
};
  • onMutate는 mutation 함수가 실행되기 전에 실행된다.
  • onSuccessonError는 함수가 성공, 실패했을 때 실행된다.
  • onSettled는 finally 처럼 요청의 성공여부와 상관없이 마지막에 실행된다.

출처

0개의 댓글