=> 리액트에서 서버 상태를 관리하는데 도움을 주는 라이브러리
React Query는 클라이언트 애플리케이션에서 서버 상태를 효율적으로 관리하고, 서버와의 데이터 동기화를 쉽게 하기 위해 등장했다.
전통적인 상태 관리 라이브러리(예: Redux, MobX)로 서버 데이터를 관리하는 것은 종종 복잡하고 반복적인 코드 작성을 요구했다.
서버 상태를 관리하는 데 필요한 페칭, 캐싱, 동기화, 에러 핸들링 등의 작업을 수작업으로 처리해야 했다.
동일한 데이터를 여러 번 페칭하거나, 불필요한 네트워크 요청이 발생하는 문제가 있었다.
데이터가 갱신될 때마다 컴포넌트가 재렌더링되면서 성능 저하가 발생할 수 있었다.
서버에서 가져온 데이터를 클라이언트에서 관리할 때 일관성을 유지하는 것이 어려웠다.
여러 컴포넌트에서 동일한 데이터를 사용할 때, 데이터의 변경 사항을 각 컴포넌트에 반영하는 것이 복잡했다.
위와 같은 문제들을 해결하기 위해 React Query가 등장하게 되었는데,
useQuery 훅을 사용하여 데이터 페칭, 캐싱, 동기화를 간편하게 처리할 수 있다.
동일한 쿼리에 대해 여러 번 요청할 경우, 자동으로 캐싱된 데이터를 사용하여 네트워크 요청을 최소화한다.
로딩 상태, 에러 상태, 데이터 상태를 자동으로 관리하여, 복잡한 상태 관리를 간소화한다.
데이터가 갱신되면 자동으로 컴포넌트를 리렌더링하여 최신 데이터를 표시한다.
백그라운드에서 데이터를 자동으로 갱신하여, 사용자가 항상 최신 데이터를 볼 수 있게 한다.
폴링, 리페치, 쿼리 무효화 등의 기능을 제공하여 데이터의 일관성을 유지한다.
데이터 페칭 중 발생하는 에러를 쉽게 처리하고, 사용자에게 적절한 피드백을 제공할 수 있다.
선언적인 API를 제공하여, 코드의 가독성을 높이고 유지보수성을 향상시킨다.
DevTools를 통해 쿼리 상태를 시각적으로 확인하고 디버깅할 수 있다.
서버 상태를 클라이언트 상태와 분리하여, 각각의 상태를 독립적으로 관리할 수 있다.
서버 상태에만 집중할 수 있도록 설계되어, 클라이언트 상태 관리 라이브러리와도 잘 통합된다.
쿼리를 관리하는 중심 객체로, 쿼리 캐시와 관련된 설정을 담당한다.
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<ExampleComponent />
</QueryClientProvider>
);
}
데이터를 가져오기 위해 사용하는 훅으로, 쿼리 키와 fetch 함수를 인자로 받아 쿼리 키에 해당하는 데이터를 가져오고 캐시한다.
import { useQuery } from 'react-query';
function ExampleComponent() {
const { data, error, isLoading } = useQuery('repoData', fetchRepoData);
async function fetchRepoData() {
const response = await fetch('https://api.github.com/repos/tannerlinsley/react-query');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
}
if (isLoading) return 'Loading...';
if (error) return `An error has occurred: ${error.message}`;
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
</div>
);
}
여러 개의 쿼리를 동시에 처리할 때 사용하는 React Query 훅이다.
각 쿼리에 대한 설정을 배열로 전달하며, 각 쿼리의 결과는 배열로 반환된다. 이를 통해 여러 데이터를 병렬로 가져오고, 각각의 상태와 결과를 관리할 수 있다.
import { useQueries } from 'react-query';
function ExampleComponent() {
const results = useQueries([
{
queryKey: ['repoData1'],
queryFn: () => fetchRepoData('https://api.github.com/repos/tannerlinsley/react-query'),
},
{
queryKey: ['repoData2'],
queryFn: () => fetchRepoData('https://api.github.com/repos/facebook/react'),
}
]);
async function fetchRepoData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
}
return (
<div>
{results.map((result, index) => {
if (result.isLoading) return <p key={index}>Loading...</p>;
if (result.error) return <p key={index}>An error has occurred: {result.error.message}</p>;
return (
<div key={index}>
<h1>{result.data.name}</h1>
<p>{result.data.description}</p>
</div>
);
})}
</div>
);
}
데이터를 생성, 업데이트, 삭제할 때 사용하는 훅이다.
useMutation은 mutate 함수를 반환하며, 이를 사용하여 변이 작업을 수행할 수 있다.
import { useMutation, useQueryClient } from 'react-query';
function ExampleComponent() {
const queryClient = useQueryClient();
const mutation = useMutation(newTodo => fetch('/api/data', {
method: 'POST',
body: JSON.stringify(newTodo),
}), {
onSuccess: () => {
// 쿼리 무효화
queryClient.invalidateQueries('todos');
},
});
function handleAddTodo() {
mutation.mutate({ title: 'New Todo' });
}
return (
<div>
<button onClick={handleAddTodo}>Add Todo</button>
</div>
);
}
쿼리의 결과를 캐싱하는 객체로, 캐싱된 데이터에 접근하거나 업데이트할 수 있다.
QueryClient의 인스턴스를 생성할 때 내부적으로 사용되며, 개발자가 직접 사용할 일은 많지 않지만 필요에 따라 커스터마이징이 가능하다.
import { QueryCache, QueryClient } from 'react-query';
const queryCache = new QueryCache({
onError: (error, query) => {
console.log(`Query ${query.queryKey} failed: ${error.message}`);
},
onSuccess: (data, query) => {
console.log(`Query ${query.queryKey} succeeded: ${data}`);
},
});
const queryClient = new QueryClient({ queryCache });
function App() {
return (
<QueryClientProvider client={queryClient}>
<ExampleComponent />
</QueryClientProvider>
);
}
QueryClient 인스턴스를 리액트 컴포넌트 트리에 제공하는 역할을 한다.
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<ExampleComponent />
</QueryClientProvider>
);
}