
이번 프로젝트에서 react-query를 적용해 보면서 공부한 내용에 대해 적어보려고 합니다.
비동기 데이터를
패칭,캐싱,동기화,업데이트하는 작업을 쉽게 해주는 라이브러리
캐싱 및 리패칭을 통해 최신의상태의 데이터를 참조고유한 쿼리의 키만 가지고 어디서든 데이터 접근 가능 서버상태의 데이터관리자주 사용하는 데이터나 연산 결과를 임시로 저장해 두고, 필요할 때 빠르게 접근할 수 있도록 하는 기술
캐싱을 통해 동일한 데이터의 반복적인 호출을 줄여 서버의 부하를 낮춤이전상태의 화면을 보여줌으로써 더나은 경험을 제공그렇다면 언제 캐싱을 통해 데이터를 재사용하고 언제 서버에 요청을 하는것인가??
react-query에서는 stale(오래된)데이터와 fresh(신선한)데이터로 구분지어 stale한 데이터로 간주되면 필요할때 refetch가 됩니다.
바로 staleTime을 통해 구분합니다.
데이터가 fresh라고 간주되는 시간입니다.
즉 staleTime만큼은 앞에서말한 필요할때(위에서 말한refech가 되는 조건)여도 refetch가 일어나지 않습니다(즉 fresh하다는것)
staleTime의 기본값은 0이기때문에 데이터를 받자마자 stale한 데이터로 간주된다 따라서 따로 설정을 통해 변경해주어야합니다
또한, cacheTime을 통해 데이터가 캐시에 남아 있는 기간을 정의할 수 있습니다.
react-query는 ContextAPI를 기반으로QueryClientProvider라는 컴포넌트를 통해 전역적으로 Query Client를 제공합니다.
query(비동기 데이터)를 관리하는 주요 객체입니다. Query Client는 모든 쿼리 상태를 관리하고, 데이터를 가져오고, 캐싱하고, 동기화하며, 데이터를 업데이트하는 등의 작업을 수행합니다.
따라서 react-query는 데이터를 전역상태로 관리하면서 여러 컴포넌트에서 비동기 데이터를 쉽게 공유할 수 있게 설계 되었습니다.
GET 요청일경우에는 useQuery를 사용하고 POST,PUT,DELETE와 같은 요청인 경우에는 useMutation을 사용합니다.
useQuery는 쿼리의 unique key값과 promise 반환하는 함수가 인자로 들어갑니다.
unique key : 애플리케이션 전체에서 쿼리를 다시 가져오고,캐싱하고,공유하는데 내부적으로 사용하는값입니다.
useQuery의 반환값은 API요청에 대한 성공 및 실패 등 다양한 상태와 데이터를 포함하는 객체를 반환합니다.
ex) data,error,isLoading,isError,isSuccess,isFetching,
refetch,status
코드예시
function Todos() {
const { isPending, isError, data, error } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodoList,
})
if (isPending) {
return <span>Loading...</span>
}
if (isError) {
return <span>Error: {error.message}</span>
}
// We can assume by this point that `isSuccess === true`
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
조건부 실행
또한 useQuery에서 세 번째 인자로enabled에 값을 넣어 특정조건(값이 true가 될때)이 만족할때만 쿼리를 실행시켜 동기적으로 사용 가능합니다.
코드예시
import { useQuery } from 'react-query';
const fetchUser = async (id) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
};
function UserProfile({ userId }) {
const { data, isLoading } = useQuery(['user', userId], () => fetchUser(userId), {
enabled: !!userId // userId가 존재할 때만 쿼리를 실행
});
if (isLoading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>User Profile</h1>
<p>{data?.name}</p>
</div>
);
}
useQuery와 달리 데이터를 생성/업데이트/삭제하거나 서버 사이드 이펙트를 수행하는 데 사용됩니다. useMutation으로 mutation 객체를 정의하고, mutate메서드를 사용하면 요청 함수를 호출해 요청이 보내집니다.function App() {
const mutation = useMutation({
mutationFn: (newTodo) => {
return axios.post('/todos', newTodo)
},
})
return (
<div>
{mutation.isPending ? (
'Adding todo...'
) : (
<>
{mutation.isError ? (
<div>An error occurred: {mutation.error.message}</div>
) : null}
{mutation.isSuccess ? <div>Todo added!</div> : null}
<button
onClick={() => {
mutation.mutate({ id: new Date(), title: 'Do Laundry' })
}}
>
Create Todo
</button>
</>
)}
</div>
)
}
mutation과 같은 요청으로 인해 서버에서 바뀌었다면 백그라운드에 남은 데이터는 stale상태가 되어 쓸모가 없어지는 상황이 발생할 수 있습니다.invalidateQueries 메소드를 통해 query가 stale되었다고 처리하여 refetch가 진행됩니다.invalidation이 가능합니다.코드예시
// 캐싱된 쿼리 모두 invalidtate
queryClient.invalidateQueries()
// 키에 todos가 들어간 쿼리 모두 invalidtate
queryClient.invalidateQueries({ queryKey: ['todos'] })
그렇다면 비동기 데이터를 관리해주는 라이브러리인 SWR이 있음에도 react-query를 선택한 이유가 무엇인지 알아보겠습니다.
react-query는 서버상태를 받아 캐싱하고, 동기화하고, 업데이트하는 것을 쉽게 해줍니다.
SWR은 먼저 캐시에서 데이터를 반환한 다음, 서버에 데이터를 가져오는 요청을 보내고, 마지막으로 최신 데이터를 제공하는 라이브러리입니다.
//react-query
const { data, isLoading, error } = useQuery('myData', fetchMyData, { cacheTime: 10000 });
//SWR
const { data, error } = useSWR('myData', fetchMyData, { dedupingInterval: 5000 });
문법에서는 크게 차이가 없는것을 확인할 수 있습니다.
React-Query에서는 공식적으로 react-query/devtools 를 통해 devtool을 지원합니다. 하지만 SWR 또한 devtools를 사용할 수 있지만, 서드 파티 라이브러리를 이용해야합니다.
SWR과 react-Query 모두 무한 스크롤을 구현하는 데 필요한 기능들을 제공하지만 SWR은 부가적인 코드를 작성해야 합니다. 그에 반해 react-Query에는 getPreviousPageParam, fetchPreviousPage, hasPreviousPage, isFetchingPreviousPage기능을 통해 좀 더 간단하게 구현할 수 있습니다.
SWR과 달리 react-Query에서는 select를 통해 원하는 데이터를 추출하여 반환할 수 있습니다.
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery('user', fetchUser, {
select: user => user.username,
})
return <div>Username: {data}</div>
}
SWR과 다르게 react-query는 쿼리가 업데이트될 때만 refetch를 진행한다. 또한 여러 컴포넌트에서 동일한 쿼리를 사용하는 경우 한번에 묶어 업데이트합니다.
SWR 와 달리 react-Query는 지정된 시간(기본 5분)동안 쿼리가 사용되지 않는다면 자동으로 메모리 해제를 하는 Garbage Collection을 통해 메모리를 관리해줍니다.
다음과 같이 react-query를 제대로 사용하게 된다면 SWR보다더 편리하고 좋은 성능으로 사용할 수 있겠다라고 판단되어서 react-query를 사용하게 되었습니다.