한 컴포넌트에서 (예를 들면 app.js) state 값에 따라 API 요청을 다르게 하고 싶었다.
예를 들면 GET 요청 API 두 개가 존재한다고 가정하자.
1. http://localhost:3001/dummy
2. http://localhost:3001/notdummy
📌 실제로 테스트 코드를 작성하고 실험할 때는 json-server
로 mock API를 만들어 사용했다.
한 컴포넌트에서 저 두 개의 API를 유연하게 사용하려면 state가 필요하다고 생각했다. 그래서 endpoint라는 state를 만들어 아래와 같이 사용하였다.
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()를 하기 때문에 데이터 갱신도 잘 된다. 하지만 몇 가지 문제가 있었다.
위의 devtools를 살펴보면 "temp_query"라는 query key 하나로 두 가지의 데이터를 다루는 것을 알 수 있다.
즉, temp_query
하나에 /dummy로 받아온 데이터, /notdummy로 받아온 데이터 이렇게 두 가지를 핸들링하고 저장해야한다.
따라서 쿼리의 유연한 관리에 어려움을 겪을 수 있을 것 같다.
캐싱 기능을 사용하기 위해 useQuery options에 staleTime을 10000ms 주었는데 이것이 무용지물이 되었다.
endpoint state가 바뀔 때 마다 refetch() 함수를 호출하기 때문인데, refetch는 캐시에 데이터가 남아있는지 없는지에 상관없이 무조건 API 재요청을 하기 때문에 캐시를 사용할 수 없다.
이건 캐싱과 연관된 문제인데, refetch()를 무조건 하기 때문에 그때마다 API 요청을 한다. 따라서 프로젝트 성향에 따라 다르겠지만 일단 서버에 과부하를 줄 수 있는 가능성이 있다고 판단할 수 있다.
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의 자연스러운 현상으로 문제들을 해결하였다고 볼 수 있겠다.
query key 안에 state를 포함시키기 때문에 endpoint가 포함된 두 개의 query key가 생성되어 데이터를 관리할 수 있다.
따라서 조금 더 유연한 데이터 관리가 가능해졌다라고 판단할 수 있겠다.
query key를 따로 사용하고, refetch()도 사용하지 않기 때문에 설정한 staleTime이 정상적으로 작동한다. 따라서 캐싱 기능을 사용할 수 있게 된다.
캐싱 기능이 작동하기 때문에 당연히 API 요청 횟수도 개발자의 의도에 따라 감소한다.