이번 프로젝트에서 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
를 사용하게 되었습니다.