티스토리 뷰

 

React-Query의 캐싱개념은 stale과 cachetime을 통해 이루어진다

 

Stale과 cacheTime은 무엇인지 먼저 알아보자.

 

| Stale 이란?

 

react query는 기본적으로 캐싱된 data를 stale한 상태로 여긴다.

 

stale의 사전적 의미는 "신선하지 않는"이라는 뜻을 가진다.

 

 

신선하다 함은, 서버에서 조회한 데이터는 그때 당시의 데이터 snapshot이고, 외부 요청으로 서버 데이터가 변경이 되었다면 내 브라우저가 가진 데이터는 이미 오래된 낡은 데이터가 되었으므로 stale하다고 말하는 것이다.

 

stale이란 최신화가 필요한 데이터라는 의미로 stale한 상태가 되면 다음의 경우에 refetch 된다.

 

refetch 되는 조건들을 확인해보자.

1. 새로운 query instance가 마운트 될 때 ( page를 이동 했다가 왔을 때 의미)

2. 브라우저 화면을 이탈 했다가 다시 focus 할 때

3. 네트워크가 다시 연결될 때

4. 특별히 설정한 refetch interval에 의한 경우 (refetchInterval)

 

 

refetchOnWindowFocus 옵션 등으로 기본 refetch 설정을 막을 수 있고
staleTime 옵션으로 설정한 시간 동안 데이터가 stale 되지 않도록 해 refetch를 막을 수도 있다.


또한 query에 별다른 action이 없으면 inactive 상태로 캐시에 남아 있다가 5분 뒤에 메모리에서 사라진다.
cacheTime 옵션을 설정해서 이 시간을 조정할 수 있다.

 

 

| react query 캐싱

 

react query는 아무 옵션을 설정하지 않으면 캐싱되지 않는다.

캐싱을 제대로 쓰려면 staleTimecacheTime에 대해 알아야 한다.

 

간단하게 먼저 설명하자면 다음과 같이 설명할 수 있다.

 

 staleTime

기본 값

defaultValue : 0

 

  • 데이터가 fresh -> stale 상태로 변경되는데 걸리는 시간
  • fresh 상태일때는 쿼리 인스턴스가 새롭게 mount 되어도 네트워크 fetch가 일어나지 않는다.
  • 데이터가 한번 fetch 되고 나서 staleTime이 지나지 않았다면 unmount 후 mount 되어도 fetch가 일어나지 않는다.

 

전달받은 데이터는 react query의 자료구조 내용 중 cache에 저장이 된다.

이때, 이 캐시 데이터의 "신선한 상태"가 언제까지 될지를 말해주는 옵션이다.

받아오는 즉시 stale 하다고 판단하며 캐싱 데이터와 무관하게 계속해서 fetching을 수행한다.

위에 언급된 캐싱고 관련된 내용은 react query에서 제공해주는 메서드, useQuery Client를 통해 조회할 수 있다.

 

const queryClient = new QueryClient({
	defaultOptions : {
    	queries : {
        	refetchOnWindowFocus : false,
            refetchOnMount : false,
            retry : false
        }
    }
})

 

- useQueryClient를 호출하면 위에서 생성자 함수, QueryClient를 통해 만들어진 객체의 정보를 얻을 수 있다.

 

이때, 이 내용 안에 캐싱과 관련된 내용들 역시 useQueryClient를 호출하면 자식들 내부에서 확인이 가능하다. Context API와 매우 흡사한 사용감을 보여준다

 

- 이 순간의 키값은 useQuery를 통해 호출할 당시 첫번째 인자로 전달한 값으로, (string 전달 시 자동 배열로 감싸서 전달)


이 내부 키값에 대해서 동일한 키를 가진 캐싱값이 존재할 경우, fetching을 진행하지 않고 이 캐싱값을 그대로 다시 사용한다.

 

 

단, 여기서 주의할 점이 있다.

 

1.staletime을 지정해주지 않고 쓴다면 react-query의 캐싱 기능을 제대로 활용할 수가 없다.

만약 해당 useQuery를 호출할 당시에 옵션으로 staletime을 따로 지정해주지 않았었다면, 항상 캐싱되어 있는 데이터는 stale하다고 여기기 때문에 refetching을 하게 되어 서버에 계속적인 요청을 하게된다.

 

따라서 사용은 하긴 해야하나, 만약 데이터 구조가 자주자주 변하는 어플리케이션이라면 지정하지 않는 편이 좋고 해당 브라우저에 표현되는 내용의 데이터들이 정적이라면 staletime을 지정해주고 요청하는 것이 서버의 부담을 경감시키는 것이 될 것이다.

 

 

2.enabled을 설정하여 retry 호출을 막자

 

옵션에 존재하는 enabled:false 를 설정해놓을 경우, 초기 마운트시에 해당 useQuery가 마치 useEffect처럼 첫 마운트시 fetcher 함수 호출을 하고, 실패했을 때는 계속 retry를 하는 행위를 사전 차단할 수 있다.

그러나, enabled:false는 말 그대로 이 useQuery의 기능을 사용하지 않겠다고 말해주는 것과 같기 때문에 수동적으로 호출하는 방식이 필요하다. 그것이 바로 useQuery 함수 호출을 하여 리턴되는 객체에 포함되어 있는 refetch 함수다.

 

 

 

3. 진짜 진짜 중요한 refetch 함수의 개념

 

여기서 정말 중요한 것이 refetch 함수는 "캐싱 결과는 조회하지 않고 완전히 무시한 채로 그냥 ajax 요청을 날리는 메서드라는 점이다

- 처음에 나는 refetch 함수의 개념을 정확하게 알지 못한 채로 느낌으로다가 사용해왔었다.근데 이제서야 실마리가 풀리는 기분이다.

 

refetch 함수는 캐싱 결과를 조회하지 않고 무시한다.

 

즉, 아무리 QueryClient 객체에 저장되어 있는 캐싱 내용에 해당 요청값의 키가 존재한다 하더라도, 크냥 무시해버리고 re-fetching 요청을 진행해버린다.

 

결론

캐싱을 구현하려고 한다면 enabled 옵션을 false로 두면 안된다.

 

 

그렇다면, true인 상태와 캐싱을 둘 다 구현하려면 어떻게 해야할까 ?

 

1. enabled option에 대해 특정한 상태를 충족할 때만 true로 만들고 그 외에는 false로 하여 초기 요청을 통한 retry로 오류를 생성하는 것을 막는다. (refetch 메서드로 강제 호출을 하지 않는다.)

 

 

2.그 뒤 조건부로 enabled가 true로 변경되면서 요청을 날리게 되어 성공하면, data 프로퍼티에 그 값이 저장되고, 캐싱에도 저장이 될 것 이다.

 

그렇다면 그 뒤에는 이 data를 가지고 UI를 그려내는 작업을 진행하면 된다.

 

마지막 추가사항으로,
useQuery를 호출할 때, 세번째 인자 객체는 그 내부에서 비동기 데이터 처리의 결과에 대해서 특정 처리를 할 수 있는 콜백함수를 담을 프로퍼티를 담는 옵션을 제공한다.

 

 

아까 위에서 언급했던 대로, 이미 useQueryClient 로 조회하는 구독값 내부의 query caching 데이터 내부에 지금 실행하려는 쿼리의 실행에 대한 키값이 존재할 경우, 해당 캐싱값을 사용하고 요청을 보내지 않는다고 하였다.

 

이말인 즉슨, 위에서 언급했던 콜백함수들은 캐싱값을 그대로 사용할 경우 호출되지 않는다는 점이다. 

 

따라서, 캐싱값을 사용하게 될 때에는 해당 data의 결과값은 변동된다는 사실을 이용하여 이 데이터를 이용한 조건부 랜더링을 실행하도록 만들어야 한다!

 

 

 cacheTime

캐시 구조에 저장된 데이터는 메모리상에 존재하게 된다.

  • 데이터가 inactive 상태일 때 캐싱된 상태로 남아있는 시간
  • 쿼리 인스턴스가 unmount 되면 데이터는 inactive 상태로 변경되며, 캐시는 cacheTime만큼 유지된다.
  • cacheTime이 지나면 가비지 콜렉터로 수집된다.
  • cacheTime이 지나기 전에 쿼리 인스턴스가 다시 마운트 되면, 데이터를 fetch하는 동안 캐시 데이터를 보여준다.
  • cacheTime은 staleTime과 관계없이, 무조건 inactive 된 시점을 기준으로 캐시 데이터 삭제를 결정한다.

 

 

이 두 가지 option은 다음과 같이 설정할 수 있다.

 

const { data } = useQuery('users', getUsers, { // options
  staleTime: 5000,
  cacheTime: Infinity,
});

 

 

staleTime으로 설정한 시간만큼 api 요청한 data가 유지되고, 시간이 지나면 fresh한 상태에서 stale한 상태로 썩어 문드러진다.

즉, 설정한 값으로인해 data가 오래된 것 같다고 판단하기 때문에 같은 데이터를 다시 한번 api 요청을 수행한다.

 

그렇다면, staleTime을 길게 주면 되지 않을까?

-> 답은 아니다. 

 

그 이유는 cacheTime에 있는데, 아무리 staleTime이 길어도 저장되는 시간인 cacheTime이 짧다면 데이터가 사라지기 때문이다.

 

또한

staleTime과 cacheTime의 defaultValue는 0과 5분이다.

때문에 아무런 설정을 하지 않으면 캐싱이 전혀 되지 않는다.

(데이터는 캐싱 되지만 신선한 데이터가 하나도 없는 것.)

 

| useQuery

 

useQuery의 첫 번째 인자에는 unique한 key가 들어간다.

이 key는 내부적으로 refetching, caching, query 공유를 위해 사용된다.

 

api 요청으로 얻은 데이터를 구분할 수 있는 key를 부여해서

또 다시 같은 데이터를 요청하지 않도록 할 수 있다.

 

useQuery의 key가 달라지면 곧 바로 다시 api 요청을 한다.

 

두 번째 인자에는 data를 resolve 하거나 error를 뱉는 Promisereturn 하는 function을 넣는다.

이 함수 안에서 error가 발생 시 자동으로 error를 reject해준다.

 

axios는 200이 아닌 응답에 에러가 발생하기 때문에 별다른 처리를 해주지 않아도 된다.

 

하지만 fetch같은 경우 일일이 throw new Error()를 해주어야 한다.

 

 

| Data refresh

post 요청을 하거나 delete 요청을 했을 때 화면에 보여주는 데이터에 변화를 줘야한다.

그러나 query key가 변하지 않으므로 강제 리프레쉬를 해야할 필요가 있는데,

queryClient의 invalidateQueries 메소드를 이용해 query key를 날려버린다.

 

 

 

실시간 베스트 글

 

혹시 국비지원이나 사설학원 (패스트 캠퍼스 등) 다니는 중이신가요?

 

현직 5년차가 직접 겪은 국비지원 패스트캠퍼스 후기 보러가기

 

국비지원 패스트캠퍼스 후기 현직 5년차 개발자가 알려주는 코딩학원 현실

국비지원 패스트캠퍼스 후기 현직 개발자가 경험한 코딩학원 선택 기준 및 현실에 대해 알려드립니다. 이런 분들은 절대 수업 듣지도 마세요.