React Query와 SWR 비교하기
React 어플리케이션을 만들 때 서버로부터 데이터를 가져오고 사용하는 것을 구현하는 것은 상당히 어려운 일입니다. 따라서 이를 돕기 위해 리액트에서 데이터를 가져오고, 캐싱하며 이를 관리할 수 있는 효과적인 방법을 제공하는 라이브러리들이 생겨났습니다.
이러한 라이브러리들을 데이터 페칭(data-fetching) 라이브러리라고 부르는데 근래 가장 잘 알려진 라이브러리로는 React Query와 SWR이 있습니다. 이 두 라이브러리가 어떤 특징을 가지고 있는지 그리고 어떤 차이점이 있는지 이 글을 통해 소개해보려합니다.
React Query
React Query는 React앱이 숨쉴 수 있도록(Breeze) 데이터를 가져오고, 캐싱하고 서버의 상태와 동기화(Synchronizing)을 쉽게 합니다. React Query의 주요 특징들을 먼저 알아봅시다.
Built-in caching
React Query는 특정 쿼리를 기반으로 데이터를 저장할 수 있는 빌트인 캐시가 가능합니다. 프로그래밍을 하며 캐싱을 다루는 것은 가장 어려운 일들 중에 하나인데 React Query는 이를 도와줍니다. 캐시를 네트워크 요청 상태에 따라 자동으로 관리하고 업데이트하여 페칭을 쉽게할 수 있게 합니다.
Stale-while-revalidate
React Query로 데이터 페칭을 하면 데이터가 캐시에 저장되는데 staleTime 이 경과되기 전에 동일한 데이터에 대한 페칭을 하면 React Query는 새로운 요청을 하는 대신 캐시된 데이터를 반환합니다.
이는 사용자가 앱를 이용하며 데이터 페칭이 일어날 때도 앱을 계속 사용할 수 있도록 합니다.
Advanced cache invalidation
React Query에서는 캐시 무효화를 제공하기 위한 기능들을 제공합니다.
import { useQuery, useQueryClient } from 'react-query'
// Get QueryClient from the context
const queryClient = useQueryClient()
queryClient.invalidateQueries('todos')
// Both queries below will be invalidated
const { isLoading, error, data, refetch } = useQuery('todos', fetchTodoList)
// const { isLoading, error, data, refetch } = useQuery(['todos', { page: 1 }], fetchTodoList)
// Cache Invalidation
refetch();
SWR(Stale-While-Revalidate)
SWR은 React의 데이터 페칭을 쉽게할 수 있는 API들을 가진 가볍고 빠른 라이브러리입니다. SWR은 stale-while-revalidate 정책을 통해 네트워크 요청을 줄여 새로운 데이터를 가져오는데 최적화되어있습니다. SWR의 주요 특징들도 알아봅시다.
Global Cache
SWR은 url을 key로 사용하여 이를 기반으로 데이터를 저장할 수 있는 글로벌 캐시를 제공합니다. 캐시는 네트워크 요청에 따라 자동으로 관리 및 업데이트되므로 데이터를 효율적으로 관리할 수 있습니다.
Lightweight and fast
SWR은 가볍고 빠르다는 것을 아주아주 강조합니다. 기본적인 데이터 페칭에 맞추어 최적화되어있습니다.
SSR / ISR / SSG support
아무래도 Vercel에서 Next.js를 만드는 팀에서 만들고 있어 여러 렌더링 패턴을 사용하는 것에 이점이 있습니다. 뿐만아니라 같이 사용할때 호환성 문제가 있다면 빠른 도움을 기대할 수 있습니다. (서로에 대해 고민하고 문서가 작성되어있어 공식문서에서 도움을 받기 좋습니다.)
React Query와 SWR의 차이점
Getting Started
React Query와 SWR의 가장 기본적인 초기 세팅을 보면 React Query가 조금 더 복잡합니다. React Query에서 useQueryClient 를 사용하기 위해 QueryClientProvider 를 설정해줘야합니다.
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from 'react-query'
import { getTodos, postTodo } from '../my-api'
// Create a client
const queryClient = new QueryClient()
function App() {
return (
// Provide the client to your App
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
function Todos() {
// Access the client
const queryClient = useQueryClient()
// Queries
const query = useQuery('todos', getTodos)
// Mutations
const mutation = useMutation(postTodo, {
onSuccess: () => {
// Invalidate and refetch
queryClient.invalidateQueries('todos')
},
})
return (
<div>
<ul>
{query.data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
<button
onClick={() => {
mutation.mutate({
id: Date.now(),
title: 'Do Laundry',
})
}}
>
Add Todo
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
반면에 SWR은 이러한 작업들이 필요하지 않습니다.
const fetcher = (...args) => fetch(...args).then(res => res.json())
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}
(API 문서만봐도 React Query보다 잘 정리되어있다는 인상을 받을 수 있었습니다.)
Built-in caching vs Global cache
React Query와 SWR은 둘다 데이터 페칭을 하고 캐싱을 활용하지만 캐싱 정책의 이름이 Built-in caching, Global cache로 다릅니다.
React Query의 Built-in caching은 각 쿼리마다 캐싱을 합니다. 이 캐싱된 데이터를 통해 앱내에서 동일한 쿼리에 대한 요청이 있을 때 캐싱된 데이터를 반환합니다.
React Query에서는 캐싱된 데이터가 오래될 경우 새로고침을 할 수 있는 메소드(staleTime)을 제공합니다. (SWR에서는 staleTimeout 이 이와같은 기능을 합니다.)
반면에 SWR의 Global cache는 모든 쿼리를 하나의 전역 캐시에 저장하여 관리합니다. 따라서 각각의 쿼리에 대해 캐싱관리를 해줄 필요가 없습니다. 그리고 전역 캐시를 사용하므로인해 서로다른 컴포넌트들 사이에서도 쉽게 데이터를 공유할 수 있게 해줍니다. 하지만 SWR의 Global cache는 관리하기 용이하지만 그 규모가 커지는 경우 메모리 누수와 같은 문제가 발생할 수 있습니다.
React Query는 각 쿼리에 대해 서로다른 key 를 설정할 수 있습니다. 이 key 에 따라 네트워크 응답 데이터가 저장되고 검색하는 것에 사용됩니다.
반면 SWR은 요청 URL을 key로 사용합니다. 따라서 서로다른 컴포넌트에서 동일한 URL을 요청하는 경우 모두 동일한 캐싱 데이터를 사용합니다.
정리하자면 React Query는 개발자가 정한 key 를 기반으로 캐싱을 하고 SWR은 요청 URL을 key 로 사용합니다. 따라서 React Query에서는 각 쿼리에 대해 캐싱설정을 할 수 있으며 원하는 경우에 캐싱 데이터를 반환하는 설정을 할 수 있습니다. 반면 SWR은 요청 URL을 key 로 사용하여 앱 전체에서 동일한 요청인 경우 큰 설정 없이 이전 요청의 캐싱 데이터를 사용할 수 있습니다. 하지만 그 규모가 커지면 메모리 누수와 같은 문제가 발생할 수 있어 관리가 필요합니다.
DevTools
SWR이 v2에서 부터 사용을 위해 별도의 셋업이 필요없고 기능이 좀 더 보완되긴 했지만 React Query에 비교하면 DevTools 기능이 아쉽습니다. React Query의 Devtools에서는 쿼리를 조회할 수 있는 것 뿐만아니라 실시간으로 캐시를 Refetch, Invalidate, Reset, Remove를 해볼 수도 있습니다.
마무리
React에서 데이터 페칭을 도와주는 React Query, SWR을 사용하는 측면에서 비교해보았습니다.
각 쿼리에 대한 캐싱 설정이 필요하고 요구사항이 복잡한 데이터를 가져오는 경우에는 React Query가 가볍고 빠른 라이브러리를 선호하고 기본적인 데이터를 가져오는 경우 SWR이 더 좋은 선택지가 될 수 있습니다.
다행히도 React Query, SWR 모두 잘 관리되고 있고 커뮤니티가 커서 두 라이브러리 모두 좋은 선택지가 될 수 있습니다. 따라서 두 라이브러리를 비교해보고 진행할 프로젝트에 더 적합한 라이브러리를 선택할 수 있도록 해야합니다.