React Query 개념 정리 23.12.15

오늘은 리액트 쿼리에대해 특강을 들었다 그래서 간단하게 개념 정리를 해볼려고 한다
1. React Query란?
서버상태이다 라는 한마디로 정리하기에는 너무 짧아 추가 설명하자면
클라이언트 사이드 상태 및 서버 사이드 상태 2가지로 설명할수있다
클라이언트 사이드 상태 (Client-Side State)
- 정의: 클라이언트 사이드 상태는 사용자의 브라우저 내에서 관리되는 상태입니다. 이는 리액트 컴포넌트의 useState 또는 상태 관리 라이브러리(예: Redux, MobX, Recoil)를 통해 관리됩니다.
- 특징
- 로컬 데이터: 주로 UI 컴포넌트의 상태, 사용자 입력, 토글 상태와 같이 로컬에서만 필요한 데이터를 관리합니다.
- 성능: 네트워크 지연 없이 빠르게 상태를 변경하고 반응할 수 있습니다.
- 수명 주기: 클라이언트 사이드 상태는 페이지를 새로고침하거나 애플리케이션을 종료할 때 초기화되는 경향이 있습니다.
- 예시: 사용자가 입력한 폼 데이터, UI 컴포넌트의 보이기/숨기기 상태 등.
서버 사이드 상태 (Server-Side State)
- 정의: 서버 사이드 상태는 서버에 저장되고 관리되는 데이터를 말합니다. 이는 데이터베이스, 파일 시스템, 또는 서버의 메모리 등에 저장될 수 있습니다.
- 특징:
- 중앙 집중식 데이터: 사용자 정보, 애플리케이션 설정, 대규모 데이터셋 등 여러 세션 또는 사용자 간에 공유되어야 하는 데이터를 관리합니다.
- 지속성: 데이터가 서버에 저장되므로, 클라이언트의 상태가 초기화되거나 변경되어도 영구적으로 유지됩니다.
- 동기화와 일관성: 클라이언트에서 서버로 데이터를 요청하거나 업데이트하면서 발생하는 네트워크 지연과 데이터 동기화에 대한 고려가 필요합니다.
- 예시: 사용자 계정 정보, 애플리케이션의 공유 설정, 대규모 데이터셋 등.
2.탄생 배경과 장점
탄생배경
- 기존 미들웨어의 한계
- 우리는 다른 서버와의 API 통신과 비동기 데이터 관리를 위해 Redux-thunk, Redux-Saga 등 미들웨어를 채택해서 사용할 수 있어요. 하지만 다음과 같은 문제가 있습니다.
- 보일러 플레이트 : 코드량이 너무 많아요.
- 규격화 문제 : Redux가 비동기 데이터 관리를 위한 전문 라이브러리가 아님(규격화 문제)
리액트 쿼리의 장점 (쉽고 책임에서 자유롭다)
- 보일러 플레이트 만들다가 오류날 일이 없어요!
- 내가 만든 부분 아니기 때문에 잘못이 일어난들 내 잘못이 아니에요!
- 사용방법이 기존 thunk 대비 너무 쉽구요, 직관적이에요.
3. 리액트 쿼리 주요개념

fresh
'Fresh' 상태는 데이터가 최신 상태임을 의미합니다. 즉, 최근에 가져온(fetch) 데이터이며 staleTime이 아직 경과하지 않았습니다. 이 상태에서는 추가적인 데이터 요청이 발생해도 리액트 쿼리가 데이터를 재요청하지 않고 현재 캐시된 값을 사용합니다.
☝ **예시**
- 상황: 온라인 쇼핑몰에서 사용자의 장바구니 정보를 표시합니다.
- 구현: **`staleTime`**이 5분으로 설정되어 있습니다.
- 결과: 사용자가 장바구니 페이지를 방문하고 5분이 지나면, 장바구니 데이터는 **`stale`** 상태가 됩니다. 이후 사용자가 장바구니 페이지를 다시 방문하거나 새로고침을 하면, 리액트 쿼리는 백그라운드에서 새로운 장바구니 데이터를 가져와서 표시합니다.
stale
- 'Stale' 상태는 데이터가 더 이상 최신 상태가 아니며 **재검증(revalidation)**이 필요할 수 있는 상태를 의미합니다. **staleTime**이 경과하면 데이터는 자동으로 'stale' 상태가 됩니다. 이 상태에서 쿼리가 다시 실행되면, 리액트 쿼리는 자동으로 백그라운드에서 데이터를 새로고침하여 최신 상태로 유지하려고 시도합니다. → query key
☝ **예시**
- 상황: 뉴스 애플리케이션에서 최신 뉴스 목록을 표시합니다.
- 구현: **`staleTime`**을 10분으로 설정합니다.
- 결과: 사용자가 애플리케이션을 사용하는 동안, 최초 뉴스 목록 로드 후 10분 동안은
새로운 뉴스 요청을 하지 않습니다. 이 시간 동안 사용자가 뉴스 목록을 보거나 새로고침을 해도,
이미 캐시된 데이터를 재사용하여 표시합니다.
staleTime
- 특정 쿼리에 대한 데이터가 '싱싱함(fresh)' 상태로 유지되는 시간을 정의합니다. 즉, 이 시간 동안은 데이터를 다시 가져오지(fetch) 않습니다. 예를 들어, **staleTime**이 5분으로 설정되어 있다면, 데이터는 5분 동안 새로고침되지 않으며 이 기간 동안 여러 번의 요청에서도 동일한 캐시된 데이터를 재사용합니다.
<aside>
☝ **예시**
- 상황: 날씨 정보를 제공하는 앱에서 현재 위치의 날씨를 표시합니다.
- 구현: **`staleTime`**을 30분으로 설정합니다.
- 결과: 사용자가 날씨 정보를 처음 요청하면, 리액트 쿼리는 서버에서 최신 날씨 데이터를 가져옵니다. 이후 30분 동안은 날씨 데이터가 **`fresh`** 상태로 유지됩니다. 이 기간 동안 사용자가 날씨를 다시 확인하려 할 때, 리액트 쿼리는 새로운 요청을 하지 않고 캐시된 데이터를 재사용하여 보여줍니다.
</aside>
staleTime의 옵션 이해
✅ 리액트에서 stale, fresh의 개념을 아는 것이 왜 중요한가요?
리액트 쿼리(React Query)에서 staleTime, stale, fresh 의 개념을 이해하는 것이 중요한 이유는 이들이 데이터 캐싱 및 재활용 전략을 어떻게 관리하는지에 대한 핵심적인 부분을 이루기 때문입니다 🙂
staleTime 기본값(Default Value)은 0ms입니다. 이는 쿼리가 성공적으로 해결되면 데이터가 즉시 'stale'(오래된 상태)로 표시된다는 것을 의미합니다. 다시 말해,
기본적으로 React Query는 새로운 데이터를 가져오는 쿼리가 성공적으로 완료된 직후에데이터를 다시 가져오는 것을 고려합니다.
이러한 기본 동작은 React Query가 데이터를 항상 최신 상태로 유지하려는 목적을 반영합니다. 하지만, 필요에 따라 staleTime을 조정하여 네트워크 요청의 빈도를 줄이고
애플리케이션의 성능을 최적화할 수 있습니다. 예를 들어,데이터가 자주 변경되지 않는
경우에는 staleTime을 늘려 데이터 캐싱 기간을 연장할 수 있습니다
fetching을 해서 가져온 ‘직후’ 데이터는
늘 ‘fresh’ 합니다.
staleTime > 0인 경우, 가져온 데이터는
그 시간 만큼 fresh한 것으로 간주
staleTime = 0인 경우, 가져온 데이터는
즉시 stale한 것으로 간주
inActive
- inActive 상태는 특정 쿼리에 대한 모든 옵저버(observer)가 제거되었을 때 발생
- 해당 데이터를 사용하는 컴포넌트가 더 이상 마운트되지 않거나 해당 쿼리를 구독하는 컴포넌트가 없을 때
- 이 상태에서는 데이터가 캐시에서 자동으로 삭제되기 전까지 일정 기간 동안 유지
이 상황에서 더 들여다 볼 것 👀 ⇒ inactive에서 fresh가 다시 될 수 있나요?
될 수 있습니다!
출처 입력
- 컴포넌트 재마운트: 이전에 사용되었던 쿼리 키를 가진 컴포넌트가 다시 마운트되면, 해당 쿼리는 다시 활성화됩니다. 이 때, **staleTime**이 아직 지나지 않았다면 데이터는 여전히 fresh 상태로 간주됩니다.
- 데이터 재요청: **staleTime**이 지나거나 사용자 상호작용에 의해 데이터가 재요청되는 경우, 새로 가져온 데이터는 fresh 상태가 됩니다.
- Refetch: 프로그래매틱하게 refetch 함수를 호출하여 데이터를 강제로 새로 가져오는 경우에도 다시 fresh 상태가 됩니다.
isLoading vs isFetching
☝ **한줄 정리**
**`isLoading`**은 데이터가 처음 로드될 때만 **`true`**가 되는 반면,
**`isFetching`**은 데이터가 처음 로드될 때뿐만 아니라 데이터가 업데이트되거나 재요청될 때도
**`true`**가 됩니다. 이러한 차이점을 이해하고 적절히 활용하면
사용자에게 더 나은 피드백과 사용자 경험을 제공할 수 있습니다.
export default function App() {
const {isLoading, data, isError, isFetching} = useQuery("todos", getTodos);
if(isFetching) {
return <div>로딩중입니다...!</div>
}
if(isError) {
return <div>에러입니다...!</div>
}
return (
<div>
{
data.map()~~~
}
</div>
)
}
isLoading
- 정의: **isLoading**은 쿼리가 초기 로딩 상태에 있을 때 **true**가 됩니다. 즉, 쿼리가 처음 실행되어 데이터를 요청하고 있는 상태를 나타냅니다.
- 사용 예시: 사용자가 웹 페이지에 처음 방문했을 때, 특정 데이터를 위한 API 요청이 처음으로 발생하고, 이 데이터가 아직 로드되지 않았을 때 **isLoading**은 **true**가 됩니다. 예를 들어, 블로그 포스트 목록을 처음 로드할 때, **isLoading**을 사용하여 로딩 스피너를 표시할 수 있습니다.
isFetching
- 정의: **isFetching**은 쿼리가 데이터를 요청하고 있는 어떠한 시점에서도 **true**가 됩니다. 이는 초기 로딩뿐만 아니라 백그라운드 업데이트나 리프레시 시에도 해당됩니다.
- 사용 예시: 사용자가 페이지에 이미 접속해 있는 상태에서, 백그라운드에서 데이터가 주기적으로 업데이트되거나 사용자가 수동으로 데이터를 새로고침할 때 **isFetching**은 **true**가 됩니다. 예를 들어, 사용자가 날씨 정보 페이지에 있고, 앱이 주기적으로 최신 날씨 데이터를 가져오는 경우, **isFetching**을 사용하여 새로고침 중임을 나타내는 작은 인디케이터를 보여줄 수 있습니다.
라이프사이클

주요옵션
기본 설정
|
의미
|
staleTime: 0
|
useQuery 또는 useInfiniteQuery에 등록된 queryFn 을 통해 fetch 받아온 데이터는 항상 stale data 취급
|
refetchOnMount: true
|
useQuery 또는 useInfiniteQuery 가 있는 컴포넌트가 마운트 시 stale data 를 refetch 자동 실행
|
refetchOnWindowFocus: true
|
실행중인 브라우저 화면을 focus 할 때 마다 stale data를 refetch 자동 실행
|
refetchOnReconnect: true
|
Network 가 끊겼다가 재연결 되었을 때 stale data를 refetch 자동 실행
|
cacheTime: 5분 (1000 * 60 * 5 ms)
|
useQuery 또는 useInfiniteQuery가 있는 컴포넌트가 언마운트 되었을 때 inactive query라 부르며, inactive 상태가 5분 경과 후 GC(가비지콜렉터)에 의해 cache data 삭제 처리
|
retry: 3
|
useQuery 또는 useInfiniteQuery에 등록된 queryFn 이 API 서버에 요청을 보내서 실패하더라도 바로 에러를 띄우지 않고 총 3번까지 재요청을 자동으로 시도
|
revalidate vs invalidateQueries
- 데이터의 stale함, fresh함과 관련된 두 가지 다른 개념입니다.
revalidate(재검증)
- **Revalidate**는 캐시된 데이터가 'stale'(오래된) 상태가 되었을 때, 이를 자동으로 감지하고 새로운 데이터를 가져오는 프로세스입니다.
- React Query는 **staleTime**이 지나거나 다른 특정 이벤트(예: 브라우저 창 포커스 재획득)가 발생했을 때 데이터를 재검증합니다.
- 이 과정은 일반적으로 백그라운드에서 이루어지며, 사용자는 데이터가 최신 상태로 유지되고 있음을 자동으로 알 수 있습니다.
invalidateQueries(Query 무효화)
- invalidateQueries 메소드는 특정 쿼리 또는 쿼리 그룹을 명시적으로 'stale' 상태로 만듭니다. 이는 React Query에게 해당 쿼리의 데이터가 더 이상 유효하지 않으며, 가능한 한 빨리 재검증해야 한다는 것을 알립니다.
- 사용자가 데이터를 수정하는 작업을 수행한 후, 관련 쿼리를 'invalidate'함으로써 최신 데이터로의 자동 업데이트를 트리거할 수 있습니다.