[React] React Query와 SWR의 차이?

저번 시간 SWR에 대해서 배워봤는데요, 많은 분들이 SWR대신 React Qeury를 사용하더라구요. 분명 더 좋다는 이야기가 많기는 한데, 구체적으로 어떤 차이인지 알아보기 위해서 공식문서를 뒤져가며 그 차이점을 알아보기 위해 노력했습니다.

이전 SWR에 대한 내용은 아래를 참고해주세요!

 

[React] SWR이 무엇인가요?

우선 SWR을 알아보기 앞서 캐시에 대해서 알아보겠습니다. ❓캐시란 무엇인가 캐시는 데이터를 일시적으로 저장하는 메모리를 가리키는 용어입니다. 한 번의 요청에 비용이 큰 경우, 여러 번 요

lurgi.tistory.com

 

그리고 몇 일간 React Query와 SWR의 차이에 대해서 명확히 하고자 했지만, 제 지식의 한계로는 그 차이를 인지할 수 없었고, 간단한 코드를 작성하는 것으로 역시 그 차이를 느끼기 힘들었습니다.

 

그러던 와중 Tanstack Query 공식 홈페이지의 Comparison | React Query vs SWR vs Apollo vs RTK Query vs React Router 라는 글을 발견했고, 이 문서를 토대로 React Query와 SWR의 차이를 조금이나마 알 수 있었습니다.

  React Query SWR
Github Repo / Stars (깃허브 스타) 37k 28k
Supported Query Syntax (지원 쿼리 문법) Promise, REST, GraphQL Promise, REST, GraphQL
Caching Strategy (캐싱 방법) Hierarchical Key -> Value (계층키를 이용) Unique Key -> Value
Cache Key Strategy (캐싱 키 방식) JSON JSON
Cache Change Detection Deep Compare Keys (Stable Serialization) Deep Compare Keys (Stable Serialization)
Data Change Detection Deep Comparison + Structural Sharing Deep Compare (via stable-hash)
Data Memoization (데이터 메모이제이션) Full Structural Sharing (구조적 공유) Identity (===) (식별 공유)
Bundle Size (번들 용량) 13.0KB 4.4KB
API Definition Location Component, External Config Component
Queries
Cache Persistence
Devtools
Polling/Intervals
Parallel Queries
Dependent Queries
Paginated Queries
Infinite Queries
Bi-directional Infinite Queries (페이지 네이션, 무한 스크롤 과 같은 UI사용) ✅ (React-Query는 다양한 프로퍼티를 지원하므로 구현하기 편리) 🔶 (SWR로 구현은 가능하나, 데이터 베이스를 핸들링 하기 위해서 추가적인 코드가 필요함)
Infinite Query Refetching
Lagged Query Data1
Selectors (선택자) ✅ (React-Query는 필요한 데이터만 선택적으로 가져올 수 있는 기능이 있다) 🛑
Initial Data
Scroll Recovery
Cache Manipulation
Outdated Query Dismissal
Render Batching & Optimization2
Auto Garbage Collection (자동적 가비지 컬렉션) ✅ (React-Query는 설정한 시간이 지나면 데이터를 새로 검증) 🛑 (SWR은 Stale한 data를 별도의 요청이 없다면 계속 남겨둠)
Mutation Hooks
Offline Mutation Support ✅ (React-Query는 오프라인 상황일 때 API 요청을 유보하고, 온라인이 될 시 그 요청을 재개한다) 🛑
Prefetching APIs
Query Cancellation ✅ (기본적으로 쿼리는 취소되지 않지만, 프로미스 처리 이전 cancle 함수를 적용하여 취소할 수 있다) 🛑
Partial Query Matching3 🔶
Stale While Revalidate
Stale Time Configuration 🛑7
Pre-usage Query/Mutation Configuration4 🛑
Window Focus Refetching
Network Status Refetching
General Cache Dehydration/Rehydration 🛑
Offline Caching ✅ (Experimental) 🛑
React Suspense (Experimental)
Abstracted/Agnostic Core 🛑
Automatic Refetch after Mutation5 🔶 🔶
Normalized Caching6 🛑 🛑
 

Comparison | React Query vs SWR vs Apollo vs RTK Query vs React Router | TanStack Query Docs

2 Render Optimization - React Query has excellent rendering performance. By default, it will automatically track which fields are accessed and only re-render if one of them changes. If you would like to opt-out of this optimization, setting notifyOnChangeP

tanstack.com

많은 블로그글에서 React Query를 사용하는 이유 중 DevTools을 꼽던데, SWR역시 DevTools을 지원하는 것을 확인했습니다.

✨ 주요 차이점 분석

1️⃣ Caching Strategy (캐싱 방법)

React Query는 Hierarchical Key(계층키)를 사용하고, SWR은 Unique Key를 사용합니다.

이는 다음과 같은 차이점을 가지는데요, 위의 그림이 Unique key를 이용한 일반적인 객체, 그리고 아래가 Hierarchical Key를 사용한 계층적 구조입니다.

ref : https://www.researchgate.net/figure/Key-hierarchies-a-N-pairwise-keys-b-Hierarchical-tree_fig1_3235873

 

계층키를 이용하면 Binary 트리 구조를 활용해 노드간 부모, 자식 관계를 만들 수 있습니다.

반면 유니크 키를 사용하면, 일대일, 혹은 일대다 관계를 만들고 사용하기 좋은 형태입니다. 고유의 데이터를 식별하는데 좋은 방법입니다.

특정 상황에 적절한 방법을 사용하시면 될 것 같습니다.

 

2️⃣ Data Memoization (데이터 메모이제이션)

React Query는 Full Structural Sharing, 즉 구조적 공유를 사용하고, SWR은 Identity, 식별 공유를 사용합니다. 구조적 공유와 식별 공유는 각각의 장단점을 가지는데요, 구조적 공유는 구조 자체를 공유함으로써 메모리를 아낄 수 있는 구조이지만, 데이터를 변경하는데 어렵고, 식별 공유는 같은 값을 참조한다면 하나의 메모리를 함께 공유함으로써 비용을 아낄 수 있지만, 하나의 메모리가 변경된다면 다른 참조 값 역시 바뀌기 때문에 불변성이 떨어지는 특징이 있습니다.

간단히 말한다면 구조적 공유는 불변성에 유리, 식별 공유는 가변성에 유리한 전략입니다.

 

3️⃣ Bi-directional Infinite Queries (페이지네이션, 무한 스크롤 과 같은 UI사용)

React Query는 다양한 기능을 사용하여 페이지네이션, 무한 스크롤 구현이 가능합니다. 이를 SWR로 구현하기 위해서는 추가적인 코드를 작성해야하는 불편함이 있습니다.

 

4️⃣ Query Data Selectors (선택자)

import { useQuery } from 'react-query'

function User() {
  const { data } = useQuery(['user'], fetchUser, {
    select: user => user.username,
  })
  return <div>Username: {data}</div>
}

React Query에서는 Selector 기능을 이용하여 원하는 데이터만 가져올 수 있습니다. 이는 SWR에서는 지원하지 않습니다.

 

5️⃣ Auto Garbage Collection (자동적 가비지 컬렉션)

캐시는 다음과 같은 사이클을 가집니다.

  • Query Instances with and without cache data (캐시 데이터 없음)
  • Background Refetching (데이터 요청 및 응답)
  • Inactive Queries (사용하지 않는 쿼리)
  • Garbage Collection (가바지 컬렉션)

React Query는 CacheTime과 StaleTime을 설정하여 데이터의 가바지 컬렉팅을 설정할 수 있습니다. SWR은 이러한 기능을 가지지 못합니다.

기본 값으로 CacheTime은 5분, StaleTime은 0으로 fresh 데이터는 캐싱이 되는 순간 Stale 상태로 바뀝니다.

아래는 React Query 환경에서 CachTime을 24시간, StaleTime을 2초로 설정하였습니다.

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      cacheTime: 1000 * 60 * 60 * 24, // 24 hours
      staleTime: 2000, // 2 sec
      retry: 0,
    },
  }
...

 

❗결론!

React Query의 공식 문서에는 이렇게 소개합니다.

‘Powerful asynchronous state management for React’ 강력한 비동기 상태 관리 라이브러리.

반면 SWR은 다음과 같이 소개합니다.

‘React Hooks for Remote Data Fetching’ 데이터 페칭을 위한 리액트 훅.

 

이 소개에서도 알 수 있듯이, SWR은 데이터를 페칭하는 것에 중점을 둔 라이브러리라면, React Query는 가져온 데이터를 상태 관리 하는 것에 중점을 둔 데이터라는 것을 알 수 있습니다.

 

특히나 차이점을 통해 알아본 바에 따르면, SWR은 기존의 데이터를 최대한 활용하여 사용자 경험을 향상시키려고 하는 반면, React Query는 지속적인 데이터 페칭과 변경, 삭제로 SWR에 비해 훨씬 동적인 기능을 많이 사용할 수 있습니다.

 

이를 토대로 결론을 내려본다면,

SWR은 간단한 데이터 페칭, 이후 변경이 많이 되지 않는다면 사용하고, React Query는 데이터의 요청, 변경이 잦고, UI에 빠르게 적용해야 하는 상황에 사용하기 적합하다고 생각이 됩니다.

SWR의 기능은 모두 React Query로 구현이 가능하나, 특히 번들 사이즈를 생각한다면, 무지성으로 React Query를 사용하기 보다는 필요한 상황에 적시에 사용하는 것이 좋아 보이네요!