[React Query] query key에 state 넣기

apro_xo·2022년 8월 11일
0
post-thumbnail

0. 테스트 상황

한 컴포넌트에서 (예를 들면 app.js) state 값에 따라 API 요청을 다르게 하고 싶었다.

예를 들면 GET 요청 API 두 개가 존재한다고 가정하자.
1. http://localhost:3001/dummy
2. http://localhost:3001/notdummy

📌 실제로 테스트 코드를 작성하고 실험할 때는 json-server로 mock API를 만들어 사용했다.

한 컴포넌트에서 저 두 개의 API를 유연하게 사용하려면 state가 필요하다고 생각했다. 그래서 endpoint라는 state를 만들어 아래와 같이 사용하였다.

1. 무지성 refetch() 방식

import axios from 'axios';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import React from 'react';

function App() {
  const [endpoint, setEndpoint] = React.useState('dummy');
  const nicknameRef = React.useRef('');
  const queryClient = useQueryClient(); // QueryClient()는 provider 정의 할 때만 사용한다.
  
  const fetchtempData = async () => {
    const res = await axios.get(`http://localhost:3001/${endpoint}`);
    
    return res.data;
  }


  const queryfetch = useQuery(['temp_query'], fetchtempData, {
    onSuccess: (data)=> {
      console.log(data)
    },
    staleTime:10000
  })
  
   React.useEffect(() => {
     queryfetch.refetch();
   }, [endpoint])

  return (
    <div className="App">
      {queryfetch.data.map((item, index) => <p key={index}>{item.sleep_time}</p>)}
      <input ref={nicknameRef}></input>
      <button onClick={()=> setEndpoint("notdummy")}>바꾸기</button>
      <button onClick={()=> setEndpoint("dummy")}>돌아가기</button>
      <button onClick={clickHandler}>추가하기</button>
    </div>
  );
}

export default App;

endpoint state를 "dummy"로 초기화하였기 때문에 컴포넌트가 처음 마운트 되었을 때 useQuery()에 의해 실행되는 API URL은 http://localhost:3001/dummy이 될 것이다.

useEffect()를 통해 endpoint state가 변경되면 refetch()하여 변경된 데이터를 받아올 수 있도록 하였다.

jsx부분에 버튼 두 개가 있는데, endpoint state를 변경할 수 있는 버튼이다.

버튼을 클릭하면서 endpoint를 바꿔 보니 원했던 것 처럼 endpoint에 맞게 API 요청도 잘 하고, refetch()를 하기 때문에 데이터 갱신도 잘 된다. 하지만 몇 가지 문제가 있었다.

1-1. 문제점🔥

1-1-1. 단일 query key


위의 devtools를 살펴보면 "temp_query"라는 query key 하나로 두 가지의 데이터를 다루는 것을 알 수 있다.
즉, temp_query 하나에 /dummy로 받아온 데이터, /notdummy로 받아온 데이터 이렇게 두 가지를 핸들링하고 저장해야한다.

따라서 쿼리의 유연한 관리에 어려움을 겪을 수 있을 것 같다.

1-1-2. 캐싱

캐싱 기능을 사용하기 위해 useQuery options에 staleTime을 10000ms 주었는데 이것이 무용지물이 되었다.

endpoint state가 바뀔 때 마다 refetch() 함수를 호출하기 때문인데, refetch는 캐시에 데이터가 남아있는지 없는지에 상관없이 무조건 API 재요청을 하기 때문에 캐시를 사용할 수 없다.

1-1-3. 많은 서버 API 요청

이건 캐싱과 연관된 문제인데, refetch()를 무조건 하기 때문에 그때마다 API 요청을 한다. 따라서 프로젝트 성향에 따라 다르겠지만 일단 서버에 과부하를 줄 수 있는 가능성이 있다고 판단할 수 있다.

2. state in query key👍

import axios from 'axios';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import React from 'react';

function App() {
  const [endpoint, setEndpoint] = React.useState('dummy');
  const nicknameRef = React.useRef('');
  const queryClient = useQueryClient(); // QueryClient()는 provider 정의 할 때만 사용한다.
  
  const fetchtempData = async () => {
    const res = await axios.get(`http://localhost:3001/${endpoint}`);
    
    return res.data;
  }


  const queryfetch = useQuery(['temp_query', endpoint], fetchtempData, {
    onSuccess: (data)=> {
      console.log(data)
    },
    staleTime:10000
  })
  

  return (
    <div className="App">
      {queryfetch.data.map((item, index) => <p key={index}>{item.sleep_time}</p>)}
      <input ref={nicknameRef}></input>
      <button onClick={()=> setEndpoint("notdummy")}>바꾸기</button>
      <button onClick={()=> setEndpoint("dummy")}>돌아가기</button>
      <button onClick={clickHandler}>추가하기</button>
    </div>
  );
}

export default App;

useQuery의 query key에 state를 넣는 방식이다. 위 코드를 보면 query key에 endpoint state가 같이 포함 돼 있음을 알 수 있다.
그리고 useEffect가 삭제되었다. state가 변경되면 컴포넌트가 리렌더링 되는 React의 자연스러운 현상으로 문제들을 해결하였다고 볼 수 있겠다.

2-1. 해결된 문제

2-1-1. query key

query key 안에 state를 포함시키기 때문에 endpoint가 포함된 두 개의 query key가 생성되어 데이터를 관리할 수 있다.

따라서 조금 더 유연한 데이터 관리가 가능해졌다라고 판단할 수 있겠다.

2-1-2. 캐싱

query key를 따로 사용하고, refetch()도 사용하지 않기 때문에 설정한 staleTime이 정상적으로 작동한다. 따라서 캐싱 기능을 사용할 수 있게 된다.

2-1-3. API 요청 횟수 감소

캐싱 기능이 작동하기 때문에 당연히 API 요청 횟수도 개발자의 의도에 따라 감소한다.

profile
유능한 프론트엔드 개발자가 되고픈 사람😀

0개의 댓글