React

[React-Query] 2. 리액트쿼리 특징과 useQuery 사용하기

hyriver(강화영) 2023. 6. 1. 11:12

React-Query

리액트쿼리는 데이터 패칭, 캐싱, 동기화, 서버쪽 데이터 업데이트 등을 쉽게 만들어주는 리액트 라이브러리이다.

처음에는 이 리액트쿼리가 서버를 연결시켜주는 역할인줄 알았으나 리액트 쿼리의 옵션 등을 사용하여 좀 더 편리하게 데이터 관리를 할 수 있는, 말그대로 옵션이었다.

 

특징

1. queryKey를 사용하여 데이터가 캐싱되고, useEffect로 데이터 상태 관리 및 패칭하던 것을 좀 더 간단하게 구현 가능(페이지네이션, 검색 기능 등). 이것은 sideEffect를 줄이고 코드를 간결하게 작성하도록 한다.

 

2. 다양한 옵션을 사용하여 복잡하게 코드를 작성해야 하는 것을 간단하게 구현 가능(동기적으로 패칭 및 자동 리패칭 등)

 

3. isLoading, isFetching, isSuccess, isError등과 같은 여러 상태 처리 가능

 

사용법

1. 먼저 최상단인 app 컴포넌트에서 쿼리 인스턴스를 생성하고 QueryClientProvider로 감싸줘야한다.

import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();	//쿼리 인스턴스 생성

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  );
}

 

2. 리액트 쿼리에서는 서버에 연결할 때 때 두가지 방법을 사용한다.

1) useQuery : 데이터를 조회할 때(crud의 read)

2) useMutate : 서버에 데이터 변경 작업을 요청할 때 사용(crud의 insert, update, delete)

 

먼저 useQuery에 대해 알아보자

 

 

useQuery

  • 서버로부터 데이터 조회 시 사용(get)하며 crud 중 reading 에만 해당한다.
  • useQuery는 비동기로 작동한다. 즉, 한 컴포넌트에 여러개의 useQuery가 있다면 하나가 끝나고 다음 useQuery가 실행되는 것이 아닌 두 개의 useQuery가 동시에 실행된다.
  • 여러개의 비동기 query를 사용해도 되지만 useQuery보다는 useQueries 사용을 권한다.

 

const { data, isLoading, error } = useQuery(queryKey, queryFn, options)

                           반환값                                                                매개변수

useQuery는 위와 같이 사용할 수 있으며, 사용할 반환값을 작성하고 3개의 매개변수를 가진다.

 

매개변수

1) queryKey : useQuery마다 부여되는 고유 Key 값

- 하나의 string 또는 배열로 지정 가능 : 하나의 key만 넣을 때는 string으로 그냥 넣어도 되고(내부적으로 자동으로 배열 안에 넣게 바꿈), 둘 이상부터는 배열을 선언하여 안에 넣어야 한다.

- 데이터 캐싱 관리 : 동일한 queryKey를 사용하여 서버의 데이터를 추가 조회하는 경우 추가로 요청하는 것이 아니라 이미 동일한 queryKey에 대한 결과값이 있기 때문에 해당 값을 그대로 가져와 사용 → 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능하며, queryKey가 같으면 캐싱 적용하고 queryKey가 다르면 서버에서 데이터 다시 fetch한다. -> 이것을 이용해서 검색/페이지네이션을 간편하게 구현할 수 있다.

const { data: courseData, error: courseError } = useQuery(['courseGet', page], () =>
    GET(`/course/***`, {
      params: { page, elementCnt: limit }
    })
  );

→ queryKey에 배열로 page를 추가하여 page가 변경되면 쿼리키가 변경되므로 데이터가 리패치된다(=페이지네이션에서 현재페이지를 page로 설정하고 이 현재페이지가 변경되면 데이터가 리패치된다). 이렇게 하면 useEffect를 사용하여 page 변경될 때 마다 렌더링되도록 하는 작업을 따로 만들지 않아도 된다.

 

2) queryFn : api 호출 함수

query Function으로 promise 처리가 이루어지는 비동기 함수가 들어간다(promise 를 반환하는 함수) → 데이터 resolve하거나 error를 보내고, 보통 fetch나 axios를 사용하여 서버 연결 요청을 보내는(서버에 api 요청하는) 함수를 인자로 받는다.

 

3) option : 데이터 패치 시 좀 더 간편하게 다양한 기능들을 사용할 수 있으며 선택사항이다.

- enabled : 원하는 시점에 실행되도록(비동기를 동기로 처리할 때)

- retry: query 동작 실패 시, 자동으로 retry 할지 결정하는 옵션

- keepPreviousData : 새롭게 fetching 시 이전 데이터 유지 여부(탭 이동시 또는 페이지네이션에서 깜빡거리는 현상 제거)

- refetchInterval : 주기적으로 refetch 할지 결정하는 옵션

refetchOnWindowFocus : 윈도우 포커스 시 자동 리패치 조절

// 1
const res = useQuery(['persons'], () => axios.get('<http://localhost:8080/persons>'));

// 2
const res = useQuery({
    queryKey: ['persons'],
    queryFn: () => axios.get('<http://localhost:8080/persons>')
});

즉, 첫번째 매개변수에는 캐시를 관리할 키값이 들어가고, 두번째 매개변수는 실질적으로 어디에 api를 패칭해서 가져올건지를 정의하는 Promise 함수가 들어간다.

 

반환값

- data : 마지막으로 성공한 resolve된 데이터(response)

- error : 에러가 발생했을 때 반환되는 객체

- isFetching : 요청이 in-flight 중일 때 true

- status, isLoading, isSuccess, isLoading 등 : 모두 현재 query의 상태

- refetch : 해당 query refetch하는 함수 제공

- remove : 해당 query cache에서 지우는 함수 제공

 

 

 

예시

//course.ts - api연결하는 코드는 이 파일에 모두 작성하고 데이터가 필요한 컴포넌트에서 해당 데이터를 불러온다
//차시+차시진도 데이터 조회 ✅
//courseModuleSeq, courseUserSeq, chasiName이라는 세개의 매개변수를 받는 chasiProgressApi 함수
//? : optional properties - 이 매개변수에 값을 할당하지 않아도 작업이 실행되며, chasiName에 값이 할당될 경우 chasiName의 값이 포함된 데이터만 가져온다(=검색기능)
export const chasiProgressApi = ({
  courseModuleSeq,
  courseUserSeq,
  chasiName
}: {
  courseModuleSeq: number;
  courseUserSeq: number;
  chasiName?: string;
}) => {
  return useQuery(
    ['progressApi', { courseModuleSeq }],	//모듈시퀀스가 변경될 때마다 리패치
    async () => {
      const response = await GET(`/module/${courseModuleSeq}/***`, {
        params: { courseUserSeq, courseModuleSeq, chasiName, page: 1, sort: 'old' }
      });
      return response;		//chasiProgressApi에 이 리턴값이 저장된다
    },
    {//option
      keepPreviousData: true, // 모듈 이동시 깜박거리는 현상 제거 - ux향상
      refetchOnWindowFocus: false // 윈도우 포커스 시 자동 리패치 방지
    }
  );
};

//course.tsx - chasiProgressApi 함수를 호출하여 데이터를 가져온다 ✅
  const { data: chasiData, refetch } = chasiProgressApi({		//해당 데이터는 chasiData에 할당된다
    courseModuleSeq: courseModuleSeq,
    courseUserSeq: courseUserSeq,
    chasiName: searchKeyword
  });

 

 

* keepPreviousData가 버전 5에서는 placeholderData: previousData => previousData, 로 변경