[React-Query] 리액트에서 비동기 요청을 | get

Duboo·2023년 11월 10일
0

REACT HOOK

목록 보기
16/16
post-thumbnail
post-custom-banner

불만

리액트와 리덕스를 사용해 상태를 관리하다 보면 서버의 상태 또한 관리를 해줘야 할 경우가 생기는데

리덕스 덕분에 전역적으로 상태를 관리할 수 있게 되었지만 서버의 데이터를 리덕스를 통해서 관리하다보면 클라이언트 데이터와 서버의 데이터가 공존하게 되는 경우가 생겼다.

이 경우 클리이언트 데이터와 서버의 데이터가 서로 상호작용하면서 동작하는 경우도 분명 있을 텐데, 양쪽의 데이터를 최신 상태로 맞춰주는 과정과 최신 상태가 맞는지에 대한 확인도 하게되어서 불편한 부분이 많았다.

해결점

이를 해결하기 위해

  • 클라이언트에서 전역적으로 관리가 필요한 데이터의 경우 리덕스를 통해서 관리
  • 서버에서 받아오는 데이터의 경우 리액트 쿼리를 이용해서 관리

리액트 쿼리를 선택하고 사용하는 이유

  • *비동기 과정을 선언적으로 관리

  • *서버에서 받아온(get) 데이터가 업데이트되면 자동으로 다시 get을 수행

  • 데이터가 최신이 아니라고 판단되면 다시 가져옴(get) | invalidateQueries

  • 서버의 데이터를 관리하기가 편함

  • 사용하기 쉬움

많은 장점이 있겠지만 현재 상황에서 가장 마음에 들었던 부분은 서버의 데이터를 따로 관리할 수 있다는 점과 비동기 요청을 선언적으로 관리하기가 편하다는 점이였다.

위 장점 뿐만 아니라 더 많은 기능들을 가지고 있어 사용하면서 천천히 익혀갈 생각이다.


세팅 및 사용

// src/index.js
import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

const queryClient = new QueryClient();

ReactDOM.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      {/* devtools */}
      <ReactQueryDevtools initialIsOpen={true} />
      <App />
    </QueryClientProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

리덕스와 비슷하게 초기 세팅을 해준다.

useQuery | 데이터 가져오기

  • *get으로 데이터를 가져오기 위한 api입니다.

  • 첫번째 파라미터로 unique key가 들어가며, 두번째 파라미터로 promise가 들어가는 비동기 (api 호출)함수가 들어갑니다.

  • *첫번째 파라미터로 설정한 unique key다른 컴포넌트에서도 해당 키를 사용하여 호출할 수 있습니다.

    • unique keystring 혹은 배열을 받습니다.
    • 배열로 넘기면 0번째 값은 string 값으로 다른 컴포넌트에서 부를 값이 들어가고 두번째 값을 넣으면 query 함수 내부에 파라미터로 해당 값이 전달됩니다.
  • return 값은 api의 성공/실패 여부, api return 값을 포함한 객체

  • *useQuery는 비동기로 작동합니다.

    • 이는 한개의 컴포넌트에서 여러개의 useQuery가 있다면 하나가 끝나고 다음이 실행되는게 아니라 두개 이상의 useQuery가 동시에 실행이 된다는 의미입니다.
    • 이 경우처럼 여러개의 query가 있다면 useQuery 보다는 useQueries를 권장합니다.
  • enabled를 사용하면 동기적으로도 사용할 수 있습니다.

const Todos = () => {
  const { data, status, error } = useQuery("todos", fetchTodoList, {
    refetchOnWindowFocus: false,
    retry: 0,
    onSuccess: data => {
      // 성공시 호출
      console.log(data);
    },
    onError: e => {
      // 실패시 호출
      console.log(e.message);
    }
  });

  if (status === "loading") {
    return <span>Loading...</span>;
  }

  if (status === "error") {
    return <span>Error: {error.message}</span>;
  }

  return (
    <ul>
      {status === "success" && data.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
};

옵션

  • refetchOnWindowFocus : 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다. | default: true

  • retry : 요청 실패시 재호출 몇번 할지 설정합니다. | default: 3

    • retry : false - 재시도가 비활성화됩니다.
    • retry : true - 실패한 요청을 무한히 재시도합니다.
    • retry : (failureCount, error) => ... 사용하면 요청이 실패한 이유에 따라 맞춤 논리를 사용할 수 있습니다.

주의❗

onSuccess: data => {
	// 성공시 호출
	console.log(data);
},

위 옵션의 호출 성공은 api 요청에 대한 성공입니다.

즉, 상태 코드 200번 등의 반환으로 데이터를 받아오는 것을 성공한것도,
상태 코드 4~500번 등의 반환으로 에러
를 받아오는 것도

모두 onSuccess에 해당합니다.

위와 같은 이유로

onError: e => {
	// 실패시 호출
	console.log(e.message);
}

위와 비슷한 맥락으로 401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.
강제로 에러 발생시키려면 api단에서 throw Error 날립니다.

데이터의 상태

생산성을 높이기 위해 알아야 할 몇 가지 매우 중요한 상태가 포함되어 있습니다.
쿼리는 특정 순간에 다음 상태 중 하나만 있을 수 있습니다.

isLoading 또는 status === 'loading' - 쿼리에 아직 데이터가 없습니다.
isError 또는 status === 'error' - 쿼리에 오류가 발생했습니다.
isSuccess 또는 status === 'success' - 쿼리가 성공했고 데이터를 사용할 수 있습니다.

쿼리가 제공하는 위 상태를 통해서 로딩 / 에러 / 성공에 대한 데이터 처리를 보다 쉽게 작성할 수 있습니다.
보다 자세한 상태에 대한 정보는 👈

동기적 실행

쿼리가 자동으로 실행되지 않도록 하려면 enabled = false 옵션을 사용하면 됩니다.

enabled 옵션을 사용하면 useQuery를 동기적으로 사용 가능합니다.

const { data: todoList, error, isFetching } = useQuery("todos", fetchTodoList);
const { data: nextTodo, error, isFetching } = useQuery(
  "nextTodos",
  fetchNextTodoList,
  {
    enabled: !!todoList // true가 되면 fetchNextTodoList를 실행
  }
);

useQuery의 3번째 인자로 옵션값을 넣어줄 수 있으며, enabled : true 로 로직을 구성하면 실행시킬 수 있습니다.

profile
둡둡
post-custom-banner

0개의 댓글