동기
리액트 쿼리 캐시는 http 브라우저 캐시를 사용하는 것이 아닌 코어에서 자체적인 캐시 스토어를 만들어 쿼리들을 컨트롤한다. 그래서, 리액트 쿼리의 캐시 컨트롤 방법을 알아둘 필요가 있다.
React-Query 가 캐시를 다루는 두개의 상태 값
사실 http에서 캐시를 다루는 상태와 동일하다. 결국 서버 데이터와 클라이언트의 캐시 데이터 사용에서 나오는 근본적인 문제는 동일 하기 때문일 것이다.
- validate
- 캐시가 유효한지 확인 하는 상태, 유효하다면 캐시를 사용한다. 유효하지 않으면 캐시를 사용하지 않고, 무조건 서버에 재요청한 데이터를 사용해 리턴한다.
cacheTime: number | Infinity
라는 설정 값을 통해 설정, default 는 5분
- stale
- 캐시가 stale 한지 확인하는 상태, stale 하다면, 쿼리 데이터 값은 캐시를 사용하여 리턴하지만,
queryFn
을 통해 백그라운드에서 서버에 재검증한다. stale 하지 않으면, validate 상태에 따라 동작한다. staleTime: number | Infinity
라는 설정 값을 통해 설정, default 는 0
기본 예제
cacheTime
과staleTime
은 default 값으로 설정 했다고 가정하자.
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
의 인스턴트가 첫번째로 마운트 됐다고 가정하자.['todos']
쿼리 키를 사용한 적이 없다면, 첫 request 는 네트워크에 요청하게 될 것이다.- 네트워크 request 가 성공하면, response 가
['todos']
쿼리 키에 캐시 될 것이다. - useQuery hook 이 해당 데이터를
staleTime
설정 값에 따라 stale 여부를 규정할 것이다. 0이 기본 값이며, 기본 값이라면 곧바로 데이터를 stale 상태로 두게 된다.
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
의 인스턴트가 두번째로 마운트 됐다고 가정하자.- 첫번째 쿼리에서 이미
['todos']
쿼리 키를 사용했으므로, 데이터가 캐시에서 바로 리턴 될 것이다. - 그후, 새로운 인스턴스가 새로운 네트워크 request 를 발생 할 것이다.
- 쿼리 키는 같은 것을 사용했으므로, queryFn 의 fetch 함수가 같던 아니던, 쿼리의 상태가 업데이트가 될 것이다. (isFetching, isLoading 등)
- request 가 성공적으로 완료되면,
['todos']
쿼리 키에 해당하는 캐시 값이 서버 값으로 업데이트 될 것이다.
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
에 해당하는 두 인스턴스가 다 언마운트 되어 더이상 사용하지 않게 됐다고 가정하자.- 이 쿼리를 사용하는 인스턴스가 더이상 없다면,
cacheTime
설정 값에 따라 캐시가 지워지고, 가비지컬렉션 될 수 있도록, 캐시 타임아웃 체크를 한다.
- 캐시 타임아웃 체크된 캐시 데이터가 설정된 캐시 유효 시간이 다 하기전에 새로운
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
인스턴스가 마운트 되었다면, 이 쿼리는 즉시 캐시된 데이터 리턴 하게 될 것이고, 백그라운드에서fetchTodos
함수를 실행할 것이다. 이 함수가 성공적으로 완료 되면, 캐시는 다시 서버의 새로운 데이터로 변경될 것이다.
- 모든
useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
인스턴스가 언마운트 되고, 5분(default) 동안 새useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
인스턴스가 마운트 되지 않는다면,['todos']
쿼리 키로 설정된 캐시 값은 지워지고 가비지컬렉션 된다.
결론
살펴보니 http 캐시와 같은 맥락이다. 즉, React-query 는 직접 http cache-control 의 Stale-While-Revalidate 를 구현하여 캐시 컨트롤을 한다. 우리는 상황에 맞게
cacheTime
과 staleTime
설정 값만 잘 맞춰 사용해주면 되겠다.참조