[React] React-query에 대해서 알아보자

2023. 1. 5. 11:25·🍞 FrontEnd/React

✅ React-query란

공식문서는, React에서 데이터의 fetching, caching, synchronizing, updating server state를 위한 라이브러리라고 정의한다. 즉, axios처럼 단순히 API fetching의 목적보다는, 이 데이터와 더불어 서버 상태(server state)를 관리하는 라이브러리인 것이다. 서버의 값을 클라이언트에 가져오거나, 캐싱, 값, 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 하는 데 사용된다.

 

✅ 사용 이유

서버로부터 값을 가져오거나 업데이트 하는 로직을 store 내부에 개발하는 경우가 많다. 그렇다 보니 store는 클라이언트 state를 유지해야 하는데 어느 순간부터 store에 클라이언트 데이터와 서버 데이터가 공존하게 되었다. 그리고 그 데이터가 서로 상호작용하면서 서버 데이터도 클라이언트 데이터도 아닌 혼종이 탄생하게 되었다. (예를 들면 서버에는 이미 페치 된 데이터가 클라이언트에서는 페치 되기 전 데이터가 유저에게 사용되고 있는 것이라고 볼 수 있다.)

그래서 react-query를 사용함으로 서버, 클라이언트 데이터를 분리한다. 

 

- Client에서 제어하거나 소유되지 않은 원격의 공간에서 관리되고 유지됨
- Fetching이나 Updating에 비동기 API가 필요함
- 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 업데이트 될 수 있음
- 앱에서 사용하는 데이터가 "유효기간이 지난" 상태가 될 가능성을 가짐

 

✅ react-query 장점

여러가지 장점이 있지만, 주로 아래와 같이 프론트 개발자가 구현하기 귀찮은 일들을 수행한다.

- 캐싱
- 서버 데이터 중복 호출 제거
- 만료된 데이터를 백그라운드에서 제거
- 데이터의 만료시점 인지
- 만료된 데이터의 업데이트
- Pagination, Lazy Loading 등의 성능 최적화
- Server State의 메모리 관리, 가비지 콜렉션
- 쿼리 결과의 구조 공유를 통한 메모이제이션

 

✅ LifeCycle(라이프 사이클)

React Query를 통해 관리되는 쿼리 데이터는 라이프 사이클에 따라 각 상태를 가진다.

fetching : 요청중인 쿼리
fresh : 만료되지 않은 쿼리. 컴포넌트가 마운트, 업데이트 되어도 데이터를 재요청하지 않음
stale : 만료된 쿼리. 컴포넌트가 마운트, 업데이트되면 데이터를 다시 요청한다
inactive : 사용하지 않는 쿼리. 일정 시간이 지나면 가비지 컬렉터가 캐시에서 제거
delete : 캐시에서 제거된 쿼리.

 

✅ 주요 컨셉

공식문서에서는, React Query의 3가지 주요 컨셉들로 아래를 소개한다.

Queries : 비동기 데이터에 대한 선언적 종속성(declarative dependency). Unique Key를 가지며, GET, POST 메서드와 연관
Mutations : Create, Update, Delete 등 서버 데이터의 수정과 관련된 기능
Query Invalidation : 캐싱된 쿼리 데이터가 유효한지 여부를 판단하는 개념.

 

📝 사용하기

본격적으로 먼저 react-query를 설치해 준다.

npm i react-query

먼저 react의 가장 기본이 되는 곳에 react-query를 사용하도록 세팅한다.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <QueryClientProvider client={queryClient}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </QueryClientProvider>
);

reportWebVitals();

 

✅ useQuery

- 데이터를 get 하기 위한 api다. post, update는 useMutation을 사용한다.

- 첫 번째 파라미터로 unique Key가 들어가고, 두번째 파라미터로 비동기 함수(api 호출 함수)가 들어간다. (당연한 말이지만 두번째 파라미터는 promise가 들어가야 한다.)

- 첫번째 파라미터로 설정한 unique Key는 다른 컴포넌트에서도 해당 키를 사용하면 호출 가능하다 unique Key는 string과 배열을 받는다. 배열로 넘기면 0번 값은 string 값으로 다른 컴포넌트에서 부를 값이 들어가고 두 번째 값을 넣으면 query 함수 내부에 파라미터로 해당 값이 전달된다.

- return 값은 api의 성공, 실패 여부, api return 값을 포함한 객체이다.

- useQuery는 비동기로 작동한다. 즉, 한 컴포넌트에 여러 개의 useQuery가 있다면 하나가 끝나고 다음 useQuery가 실행되는 것이 아닌 두개의 useQuery가 동시에 실행된다. 여러개의 비동기 query가 있다면 useQuery보다는 밑에 설명할 useQuries를 추천한다.

- enabled를 사용하면 useQeury를 동기적으로 사용 가능하다.

const Todos = () => {
  const { isLoading, isError, data, error } = useQuery("todos", fetchTodoList, {
    refetchOnWindowFocus: false, // react-query는 사용자가 사용하는 윈도우가 다른 곳을 갔다가 다시 화면으로 돌아오면 이 함수를 재실행합니다. 그 재실행 여부 옵션 입니다.
    retry: 0, // 실패시 재호출 몇번 할지
    onSuccess: data => {
      // 성공시 호출
      console.log(data);
    },
    onError: e => {
      // 실패시 호출 (401, 404 같은 error가 아니라 정말 api 호출이 실패한 경우만 호출됩니다.)
      // 강제로 에러 발생시키려면 api단에서 throw Error 날립니다. (참조: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default)
      console.log(e.message);
    }
  });

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

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

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

 

isLoading, isSuccess 말고 status로 한 번에 처리 가능하다.

function Todos() {
  const { status, data, error } = useQuery("todos", fetchTodoList);

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

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

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

 

✅ useQueries

useQuery를 비동기로 여러 개 실행할 경우 여러 귀찮은 경우가 생긴다.

const usersQuery = useQuery("users", fetchUsers);
const teamsQuery = useQuery("teams", fetchTeams);
const projectsQuery = useQuery("projects", fetchProjects);

// 어차피 세 함수 모두 비동기로 실행하는데, 세 변수를 개발자는 다 기억해야하고 세 변수에 대한 로딩, 성공, 실패처리를 모두 해야한다.

이때 promise.all처럼 useQuery를 하나로 묶을 수 있는데, 그것이 useQueries다. promise.all과 마찬가지로 하나의 배열에 각 쿼리에 대한 상태 값이 객체로 들어온다.

// 아래 예시는 롤 룬과, 스펠을 받아오는 예시
const result = useQueries([
  {
    queryKey: ["getRune", riot.version],
    queryFn: () => api.getRunInfo(riot.version)
  },
  {
    queryKey: ["getSpell", riot.version],
    queryFn: () => api.getSpellInfo(riot.version)
  }
]);

useEffect(() => {
  console.log(result); // [{rune 정보, data: [], isSucces: true ...}, {spell 정보, data: [], isSucces: true ...}]
  const loadingFinishAll = result.some(result => result.isLoading);
  console.log(loadingFinishAll); // loadingFinishAll이 false이면 최종 완료
}, [result]);

 

✅ useMutation

값을 바꿀 때 사용하는 api다. return 값은 useQuery와 동일하다.
간단한 로그인 예시

import { useState, useContext, useEffect } from "react";
import loginApi from "api";
import { useMutation } from "react-query";

const Index = () => {
  const [id, setId] = useState("");
  const [password, setPassword] = useState("");

  const loginMutation = useMutation(loginApi, {
    onMutate: variable => {
      console.log("onMutate", variable);
      // variable : {loginId: 'xxx', password; 'xxx'}
    },
    onError: (error, variable, context) => {
      // error
    },
    onSuccess: (data, variables, context) => {
      console.log("success", data, variables, context);
    },
    onSettled: () => {
      console.log("end");
    }
  });

  const handleSubmit = () => {
    loginMutation.mutate({ loginId: id, password });
  };

  return (
    <div>
      {loginMutation.isSuccess ? "success" : "pending"}
      {loginMutation.isError ? "error" : "pending"}
      <input type="text" value={id} onChange={e => setId(e.target.value)} />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
      />
      <button onClick={handleSubmit}>로그인</button>
    </div>
  );
};

export default Index;

 

update후에 get 다시 실행

react-query 장점으로 update 후에 get 함수를 간단히 재실행할 수 있다고 한다.
mutation 함수가 성공할 때, unique key로 맵핑된 get 함수를 invalidateQueries에 넣어주면 된다.

const mutation = useMutation(postTodo, {
  onSuccess: () => {
    // postTodo가 성공하면 todos로 맵핑된 useQuery api 함수를 실행합니다.
    queryClient.invalidateQueries("todos");
  }
});

 

만약 mutation에서 return 된 값을 이용해서 get 함수의 파라미터를 변경해야 할 경우 setQueryData를 사용한다.

const queryClient = useQueryClient();

const mutation = useMutation(editTodo, {
  onSuccess: data => {
    // data가 fetchTodoById로 들어간다
    queryClient.setQueryData(["todo", { id: 5 }], data);
  }
});

const { status, data, error } = useQuery(["todo", { id: 5 }], fetchTodoById);

mutation.mutate({
  id: 5,
  name: "nkh"
});

 

 

 

React-Query 살펴보기

React-Query는 React앱에서 비동기 로직을 쉽게 다루게 해주는 라이브러리입니다. 그동안 제가 React로 개발할 때 가장 많이 써왔던, Redux와 Saga를 이용해 비동기 관련 로직들을 관리하는 것과 꽤나 다

maxkim-j.github.io

 

저작자표시 (새창열림)

'🍞 FrontEnd > React' 카테고리의 다른 글

[React] react-query Missing queryFn  (0) 2023.01.20
[React] react-hook-form 버전 오류  (0) 2023.01.11
[React] React-slick 캐러셀 구현 모듈  (0) 2023.01.04
[Redux] Redux-saga를 알아보자  (0) 2023.01.03
[React] 구독하기(팔로워, 팔로우) 기능  (0) 2022.12.29
'🍞 FrontEnd/React' 카테고리의 다른 글
  • [React] react-query Missing queryFn
  • [React] react-hook-form 버전 오류
  • [React] React-slick 캐러셀 구현 모듈
  • [Redux] Redux-saga를 알아보자
박빵이
박빵이
2025년에도 갓생살기
  • 박빵이
    기억보다 기록
    박빵이
  • 전체
    오늘
    어제
    • 분류 전체보기 (337)
      • 🍞 FrontEnd (97)
        • HTML+CSS (4)
        • JavaScript (17)
        • TypeScript (4)
        • React (52)
        • Next.js (2)
        • Android (15)
      • 🍞 BackEnd (24)
        • Java (15)
        • Node.js (6)
        • Spring (1)
      • 🍞 Cloud & Infra (0)
        • AWS SAA (0)
        • Microsoft Azure (0)
      • 🍞 Algorithm (147)
        • C++ (4)
        • Baekjoon (41)
        • Programmers (97)
      • 🍞 Computer Science (18)
        • 운영체제 (1)
        • 데이터 통신 (6)
        • 네트워크 (6)
        • 데이터베이스 (1)
      • 🍞 대외활동 & 부트캠프 (42)
        • 삼성 청년 SW 아카데미 (1)
        • LG유플러스 유레카 (0)
        • 한국대학생IT경영학회 (1)
        • IT연합동아리 UMC (17)
        • 길벗 블로깅 멘토 (18)
        • IT연합동아리 피로그래밍 (3)
        • 개발 컨퍼런스 (2)
  • 블로그 메뉴

    • Admin
  • 링크

    • GitHub
  • 인기 글

  • 태그

    유니온파인드
    길벗 블로깅 멘토링
    프로그래머스
    코딩자율학습
    코틀린
    level1
    백준
    level2
    JavaScript
    map
    Front
    알고리즘
    umc
    Android
    길벗 블로깅 멘토
    안드로이드
    Java
    react
    위상정렬
    C++
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
박빵이
[React] React-query에 대해서 알아보자
상단으로

티스토리툴바