문서의 각 단락이 어느 리비전에서 마지막으로 수정되었는지 확인할 수 있습니다. 왼쪽의 정보 칩을 통해 작성자와 수정 시점을 파악하세요.

데이터 패칭 TanStack Query 및 SWR | |
이름 | |
분류 | |
주요 기능 | 서버 상태 관리, 자동 캐싱, 동기화, 에러 핸들링 |
라이선스 | |
주요 사용 언어 | |
주요 프레임워크 | |
기술 상세 정보 | |
개발사/메인테이너 | TanStack Query: Tanner Linsley / SWR: Vercel |
초기 릴리스 | TanStack Query: 2019년, SWR: 2019년 |
현재 안정 버전 | |
저장소 | |
핵심 개념 | |
장점 | |
주요 차이점 | TanStack Query: 기능 풍부, 설정 옵션 다수. SWR: 가볍고 간단, Next.js와 통합 용이 |
대안 라이브러리 | |
설치 명령어 (npm) | TanStack Query: |
관련 문서 | |

TanStack Query와 SWR은 클라이언트 사이드 상태 관리 라이브러리로, 주로 서버의 데이터를 가져오고(데이터 패칭), 캐싱하며, 동기화하는 데 사용된다. 두 라이브러리 모두 React 생태계에서 널리 채택되어 비동기 데이터 흐름을 선언적이고 효율적으로 관리할 수 있게 해준다.
이들의 주요 목표는 API 호출과 관련된 반복적인 로직(로딩 상태 관리, 에러 처리, 데이터 캐싱, 백그라운드 갱신 등)을 추상화하여 개발자가 비즈니스 로직에 더 집중할 수 있도록 하는 것이다. 이를 통해 컴포넌트의 코드는 간결해지고, 애플리케이션의 사용자 경험은 자동으로 최신 데이터를 제공받으며 향상된다.
두 라이브러리는 기본 철학과 구현 방식에서 차이를 보인다. TanStack Query는 보다 포괄적이고 기능이 풍부한 쿼리 및 뮤테이션 패러다임을 제공하는 반면, SWR은 이름에서 유래한 스태일-와일-리밸리데이트 전략을 핵심으로 하는 경량화된 접근법을 선호한다. 이 문서는 두 도구의 핵심 개념, 기능, 성능 특성을 비교하고, 프로젝트 요구사항에 맞는 선택을 돕는 가이드를 제시한다.

데이터 패칭 메커니즘 측면에서 TanStack Query와 SWR은 모두 비동기 상태 관리를 단순화하지만 접근 방식에 차이가 있다. TanStack Query는 선언적 쿼리와 뮤테이션을 명확히 구분하며, useQuery 및 useMutation과 같은 훅을 통해 데이터 패칭, 캐싱, 동기화, 에러 핸들링을 포괄적으로 관리한다. 반면 SWR은 이름 그대로 'Stale-While-Revalidate' 전략을 핵심으로 하여, useSWR이라는 단일 훅을 중심으로 더 간결한 API를 제공한다. 기본적으로 SWR은 먼저 캐시된 스태일 데이터를 즉시 반환한 후, 백그라운드에서 재검증 요청을 보내 최신 데이터로 캐시를 업데이트한다.
캐싱 전략에서도 두 라이브러리는 서로 다른 철학을 보인다. TanStack Query는 중앙 집중식이고 구조화된 캐시 시스템을 갖추고 있다. 쿼리는 고유한 키로 식별되며, 캐시 데이터는 이 키를 기준으로 자동으로 관리된다. 캐시 무효화, 프리페칭, 백그라운드 리프레시 등 세밀한 제어가 가능하다. SWR의 캐싱은 더욱 가볍고 분산된 방식을 취한다. 전역 스토어 대신 훅 수준에서 캐시를 관리하는 경향이 있으며, 동일한 키를 가진 다수의 useSWR 호출은 자동으로 캐시와 상태를 공유한다. SWR의 핵심인 스태일-와일-리밸리데이트 전략은 사용자 경험을 우선시하여, 데이터가 아직 유효하다고 판단되면 새로고침 없이 즉시 화면에 보여준다.
에러 핸들링 방식은 유사하지만 세부 구현에 차이가 있다. 두 라이브러리 모두 쿼리 결과 객체 내에 isError, error 상태를 제공한다. TanStack Query는 전역 수준에서 에러를 처리할 수 있는 onError 콜백이나 에러 바운더리와의 통합을 위한 더 많은 옵션을 제공한다. 또한, 뮤테이션 실패 시 자동 재시도나 낙관적 업데이트 롤백과 같은 고급 기능을 내장하고 있다. SWR은 에러 재시도 전략을 기본적으로 포함하고 있으며, onErrorRetry 옵션을 통해 사용자 정의가 가능하다. 에러 처리 자체는 더 직관적이고 간단한 API에 초점을 맞추는 경향이 있다.
비교 항목 | TanStack Query | SWR |
|---|---|---|
데이터 패칭 메커니즘 |
|
|
캐싱 철학 | 중앙 집중식, 구조화된 캐시 관리 | 가볍고 분산된, 훅 기반 캐시 공유 |
기본 데이터 제공 전략 | 캐시 데이터가 있으면 즉시 제공, 동시에 백그라운드 리프레시[1] | 스태일 데이터 즉시 제공, 이후 재검증(SWR) |
에러 핸들링 | 전역 및 개별 수준의 세밀한 제어 옵션 제공 | 기본 재시도 메커니즘과 간결한 에러 상태 제공 |
TanStack Query는 선언적 쿼리를 사용하여 데이터를 패칭한다. 개발자는 useQuery 훅에 고유한 쿼리 키와 데이터를 가져오는 비동기 함수(페치 함수)를 제공한다. 라이브러리는 이 키를 기반으로 데이터를 자동으로 캐싱하고, 컴포넌트가 마운트되거나 윈도우가 다시 포커스될 때 등 다양한 조건에서 데이터를 자동으로 다시 가져온다([2]). 또한 뮤테이션 후 관련 쿼리를 무효화하여 데이터를 최신 상태로 유지하는 전략을 제공한다.
반면 SWR은 "스태일-와일-리밸리데이트" 전략을 핵심 메커니즘으로 삼는다. 이는 캐시에 있는 데이터(스태일 데이터)를 먼저 반환한 후, 백그라운드에서 재검증 요청을 보내고, 최신 데이터로 교체하는 방식이다. useSWR 훅은 첫 번째 인자로 고유한 키(일반적으로 URL 문자열)를 받으며, 데이터 패칭 로직은 기본적으로 내장된 페처를 사용하거나 사용자가 직접 제공할 수 있다. SWR은 또한 포커스나 네트워크 재연결 시 자동 재검증을 기본으로 지원한다.
두 라이브러리의 주요 차이점은 초기 데이터가 없을 때의 동작과 패칭 트리거에 있다. TanStack Query는 쿼리가 특정 상태(로딩, 성공, 에러)를 명확히 가지며, 초기 데이터가 없으면 로딩 상태를 유지한다. SWR은 스태일 데이터가 없으면 즉시 데이터 패칭을 시도하지만, 동시에 null이나 undefined 데이터를 반환할 수 있어 개발자가 이를 처리해야 한다. 다음 표는 두 방식을 간략히 비교한다.
특징 | TanStack Query | SWR |
|---|---|---|
기본 패칭 방식 | 선언적 쿼리 실행 | 스태일-와일-리밸리데이트 |
초기 상태 | 로딩, 성공, 에러 상태 명확 | 데이터가 없을 수 있음 (별도 처리 필요) |
재검증 트리거 | 윈도우 포커스, 인터벌, 수동 무효화 등 | 윈도우 포커스, 네트워크 복구, 인터벌 등 |
키 형태 | 문자열, 배열, 객체 등 유연한 쿼리 키 | 주로 문자열(URL) |
결론적으로, TanStack Query는 상태 머신에 가까운 체계적인 데이터 패칭 라이프사이클을 제공하는 반면, SWR은 캐시 우선의 빠른 사용자 경험에 초점을 맞춘 즉각적인 데이터 반환 메커니즘을 강조한다.
두 라이브러리는 모두 클라이언트 사이드 캐싱을 통해 불필요한 네트워크 요청을 줄이고 사용자 경험을 향상시키지만, 그 접근 방식에는 차이가 있습니다.
TanStack Query는 구조화된 캐시 키 시스템을 기반으로 합니다. 각 쿼리는 고유한 키로 식별되며, 이 키는 캐시에서 데이터를 저장하고 검색하는 데 사용됩니다. 기본적으로 가져온 데이터는 메모리에 보관되며, 특정 시간(기본값 5분)이 지나면 '만료(stale)' 상태로 표시됩니다. 만료된 데이터는 화면에 즉시 표시되지만, 백그라운드에서 자동으로 재검증(refetch)이 트리거됩니다. 캐시된 데이터의 수명은 cacheTime 옵션으로 제어할 수 있으며, 이 기간 동안은 비활성 쿼리 데이터도 메모리에 유지되어 빠른 재접속 시 사용할 수 있습니다. 또한, 뮤테이션이 성공적으로 완료되면 관련 쿼리의 데이터를 자동으로 무효화하여 최신 데이터를 다시 가져오도록 할 수 있습니다.
반면, SWR은 이름에서 유래한 스태일-와일-리밸리데이트 전략을 핵심 철학으로 삼습니다. 이는 데이터를 일단 캐시에서 즉시 보여준 후(Stale), 백그라운드에서 재검증 요청(Revalidate)을 보내 최신 상태로 갱신하는 방식입니다. 캐시 키는 일반적으로 문자열이지만, 함수나 배열도 사용할 수 있습니다. 재검증 동작은 매우 세밀하게 제어할 수 있으며, 포커스 복귀 시, 네트워크 재연결 시, 정기적인 간격으로 등의 조건에 따라 자동으로 트리거됩니다. SWR은 또한 로컬 뮤테이션을 통해 서버에 요청을 보내기 전에 캐시 데이터를 낙관적으로 업데이트하여 반응성을 높이는 기능을 제공합니다.
특징 | TanStack Query | SWR |
|---|---|---|
캐시 키 | 구조화된 배열 또는 문자열 | 주로 문자열, 배열 가능 |
기본 재검증 트리거 | 쿼리가 다시 활성화될 때, 윈도우 포커스 시 등 | 포커스, 네트워크 복구, 정기 간격 등 |
데이터 무효화 |
|
|
글로벌 상태 관리 | 별도의 상태 관리 라이브러리와 통합 필요 | 전역 상태 공유를 위한 |
TanStack Query와 SWR은 모두 비동기 데이터 패칭 과정에서 발생할 수 있는 에러를 구조적으로 처리할 수 있는 기능을 제공한다. 두 라이브러리 모두 쿼리의 상태(isError, error)를 반환하여 컴포넌트 레벨에서 에러 상황을 쉽게 감지하고 UI에 반영할 수 있게 한다. 또한 전역적인 에러 콜백을 설정하는 방법을 지원하여, 중앙에서 일관된 에러 처리 로직(예: 에러 모달 표시, 로깅)을 적용할 수 있다.
구체적인 접근 방식에는 차이가 존재한다. TanStack Query는 useQuery와 useMutation 훅의 onError 콜백 옵션을 통해 개별 쿼리나 뮤테이션 수준에서 에러를 처리할 수 있다. 더 나아가, QueryClient를 생성할 때 defaultOptions를 설정하거나 QueryCache와 MutationCache에 전역 에러 리스너를 부착하여 애플리케이션 전체의 에러를 한 곳에서 관리하는 체계를 갖추고 있다. 반면 SWR은 useSWR 훅의 onError 옵션으로 개별 요청의 에러를 처리하며, <SWRConfig>의 공급자(provider)를 통해 전역 설정을 공유할 수 있다.
에러 발생 후의 재시도(retry) 정책도 중요한 부분이다. 두 라이브러리 모두 기본적인 재시도 로직을 내장하고 있으며, 재시도 횟수, 재시도 간 지연 시간, 특정 에러 타입에 따른 재시도 여부 등을 세밀하게 구성할 수 있다. TanStack Query의 경우 네트워크 장애 시 지수 백오프(exponential backoff) 전략을 기본적으로 적용하는 반면, SWR은 보다 단순한 간격으로 재시도한다[3]. 개발자는 이러한 재시도 동작을 필요에 맞게 조정하거나 완전히 비활성화할 수 있다.

TanStack Query는 쿼리와 뮤테이션이라는 두 가지 핵심 개념을 중심으로 데이터 패칭과 상태 관리를 구조화한다. 쿼리는 서버로부터 데이터를 가져오는 읽기 작업에 사용되며, useQuery 훅을 통해 구현된다. 이 훅은 고유한 키, 데이터를 패칭하는 비동기 함수, 그리고 캐싱, 재시도, 자동 리페칭과 관련된 다양한 옵션을 인자로 받는다. 반면, 뮤테이션은 서버의 데이터를 생성, 수정, 삭제하는 쓰기 작업에 사용되며, useMutation 훅으로 관리된다. 뮤테이션은 성공, 에러, 로딩 상태를 제공하며, 성공 후 관련 쿼리를 자동으로 무효화하고 리페치하여 클라이언트 상태를 서버 상태와 일관되게 유지하는 데 중요한 역할을 한다.
인피니트 쿼리와 페이지네이션 쿼리는 대량의 데이터를 효율적으로 처리하는 기능이다. useInfiniteQuery 훅은 무한 스크롤이나 '더 보기' 버튼을 구현할 때 유용하다. 이 훅은 데이터를 페이지 단위로 가져오며, 마지막으로 패치한 페이지의 정보를 기반으로 다음 페이지를 요청하는 로직을 쉽게 구성할 수 있다. 각 페이지의 데이터는 배열로 누적되어 관리되며, fetchNextPage와 hasNextPage 같은 메서드를 통해 직관적인 네비게이션이 가능하다.
프리페칭은 사용자 경험을 크게 향상시키는 기능이다. 데이터가 필요해지기 전에 미리 캐시에 로드해 둠으로써, 사용자가 실제로 해당 데이터를 요청할 때 즉시 표시할 수 있다. TanStack Query는 queryClient.prefetchQuery 메서드를 제공한다. 예를 들어, 사용자가 목록 페이지에 머물 때, 상세 페이지의 데이터를 미리 패칭하거나, 마우스가 링크 위에 호버될 때 관련 데이터를 프리페치하도록 설정할 수 있다. 이는 네트워크 사용을 효율적으로 만들고, 로딩 상태를 최소화한다.
기능 | 설명 | 사용 훅/메서드 |
|---|---|---|
데이터 패칭 (읽기) | 서버로부터 데이터를 조회한다. |
|
데이터 변경 (쓰기) | 서버의 데이터를 생성, 수정, 삭제한다. |
|
무한 로딩/페이지네이션 | 페이지 단위로 데이터를 연속적으로 패칭한다. |
|
데이터 프리페칭 | 미래에 필요할 데이터를 미리 캐시에 로드한다. |
|
TanStack Query에서 쿼리는 서버로부터 데이터를 가져오는(fetch) 모든 비동기 작업을 의미합니다. 주로 GET 요청에 해당하며, 데이터를 읽는 데 사용됩니다. 쿼리는 고유한 키(일반적으로 문자열 또는 배열)로 식별되며, 이 키는 캐싱과 리밸리데이션의 기준이 됩니다. useQuery 훅을 사용하여 쿼리를 정의하고, 로딩 상태, 에러 상태, 성공적으로 가져온 데이터에 접근할 수 있습니다. 쿼리는 자동으로 캐시에 저장되며, 동일한 키를 가진 후속 요청은 캐시된 데이터를 우선적으로 반환합니다.
반면에 뮤테이션은 서버의 데이터를 생성, 수정, 삭제하는 작업을 의미합니다. 주로 POST, PUT, PATCH, DELETE 요청에 해당합니다. useMutation 훅을 사용하여 정의하며, 쿼리와 달리 고유 키가 필수는 아닙니다. 뮤테이션은 실행 함수(mutate)를 반환하며, 이 함수를 호출해야 실제 요청이 시작됩니다. 뮤테이션의 주요 특징은 관련된 쿼리의 무효화와 자동 리페칭을 쉽게 트리거할 수 있다는 점입니다. 예를 들어, 게시글을 생성하는 뮤테이션이 성공한 후, 게시글 목록을 보여주는 쿼리를 무효화하면 목록이 자동으로 최신 상태로 갱신됩니다.
쿼리와 뮤테이션의 상태 및 사이드 이펙트 관리 방식은 다음과 같이 비교할 수 있습니다.
특징 | 쿼리 (Query) | 뮤테이션 (Mutation) |
|---|---|---|
주요 목적 | 데이터 가져오기 (Fetch) | 데이터 변경하기 (Create, Update, Delete) |
사용 훅 |
|
|
실행 시점 | 컴포넌트 마운트 시 자동 실행 가능 | 명시적 함수 호출( |
고유 키 | 필수. 캐싱의 기준. | 필수 아님. |
캐싱 | 자동 캐싱 및 관리 | 결과는 기본적으로 캐시되지 않음 |
연관 작업 | - | 성공/실패 시 관련 쿼리 무효화 및 리페칭 트리거 |
이러한 분리는 관심사 분리 원칙을 따르며, 애플리케이션의 데이터 흐름을 선언적이고 예측 가능하게 만듭니다. 개발자는 데이터를 어떻게 가져올지(쿼리)와 어떻게 변경할지(뮤테이션)를 명확히 정의하고, 라이브러리가 캐시 일관성과 UI 상태 동기화를 관리하도록 위임할 수 있습니다.
TanStack Query의 인피니트 쿼리는 페이지네이션 또는 무한 스크롤을 구현할 때 사용하는 특수한 쿼리 타입이다. 기존의 쿼리가 단일 데이터 세트를 가져오는 데 중점을 둔다면, 인피니트 쿼리는 사용자가 스크롤하거나 '더 보기' 버튼을 클릭할 때 추가 페이지의 데이터를 연속적으로 가져오는 데 최적화되어 있다. useInfiniteQuery 훅을 통해 구현되며, 각 페이지의 데이터를 배열로 관리하고, 다음 페이지를 요청할 수 있는 함수를 제공한다.
인피니트 쿼리의 핵심은 getNextPageParam과 fetchNextPage 함수에 있다. getNextPageParam 함수는 마지막으로 가져온 페이지의 데이터를 분석하여 다음 페이지를 요청하는 데 필요한 파라미터(예: 페이지 번호, 커서)를 반환한다. 개발자는 이 함수를 정의하여 API의 페이지네이션 로직에 맞춰야 한다. 사용자가 다음 데이터를 요청하면, fetchNextPage 함수가 호출되어 getNextPageParam에서 반환된 파라미터로 새로운 요청을 수행한다. 가져온 데이터는 data.pages 배열에 누적되어 저장된다.
이 기능은 성능과 사용자 경험을 크게 향상시킨다. 필요한 시점에만 데이터를 지연 로딩하여 초기 로딩 시간을 단축하고, 불필요한 네트워크 요청을 줄인다. 또한, 가져온 모든 페이지 데이터를 자동으로 캐싱하고 통합하여 관리하므로, 캐시 무효화나 데이터 재동기화 시 일관성을 유지하기 쉽다. 다음은 인피니트 쿼리의 기본적인 사용 패턴을 보여주는 코드 구조이다.
```javascript
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam = 1 }) => fetchProjects(pageParam),
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
});
```
위 예시에서 hasNextPage는 getNextPageParam이 undefined를 반환하지 않는 한 true를 유지하며, isFetchingNextPage는 다음 페이지를 가져오는 중인지 여부를 나타낸다. 이를 통해 UI에서 로딩 상태를 표시하거나, 더 이상 로드할 데이터가 없을 때 '더 보기' 버튼을 비활성화하는 등의 로직을 구현할 수 있다.
TanStack Query의 프리페칭 기능은 사용자가 특정 데이터를 필요로 하기 전에 미리 해당 데이터를 가져와 캐시에 저장하는 전략이다. 이는 사용자 경험을 크게 향상시키는 데 목적이 있다. 예를 들어, 사용자가 목록에서 특정 항목을 클릭하여 상세 페이지로 이동할 가능성이 높을 때, 해당 상세 데이터를 미리 불러오면 페이지 전환 시 로딩 지연 없이 즉시 데이터를 표시할 수 있다.
프리페칭은 주로 prefetchQuery 또는 prefetchInfiniteQuery 메서드를 통해 실행된다. 이 메서드들은 쿼리 키와 패칭 함수를 인자로 받아 데이터를 가져온 후 캐시에 저장한다. 프리페칭된 데이터는 이후 동일한 쿼리 키로 데이터를 요청하는 컴포넌트에서 즉시 사용 가능하다. 데이터의 신선도는 해당 쿼리에 설정된 staleTime에 따라 관리된다.
프리페칭을 구현하는 일반적인 접근법은 다음과 같다.
구현 방식 | 설명 | 사용 예시 |
|---|---|---|
사용자 상호작용 시 | 마우스 호버나 버튼 클릭과 같은 이벤트에 바인딩한다. | 링크에 마우스를 올리면 해당 페이지 데이터를 프리페칭한다. |
컴포넌트 마운트 시 |
| 대시보드 컴포넌트가 로드될 때 주요 위젯의 데이터를 프리페칭한다. |
라우터 통합 | React Router나 Next.js의 라우터 이벤트를 활용한다. | Next.js의 |
이러한 프리페칭은 데이터 접근성을 예측할 수 있는 상황에서 매우 효과적이다. 그러나 과도하게 사용하면 불필요한 네트워크 요청을 초래하여 서버에 부하를 줄 수 있다. 따라서 사용자의 실제 행동 패턴을 분석하여 프리페칭을 적용할 데이터와 시기를 신중하게 결정하는 것이 중요하다.

SWR은 데이터 패칭 라이브러리로서, 스태일-와일-리밸리데이트(Stale-While-Revalidate) 전략을 핵심 철학으로 삼는다. 이 전략은 캐시된 데이터(스태일 데이터)를 즉시 반환한 뒤, 백그라운드에서 재검증 요청을 보내고 최신 데이터로 캐시를 업데이트하는 방식이다. 이를 통해 사용자는 빠르게 화면을 볼 수 있고, 데이터는 최신 상태로 유지된다. SWR은 이 전략을 기반으로 자동 재검증, 의존성 쿼리, 로컬 뮤테이션 등 실용적인 기능들을 제공한다.
주요 기능 중 하나는 다양한 조건에서의 자동 재검증이다. SWR은 다음과 같은 상황에서 데이터를 자동으로 재검증한다.
재검증 트리거 | 설명 |
|---|---|
포커스 복귀 | 사용자가 다른 탭에서 돌아오거나 창을 다시 포커스할 때 |
네트워크 재연결 | 오프라인 상태에서 온라인으로 전환될 때 |
정기적 간격 |
|
이러한 자동 재검증은 데이터의 신선도를 보장하는 데 큰 도움을 준다.
의존성 쿼리는 여러 쿼리 간의 실행 순서를 관리할 수 있는 기능이다. 한 쿼리의 결과가 다른 쿼리의 실행에 필요한 매개변수가 될 때 유용하게 사용된다. 예를 들어, 사용자 정보를 가져온 후 그 userId를 사용해 프로필 정보를 가져오는 경우, 두 번째 쿼리는 enabled 옵션을 통해 첫 번째 쿼리의 성공 여부에 의존하도록 설정할 수 있다. 이를 통해 불필요한 에러 요청을 방지하고 논리적인 데이터 패칭 흐름을 구성할 수 있다.
로컬 뮤테이션은 서버에 요청을 보내기 전에 클라이언트의 캐시 데이터를 즉시 업데이트하는 낙관적 UI 업데이트를 가능하게 한다. 사용자 경험을 매우 빠르게 만들어준다. 이후 실제 서버 요청이 성공하면 데이터가 정확히 동기화되고, 실패할 경우 자동으로 이전의 올바른 데이터로 롤백된다. 이 기능은 좋아요, 팔로우, 폼 제출과 같은 상호작용에 특히 효과적이다.
SWR의 핵심 철학은 스태일-와일-리밸리데이트(Stale-While-Revalidate) 전략에 기반한다. 이는 캐시된 데이터(스태일 데이터)가 최신 상태가 아니더라도(Stale) 즉시 사용자에게 보여주고, 백그라운드에서 새로운 데이터를 가져와(Revalidate) 캐시를 갱신하는 접근 방식이다. 이를 통해 사용자는 데이터를 기다리는 지연 시간 없이 빠르게 화면을 볼 수 있으며, 이후에 자동으로 최신 데이터로 업데이트된다.
구현 메커니즘은 다음과 같다. 컴포넌트가 마운트되거나 포커스를 다시 얻을 때, SWR은 먼저 캐시에서 해당 키의 데이터를 반환한다. 동시에 백그라운드에서 패칭 요청을 시작하여 서버에 최신 데이터를 요청한다. 요청이 완료되면 캐시가 갱신되고, 컴포넌트는 새로운 데이터로 다시 렌더링된다. 이 과정은 revalidateOnMount, revalidateOnFocus, revalidateOnReconnect 같은 옵션을 통해 세밀하게 제어할 수 있다.
이 전략의 주요 장점은 사용자 경험의 극대화에 있다. 애플리케이션은 느린 네트워크 상황에서도 캐시 덕분에 즉시 반응하며, 서버 부하를 줄이는 데도 기여한다. 불필요한 로딩 상태를 최소화하면서도 데이터의 최신성을 유지할 수 있는 효율적인 절충안을 제공한다.
의존성 쿼리는 SWR에서 제공하는 기능으로, 특정 쿼리의 실행이 다른 쿼리의 데이터 결과에 의존하도록 설정하는 것을 말한다. 이를 통해 비동기 데이터 패칭의 순차적 흐름이나 조건부 실행을 쉽게 구현할 수 있다.
의존성 쿼리는 useSWR 훅의 key 매개변수를 함수 형태로 전달하거나, enabled 옵션을 사용하여 구성한다. 예를 들어, 사용자 프로필을 먼저 패칭한 후, 해당 프로필의 ID를 사용하여 추가적인 데이터(예: 주문 내역)를 패칭해야 하는 경우에 유용하다. 첫 번째 쿼리의 데이터가 성공적으로 로드되지 않으면, 두 번째 쿼리는 자동으로 실행되지 않거나 null 키를 가진 상태로 대기한다.
이 접근 방식은 여러 쿼리 간의 명확한 의존 관계를 선언적으로 표현함으로써 코드의 가독성을 높이고, 불필요한 네트워크 요청을 방지한다. 또한 React의 컴포넌트 상태를 사용하여 복잡한 조건부 로직을 관리할 필요가 줄어든다. SWR의 스태일-와일-리밸리데이트 전략과 결합되면, 의존성 데이터가 변경될 때 자동으로 연쇄적인 재검증이 이루어져 데이터의 일관성을 유지하는 데 도움이 된다.
SWR의 로컬 뮤테이션은 서버에 실제 요청을 보내기 전에, 낙관적 업데이트를 통해 클라이언트의 캐시 데이터를 즉시 변경하는 기능이다. 사용자 경험을 향상시키기 위해, 데이터 변경 요청에 대한 서버 응답을 기다리지 않고 UI를 먼저 업데이트한다. 이는 mutate 함수를 사용하여 수행되며, 첫 번째 인자로 새로운 데이터를, 두 번째 인자로 낙관적 업데이트 여부를 나타내는 optimisticData 옵션을 전달할 수 있다.
로컬 뮤테이션의 동작 흐름은 다음과 같다. 먼저, mutate 함수가 호출되면 optimisticData로 지정된 값으로 관련 쿼리의 캐시가 즉시 갱신되고 컴포넌트가 리렌더링된다. 동시에, 백그라운드에서 실제 서버에 데이터 변경 요청(예: POST 또는 PUT 요청)이 전송된다. 서버 요청이 성공하면, 서버에서 반환된 실제 데이터로 캐시를 재검증하여 최종적으로 일치시킨다. 만약 요청이 실패하면, 자동으로 이전의 올바른 캐시 데이터로 롤백된다.
이 기능을 효과적으로 사용하기 위한 주요 옵션은 아래와 같다.
옵션 | 설명 |
|---|---|
| 서버 응답 전에 UI에 즉시 표시할 데이터. |
| 요청 실패 시 자동 롤백 여부를 결정한다. 기본값은 |
| 뮤테이션 성공 후 관련 데이터를 재검증할지 여부를 결정한다. |
로컬 뮤테이션은 댓글 작성, 좋아요 토글, 폼 제출과 같이 사용자 인터랙션이 빈번한 UI에서 반응성을 극대화하는 데 유용하다. 그러나 서버 상태와 클라이언트 상태가 일시적으로 불일치할 수 있으므로, 에러 발생 시 사용자에게 적절한 피드백을 제공하는 것이 중요하다.

두 라이브러리는 모두 불필요한 렌더링과 네트워크 요청을 줄여 애플리케이션 성능을 최적화하는 데 중점을 둔다. TanStack Query는 쿼리 키를 기반으로 한 정교한 추적 시스템을 통해, 특정 쿼리와 연결된 컴포넌트만 데이터 변경 시 리렌더링되도록 한다. 이는 메모이제이션과 함께 작동하여 깊은 컴포넌트 트리에서도 불필요한 렌더링을 효과적으로 차단한다. SWR 또한 useSWR 훅을 사용하는 컴포넌트에 대한 렌더링 제어를 제공하며, 데이터가 실제로 변경되었을 때만 관련 컴포넌트를 갱신한다.
네트워크 요청 최소화 측면에서 두 라이브러리의 캐싱 전략이 핵심 역할을 한다. TanStack Query는 인메모리 캐시에 저장된 데이터를 기본으로 제공하며, staleTime 설정을 통해 일정 시간 동안은 캐시된 데이터를 신선한 것으로 간주하여 백그라운드 재검증 없이 즉시 반환한다. 또한 프리페칭 기능을 활용하면 사용자가 특정 데이터가 필요해지기 전에 미리 불러와 캐시에 저장할 수 있어, 사용자 경험과 성능을 동시에 향상시킨다.
SWR의 핵심 최적화 전략은 스태일-와일-리밸리데이트 원칙이다. 이는 캐시된 데이터를 즉시 보여준 후(Stale), 백그라운드에서 재검증 요청(Revalidate)을 보내 최신 상태로 동기화하는 방식이다. 이를 통해 사용자는 빠르게 콘텐츠를 확인할 수 있고, 애플리케이션은 네트워크 사용량을 줄이면서도 데이터 일관성을 유지한다. 또한 포커스 리밸리데이션이나 인터벌 폴링 같은 기능을 통해 조건부로 데이터를 갱신하여 불필요한 요청을 추가로 줄인다.
최적화 영역 | TanStack Query 접근 방식 | SWR 접근 방식 |
|---|---|---|
렌더링 제어 | 쿼리 키 의존성 추적 및 메모이제이션 | 훅 수준의 데이터 변경 감지 |
캐시 활용 | 명시적 staleTime 설정, 구조적 공유[4] | SWRConfig를 통한 전역 재검증 전략 설정 |
요청 최소화 | 프리페칭, 자동 가비지 컬렉션 | 조건부 재검증(포커스, 네트워크 복구 시) |
두 라이브러리는 불필요한 리렌더링을 줄이고 UI 반응성을 높이기 위해 다양한 렌더링 최적화 전략을 제공한다.
TanStack Query는 쿼리 키를 기반으로 한 세밀한 구독 모델을 사용한다. 컴포넌트는 정확히 자신이 의존하는 쿼리 데이터의 변화에만 반응하여 리렌더링된다. 또한 기본적으로 구조적 공유를 통해 데이터의 참조 안정성을 유지하여, 실제 데이터 내용이 변경되지 않았을 경우 메모이제이션된 컴포넌트의 불필요한 리렌더링을 방지한다. 인피니트 쿼리나 페이지네이션을 사용할 때도 이전에 가져온 페이지 데이터는 안정적인 참조를 유지한다.
SWR은 스태일-와일-리밸리데이트 전략 덕분에 초기 로딩 상태 없이 즉시 캐시된 데이터를 렌더링할 수 있어 사용자 경험을 향상시킨다. useSWR 훅은 동일한 키를 가진 여러 요청을 자동으로 중복 제거하고 하나의 네트워크 요청으로 처리하며, 그 결과를 모든 구독 컴포넌트에 공유한다. 로컬 뮤테이션을 통해 낙관적 업데이트를 수행하면, 서버 응답을 기다리지 않고도 UI를 즉시 업데이트하여 반응성을 높인다.
두 라이브러리는 불필요한 네트워크 요청을 줄여 애플리케이션 성능을 향상시키는 전략을 제공합니다. TanStack Query는 쿼리 키를 기반으로 한 중앙 집중식 캐시 관리와 스마트 리페칭 정책을 통해, 동일한 데이터에 대한 중복 요청을 자동으로 통합하고 캐시된 데이터를 우선적으로 제공합니다. 또한 프리페칭 기능을 통해 사용자가 필요로 할 데이터를 미리 불러와 캐시에 저장함으로써, 실제 데이터 요청 시 네트워크 호출을 생략할 수 있습니다.
SWR은 이름의 유래가 된 스태일-와일-리밸리데이트 전략을 핵심으로 합니다. 이 전략은 캐시된 데이터(스태일 데이터)를 즉시 반환한 후, 백그라운드에서 재검증(리밸리데이트) 요청을 보내 네트워크 사용을 최적화합니다. 또한 포커스 리밸리데이트나 인터벌 리밸리데이트와 같은 옵션을 통해 데이터 새로고침 주기를 세밀하게 제어할 수 있어, 과도한 폴링을 방지합니다.
두 도구 모두 다음과 같은 공통적인 방법으로 네트워크 요청을 최소화합니다.
전략 | 설명 | 효과 |
|---|---|---|
캐시 우선 제공 | 유효한 캐시 데이터가 있으면 이를 먼저 보여주고 백그라운드에서 갱신 | 사용자 경험 향상, 불필요한 로딩 상태 감소 |
요청 중복 제거 | 동일한 시점에 발생하는 같은 쿼리 키의 요청을 단일 요청으로 병합 | 서버 부하 감소, 대역폭 절약 |
조건부 패칭 | 데이터가 이미 존재하거나 특정 조건이 만족되지 않으면 요청을 보내지 않음 | 완전히 불필요한 요청 방지 |
이러한 메커니즘은 특히 데이터 변경 빈도가 낮거나, 동일한 데이터를 여러 컴포넌트에서 요구하는 복잡한 애플리케이션에서 네트워크 트래픽과 서버 부하를 현저히 줄여줍니다.

TanStack Query는 복잡한 서버 상태 관리가 필요한 대규모 애플리케이션에 적합합니다. 백엔드 API가 복잡하거나, 데이터의 실시간 동기화, 낙관적 업데이트, 페이징 및 인피니트 스크롤 구현이 중요한 경우에 강점을 보입니다. 특히 뮤테이션 후 관련 쿼리의 자동 재검증, 강력한 디바운싱 및 쓰로틀링 지원, 그리고 포괄적인 타입스크립트 지원을 필요로 하는 프로젝트에서 선호됩니다.
반면, SWR은 상대적으로 단순한 데이터 패칭 요구사항을 가진 중소규모 프로젝트나, 빠른 프로토타이핑에 유리합니다. 기본 제공되는 스태일-와일-리밸리데이트 전략 덕분에 데이터의 신선도를 유지하는 데 초점을 맞추며, 설정이 간단하고 학습 곡선이 낮은 편입니다. Next.js 생태계와의 긴밀한 통합을 원하거나, 가볍고 직관적인 클라이언트 사이드 캐싱 솔루션이 필요할 때 선택할 수 있습니다.
프로젝트의 요구사항과 팀의 숙련도를 고려하여 선택하는 것이 중요합니다. 아래 표는 두 라이브러리의 주요 적합성을 비교한 것입니다.
고려 사항 | TanStack Query 선호 시나리오 | SWR 선호 시나리오 |
|---|---|---|
애플리케이션 규모/복잡도 | 대규모, 복잡한 서버 상태 관리 | 중소규모, 비교적 단순한 데이터 패칭 |
필요한 기능 | 강력한 뮤테이션, 인피니트 쿼리, 고급 캐싱 전략 | 기본적인 패칭, 자동 재검증, 빠른 설정 |
프레임워크 선호도 | 프레임워크에 구애받지 않음 | Next.js 프로젝트와의 긴밀한 통합 |
학습 곡선 | 상대적으로 높음, 기능이 풍부함 | 상대적으로 낮음, 간결하고 직관적임 |
번들 크기 민감도 | 덜 민감함 | 매우 민감함, 가벼운 솔루션을 원할 때 |
TanStack Query는 복잡한 서버 상태 관리가 필요한 대규모 애플리케이션에 특히 적합하다. 서버 데이터의 캐싱, 동기화, 업데이트를 포괄적으로 관리해야 하는 경우 강력한 이점을 발휘한다. 뮤테이션 API가 풍부하여 데이터 생성, 수정, 삭제 후의 캐시 무효화 및 자동 재요청 전략을 세밀하게 구성할 수 있다. 이는 대시보드, 관리자 패널, 실시간 데이터가 중요한 e-커머스 플랫폼과 같은 프로젝트에 유용하다.
다음과 같은 구체적인 요구사항이 있을 때 TanStack Query를 선택하는 것이 바람직하다.
요구사항 | 설명 |
|---|---|
복잡한 데이터 의존성 | 여러 쿼리 결과를 조합하거나, 순차적/병렬적 패칭이 필요한 경우 |
낙관적 업데이트 | 사용자 경험을 위해 서버 응답을 기다리지 않고 UI를 먼저 업데이트해야 하는 경우 |
백그라운드 데이터 동기화 | 윈도우 포커스 변경, 네트워크 재연결 시 데이터를 자동으로 최신 상태로 유지해야 하는 경우 |
페이지네이션/무한 스크롤 | 인피니트 쿼리를 통한 구조화된 페이지네이션 지원이 필요한 경우 |
고도화된 프리페칭 | 사용자 상호작용을 예측하여 데이터를 미리 캐시에 로드해야 하는 경우 |
또한, TypeScript와의 통합이 뛰어나고, 공식 문서와 커뮤니티가 매우 활성화되어 있어 문제 해결과 학습에 용이하다. React 외에도 Solid.js, Svelte, Vue와 같은 다른 프레임워크를 공식적으로 지원하므로, 기술 스택이 다양하거나 변경될 가능성이 있는 프로젝트에도 적합하다.
SWR은 비교적 단순한 데이터 패칭 요구사항, 빠른 프로토타이핑, 그리고 Next.js 애플리케이션과의 긴밀한 통합이 필요한 경우에 특히 적합하다. 기본 제공되는 스태일-와일-리밸리데이트 전략 덕분에 사용자 경험을 저해하지 않으면서도 데이터를 자동으로 최신 상태로 유지하는 것이 핵심 목표일 때 강점을 발휘한다.
애플리케이션의 데이터 구조가 복잡하지 않고, CRUD 연산 중 'Read'(조회)에 더 중점을 두며, 실시간성이 크게 요구되지 않는 프로젝트에 잘 어울린다. 예를 들어, 블로그 포스트 목록, 사용자 프로필 정보, 설정 데이터 등을 패칭하는 경우가 이에 해당한다. 또한, 로컬 뮤테이션 기능을 통해 서버 응답을 기다리지 않고도 UI를 즉시 낙관적으로 업데이트할 수 있어 사용자에게 빠른 반응성을 제공할 수 있다.
고려 사항 | SWR이 적합한 이유 |
|---|---|
프로젝트 규모 | 중소규모 프로젝트 또는 기능 범위가 명확한 마이크로 프론트엔드 |
데이터 복잡도 | 계층 구조가 단순하고, 서로 다른 쿼리 간의 복잡한 의존성이 적은 데이터 |
개발 속도 | 간결한 API와 낮은 학습 곡선으로 빠른 개발과 도입이 필요할 때 |
에코시스템 | |
최신성 전략 | 포커스 시 또는 정기적인 간격으로 데이터를 자동 재검증하는 기본 전략으로 충분할 때 |
TanStack Query에 비해 번들 크기가 상대적으로 작은 점도 고려할 만한 장점이다. 따라서, 복잡한 서버 상태 관리보다는 클라이언트 사이드에서의 효율적인 데이터 캐싱과 HTTP 요청 최적화에 초점을 맞추고자 하는 팀에게 좋은 선택지가 될 수 있다.

TanStack Query와 SWR 간의 마이그레이션은 두 라이브러리의 철학과 API가 유사한 점이 많아 비교적 수월한 편이다. 그러나 몇 가지 핵심적인 차이점을 이해하고 계획적으로 접근해야 한다. 가장 큰 변화는 TanStack Query가 쿼리 키를 배열로 관리하며, 뮤테이션이 쿼리와 분리된 독립적인 개념이라는 점이다. 또한 캐싱 동작과 에러 핸들링의 디테일에서 차이가 발생할 수 있다.
마이그레이션을 시작할 때는 점진적인 접근이 권장된다. 한 번에 전체 애플리케이션을 변경하기보다, 특정 기능 모듈이나 페이지 단위로 마이그레이션을 진행하는 것이 위험을 줄인다. 주요 변경 사항을 비교한 표는 다음과 같다.
개념 | SWR | TanStack Query | 주의사항 |
|---|---|---|---|
쿼리 키 | 문자열 또는 배열 | 반드시 배열 | SWR의 배열 키는 TanStack Query와 호환된다. |
데이터 패칭 함수 |
|
|
|
뮤테이션 |
|
| TanStack Query는 서버 상태 업데이트 후 관련 쿼리 무효화를 권장한다. |
에러 재시도 | 기본 재시도 동작 존재 | 보다 세분화된 재시도 설정 가능 | 동작이 다를 수 있어 테스트가 필요하다. |
캐시 공급자 | 자체 제공 |
| 애플리케이션 루트를 |
마이그레이션 후에는 성능과 동작을 꼼꼼히 테스트해야 한다. 인피니트 쿼리나 옵티미스틱 업데이트 같은 고급 기능을 사용했다면, 해당 API의 구현 방식이 상이할 수 있다. 또한 TanStack Query의 디브툴즈를 활용하면 캐시 상태를 시각적으로 확인하며 디버깅하는 데 큰 도움이 된다. 최종적으로는 네트워크 요청 수, 불필요한 리렌더링, 사용자 경험에 변화가 없는지 종합적으로 검증하는 과정이 필수적이다.
