
이번글에서는 React에서 서버측 데이터 캐싱할 때 자주사용되는 React-Query에 대해 알아보겠습니다.
https://tanstack.com/query/v5/docs/framework/react/overview
TanStack | High Quality Open-Source Software for Web Developers
Headless, type-safe, powerful utilities for complex workflows like Data Management, Data Visualization, Charts, Tables, and UI Components.
tanstack.com
공식문서에 소개된 내용중에 React-Query를 사용하면 좋은점에 대해 소개되어있습니다.
1. 애플리케이션에서 복잡하고 오해받기 쉬운 많은 줄의 코드를 제거하고 React 쿼리 로직을 몇 줄만 사용하여 대체하는 데 도움을 드립니다 .
2. 새로운 서버 상태 데이터 소스를 연결하는 것에 대해 걱정하지 않고도 애플리케이션을 보다 쉽게 유지 관리하고 새로운 기능을 빌드할 수 있도록 하세요.
3. 이전보다 훨씬 더 빠르고 반응성이 뛰어난 애플리케이션을 만들어 최종 사용자에게 직접적인 영향을 미치세요.
4. 대역폭을 절약하고 메모리 성능을 높이는 데 도움이 될 수 있습니다.
그럼 이제 React-Query를 사용했을때 이점이 되는 기능을 소개해볼게요.
1. 캐싱
React-Query는 캐싱을 통해 동일한 데이터에 대한 반복적인 비동기 데이터 호출을 방지하고, 이는 불필요한 API 호출을 줄여 서버에 대한 부하를 줄이는 좋은 결과를 가져옵니다.

React-Query를 사용하지 않을 경우 데이터의 요청 흐름을 나타낸것인데요, 기존에는 비동기 요청을 보내고 해당 데이터를 받아오기 전까지 로딩 상태를 보여줍니다. 그리고 데이터를 받아오면 로딩화면이 새로운 데이터로 변경되어 표현됩니다.

하지만 Reafct-Query를 사용하면 최초에 요청한게 아니라면 로딩화면 대신에 이전에 캐싱된 데이터를 보여주어 사용자 경험에 더 많은 도움을 줄 수 있다고 생각합니다. (하루종일 돌아가는 스피너 대신 캐싱된 데이터룰 보여주는게 더 낫다고 판단한것 같음)
최신 데이터 판별
서버 데이터를 캐싱을 햇지만, 만약 이 데이터가 최신데이터가 아닐경우인지는 어떻게 판단할까요 ?
React-Query에서는 최신의 데이터를 fresh한 데이터, 기존의 데이터를 stale한 데이터라고 말합니다.
우선 데이터의 갱신 시점을 알아야 언제 패치할 수 있을지 알 것입니다. 데이터를 갱신하는 시점은 아래와 같은데요,.
- 화면을 보고 있을 때
- 페이지의 전환이 일어났을 때
- 페이지 전환 없이 이벤트가 발생해 데이터를 요청할 때
크게 보면 위의 3가지로 나눌 수 있습니다.. 이를 위해 React-Query에서는 기본적인 아래의 옵션들을 제공합니다.
refetchOnWindowFocus, //default: true
refetchOnMount, //default: true
refetchOnReconnect, //default: true
staleTime, //default: 0 cacheTime,
브라우저에 포커스가 들어온 경우(refetchOnWindowFocus)
새로운 컴포넌트 마운트가 발생한 경우(refetchOnMount)
네트워크 재연결이 발생한 경우(refetchOnReconnect)
StaleTime과 gcTime

용어 설명
- fresh : 최신 데이터(신선한 값?)
- fetch : 데이터 요청
- stale : 오래된 데이터(썩은 값?)
- inactive : 미사용 상태(unmount 시에)
- delete : 삭제(unmount 이후 gcTime이 지나면 할당 해제)
staleTime
staleTime은 데이터가 fresh → stale 상태로 변경되는 데 걸리는 시간입니다. fresh 상태일 때는 refetch 트리거가 발생해도 refetch가 일어나지 않는다.
기본값이 0이므로 따로 설정해주지 않는다면 Refetch 트리거가 발생했을 때 무조건 Refetch가 발생합니다.
gcTime(cacheTime)
gcTime은 데이터가 inactive한 상태일 때 캐싱된 상태로 남아있는 시간입니다.
특정 컴포넌트가 unmount(페이지 전환 등으로 화면에서 사라질 때) 되면 사용된 데이터는 inactive상태로 바뀌고, 이때 데이터는 gcTime만큼 유지됩니다. gcTime 이후 데이터는 가비지 콜렉터로 수집되어 메모리에서 해제된다.(v4 -> v5로 업데이트 되면서 cacheTime이 gcTime으로 변경됨)
만일 gcTime이 지나지 않았는데 해당 데이터를 사용하는 컴포넌트가 다시 mount되면, 새로운 데이터를 fetch해오는 동안 캐싱된 데이터를 보여줍니다.
캐싱된 데이터를 계속 보여주는게 아니라 fetch하는 동안 이전 데이터를 보여줍니다.
데이터 라이프사이클 흐름의 예시
fetch후에 fresh상태로 전환되고, staleTime이후에 stale 상태로 변경됩니다.
- 여기서 refetch가 일어날 경우에는 다시 새로운 데이터를 받아오니 fresh상태로 변경되고
- refetch가 일어나지 않을 경우(unmount), inactive상태가 되어 gcTime만큼 남아있다가 가비지 컬렉터에 의해 메모리에서 할당 해제
그럼이제 예제 코드를 통해 알아볼게요.
2. 예제
기존에 React-query를 사용하지 않을 경우

Repository에서 데이터를 가져오고, 해당 데이터를 가져올때 try-catch문으로 성공 실패시에 데이터를 바인딩, 또는 에러 메세지를 표시하고, 그 전까지는 로딩여부를 판단하여 보여줍니다.
단순히 데이터를 요청하여 보여주는데에 있어 컴포넌트가 unmount 되면 아까 요청한 데이터 들은 메모리에서 제거될 것이며 매번 컴포넌트가 mount될 경우에 새로운 데이터를 데이터를 요청할것입니다. (그리고 로딩 여부나 에러처리 등을 각각해줘야하기 때문에 귀찮은 부분도 있습니다...)
React-query를 사용할 경우

1. QueryClient 생성

2. QueryClientProvider로 감싸기

위에서 작성한 queryClient를 props로 넣어줍니다.
3. Query 작성

데이터를 요청하는 함수를 useFetchProjectQuery로 이름을 정했고, 이 queryKey를 통해 queryClient에 저장됩니다.
그리고 queryFn에는 어떤 동작을 할 건지 작성해주면되는데 저는 Project의 목록을 가져오는 함수를 작성 하였습니다.
4. UI에 표현

해당 쿼리에서 data, error, Loading여부를 추출하여 해당 내용을 UI에 보여줍니다. 이렇게 hook으로 작성하면 컴포넌트에 종속된 데이터 요청이 아니라 어느 컴포넌트에서든 이 hook을 이용하여 데이터를 요청할 수 있기 때문에 코드의 재사용성이 올라가고 유지보수 측면에서도 이점이 있는거 같습니다.
3. Query, Mutation
React Query는 API의 요청을 Query, Mutation의 두가지로 처리합니다.
Query

Query는 주로 데이터의 단순 조회에서 사용되는데요, queryKey를 통해 데이터를 캐싱하고, 데이터의 공유가 가능합니다.
상태값은 상당히 많은데 , 주로 사용되는 상태값은 { data, error, isLoding, isError } 4가지가 있습니다.
Mutation
Mutation은 서버의 데이터를 변경하는 작업을 처리하는데, POST, PUT, DELETE 등의 작업에 사용됩니다.

4. 후기
이렇게 서버측 데이터를 괸리하는데 용이한 React-Query에 대해 알아보았는데요. 사용하면서 데이터를 조회하는것은 query, 데이터의 수정이 필요할 때에는 mutation을 사용하여 요청을 보낸다는 점에서 GraphQL가 되게 비슷한 느낌을 받았습니다.
'Web > React' 카테고리의 다른 글
[React] 함수형 컴포넌트를 권장하는 이유? (0) | 2025.03.10 |
---|---|
[React] Error Boundary란? (0) | 2024.12.05 |
[React] 프로젝트 관리 시스템 만들기(feat . Supabase) (1) | 2024.11.21 |
[React] 렌더링 과정 알아보기 (6) | 2024.11.12 |
[React] Element란? (1) | 2024.09.05 |