본문 바로가기

React/실험실

[React] React-Query - useQuery

들어가며. 

react-query를 몇번 사용했지만 타입스크립트와 함께 사용했을 때, 또는 쿼리를 가지고 오는

부분에서 불편함을 느끼고 있어서 이번에 정리해서 작성하려고 한다. 

 

useQuery 

  const { data } = useQuery("todo", getTodoList);

가장 기본적인 사용방법이다. 

 

첫 번째 매개변수는 Query Key가 들어가는데, 문자 혹은 배열로 넣을 수 있다. 

const todoList = useQuery("todoList", getTodoList);
or 
const todoList = useQuery(["todoList", "1"], getTodoList);

Key는 고유해야 하며, 추후 해당 키를 가지고 구분할 수 있다. 

 

두 번째 매개변수로는 Promise를 반환하는 함수를 넣어줘야 한다. 

/// api.ts

export const getTodoList = async () => {
  const { data } = await axios.get(`${TEST_URL}/todoList`);

  return data;
};

export const getTodo = async (id: string) => {
  const { data } = await axios.get(
    `${TEST_URL}/todo?${new URLSearchParams({ id })}`
  );

  return data;
};

// app.tsx
const todoList = useQuery("todoList", getTodoList);
const todo = useQuery(["todo", "1"], () => {
  getTodo("1");
});

따로 매개변수로 받는 것이 없다면 첫 번째 형식으로 바로 함수를 넣고 매개변수가 필요하다면 

화살표 함수를 사용해서 넣어준다. 

 

위 예시에는 없지만 세 번째 매개변수도 있다. 세 번째 매개변수는 Option을 설정할 수 있다. 

enabled ( boolean )

▶ 쿼리가 자동으로 실행되지 않게 막는 옵션 

default : true ( 자동 재실행 ) 

const todo = useQuery(
  ["todo", "1"],
   () => {
    getTodo("1");
  },
  {
    enabled: isClick,
  }
);

 

retry ( boolean | number ) 

요청을 실패 할 경우 몇번까지 재시도할지 정하는 옵션

default : 3

만약 true로 설정할 경우 무한 재시도를 하고, false일 경우 재시도를 하지 않는다. 

 

staleTime ( number | Infinity )

 데이터가 fresh 상태로 유지되는 시간을 말한다. 

 default : 0

 0으로 하는 경우 사실 상 캐싱 기능을 사용하지 않겠다는 뜻인데, 상황에 따라 잘 고려해야 한다. 

 

cacheTime ( number | Infinity )

▶ inactive 상태로 캐시 데이터가 남아있는 시간을 나타낸다. 

▶ default : 5 * 60 * 1000 ( 5분 )

 

refetchOnMount ( boolean | "always" )

▶ 데이터가 stale 상태인 경우 마운트 시 마다 refetch를 발생한다. 

▶ default : true 

 

refetchOnWindowFocus ( boolean | "always" )

▶ 데이터가 stale 상태인 경우 윈도우 포커싱이 발생하면 refetch를 발생한다.

▶ default : true

▶ 만약 다른 탭, 또는 다른 창을 눌렀다가 다시 복귀한 경우를 윈도우 포커싱 이라고 한다. 

 

refetchOnReconnect ( boolean | "always" )

▶ 데이터가 stale 상태인 경우 네트워크가 다시 연결되면 refetch를 발생한다. 

▶ default : true 

 

refetchOn 함수는 모두 "always" 값도 가질 수 있는데, always의 경우 매번 해당 상황이 발생 시 

refetch가 발생한다. 

 

onSuccess ( (data: TDdata) => void )

▶ 쿼리가 성공적으로 실행되면 자동으로 호출되는 함수이다. 

▶ data의 경우 서버에서 넘어오는 response의 값이다. 

 

onError ( (error: TError) => void )

▶ 쿼리가 실패하면 자동으로 호출되는 함수이다. 

▶ error의 경우 에러 값을 가지고 있다. 

 

onSettled ( (data?:TDdata, error?:TError) => void ) 

▶ 쿼리에 성공 또는 실패한 경우 호출되는 함수이다. 

▶ 성공한 경우 TDdata, 실패한 경우 TError가 전달된다. 

 

initialData ( TData | () => TData )

▶ 쿼리의 초기 데이터로 사용된다. ( 쿼리가 아직 생성되지 않았거나 캐시되지 않았을 때 )

 

QueryKey 관리하기 

위에서 Key를 사용할 때 문자열 또는 배열로 바로 넣어줘도 사용할 순 있지만 

일관성을 위해서는 Key를 만들어서 사용하는 것이 좋다. 

const Todo_Key = {
  todoList: "todo_todoList",
  todo: "todo_todo",
};

( todo_todo 는 내가 봐도 너무 대충 만들긴 했네요... ㅎㅎ )

 

커스텀 훅

여러 컴포넌트에서 같은 요청을 할 때 매번 새롭게 만들기보단 커스텀 훅을 만들어서 공통으로

관리할 수 있다. 

import { AxiosError } from "axios";
import { useQuery, UseQueryOptions } from "react-query";
import { getTodoList } from "../../api/todo";
import Todo from "../../types/todo";

const Todo_Key = {
  todoList: "todo_todoList",
  todo: "todo_todo",
};

interface Return {
  data: Todo[] | undefined;
}

const useTodoList = (options: UseQueryOptions<Todo[], AxiosError>): Return => {
  const { data } = useQuery<Todo[], AxiosError>(
    Todo_Key.todoList,
    getTodoList,
    options
  );

  return { data };
};

export default useTodoList;

이때는 <> 제네릭이 중요하다. 

기본적으로 useQuery는 다음과 같이 선언되어 있다. 

export function useQuery<
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>

TQueryFnData 

useQuery로 실행 했을 때 두 번째 매개변수인 Promise의 반환하는 타입을 나타낸다. 

 

TError 

마찬가지로 두 번째 매개변수의 Error 타입을 나타낸다. 

나는 백엔드 요청을 하는 비동기 작업을 예시로 했기 때문에 Axios 오류를 넣어줬다. 

 

TData 

data에 담기는 실질적인 type을 나타낸다. 

일반적으로 따로 지정하지 않으면 TQueryFnData를 따라가고, 선언한 경우에만 선언한 타입으로 

설정된다. 

 

TQueryKey

첫 번째 인자로 넘겨주는 Query Key의 타입을 명시해주는 타입이다. 

일반적으로 가장 사용할 경우가 적은 타입이라고 생각된다. 

 

내가 사용하는 폴더 구조

일반적으로 API는 api폴더를 사용해서 안에 넣어주고, 커스텀 훅은 당연히 hooks 폴더에서 

query 폴더를 만들어서 사용한다. 

 

 

반응형