react query를 이용한 캐싱 - boostcampwm-2022/web33-Mildo GitHub Wiki
- 마커를 클릭하면 모달창이 표시된다. 모달창은 1단계, 2단계로 나뉜다. 2단계 모달창에서 해당 지역의
24시간 이내의 인구 밀도 정보
를 그래프로 보여줘야 한다. -
24시간 이내의 인구 밀도 정보
는 2단계 모달창을 열 때마다 서버에 요청된다. 이 응답은 약 1.2~1.8초가 걸린다. - 즉, 2단계 모달창을 열 때마다 1.2초~1.8초를 기다려야 한다.
-
24시간 이내의 인구 밀도 정보
는 30분 간격으로 보여주기 때문에 실시간성이 크게 보장되지 않아도 된다. - 그렇다면 한 번 요청한 데이터는 몇 분 정도 지나도 같은 데이터를 보여줘도 되지 않을까?
- 그래서 jotai를 이용해 한 번 응답 받은 데이터는 전역 상태로 저장하고, 이후 같은 지역에 대한 데이터를 보여줘야 할 때에는 전역 상태에서 가져오도록 했다.
-
jotai를 이용해 전역 상태로 관리하면 캐싱 효과를 구현할 수 있었지만, 한 번 전역 상태로 저장된 데이터는 이후 변경되지 않는다.
-
즉, 사용자가 새로고침 하지 않으면 아무리 시간이 지나도 최초로 모달창을 열었던 그 시간을 기준으로
24시간 이내의 인구 밀도 정보
를 보여준다.ex) 오후 2시에 처음 모달창을 열고 새로고침을 하지 않은 상태에서 오후 9시에 모달창을 열게 되면, 오후 2시를 기준으로
24시간 이내의 인구 밀도 정보
가 표시된다. -
좀 더 본격적인 캐싱 방법을 찾아보던 도중 리액트 쿼리로 캐싱을 구현할 수 있다는 것을 알게 되었고, 학습에도 도움이 될 것이라 생각하여 리액트 쿼리를 적용하기로 했다.
...
const [secondLevelInfoCache, setSecondLevelInfoCache] = useAtom(
secondLevelInfoCacheAtom
);
const [graphInfo, setGraphInfo] = useState<SecondLevelTimeInfoCacheTypes>({});
...
// 전역에 areaName을 키로 갖고 있는 속성이 있으면 hit
if (secondLevelInfoCache[areaName]) {
setGraphInfo(secondLevelInfoCache[areaName]);
return;
}
// 아니면 api 호출
const { data } = await apis.getPastInformation(areaName);
// 전역 상태로 저장하고
setSecondLevelInfoCache({ ...secondLevelInfoCache, [areaName]: data });
// 그래프 보여주기
setGraphInfo(data);
- 해당 지역의
24시간 이내의 인구 밀도 정보
가 전역 상태(secondLevelInfoCache
)에 저장되어 있으면 hit - 없으면 api 서버에 요청하고 전역 상태에 저장
[React Query](https://www.notion.so/React-Query-18849fd1e55e43fa97e6838291dfa92b)
- react query
- 서버와 클라이언트가 비동기적으로 공유하는 데이터인 ‘서버 상태’를 관리하는 관리하는 라이브러리이다.
- 데이터 캐싱이 가능하다.
- 데이터를 불러오는 로직과 state로 설정하는 로직이 합쳐져서 좀 더 깔끔한 코드를 작성할 수 있다.
-
suspense: true
만 추가하면 간단하게 susepnse 기능을 구현할 수 있다.
- useQuery
- 서버에서 데이터를 가져오고 캐싱하는 훅이다.
- query key, query function, options를 인자로 받는다.
options 설정이 중요하다.
- staleTime을 설정해줘야 한다.
- 0이면 캐시된 데이터는 항상 stale하기 때문에 refetching하게 되어 서버에 계속 요청한다.
- staleTime보다 cacheTime이 더 길어야 한다.
- cacheTime이 더 짧으면 데이터가 상하지 않았지만 캐시에 있는 데이터는 이미 가비지 컬렉터에 의해 처리되었으므로 캐싱 기능을 이용할 수 없다.
- enabled 옵션 설정
- true이면 계속 요청을 보내고 false이면 캐싱 기능을 사용하지 않는 것과 마찬가지이기 때문에 보통 조건문으로 결정한다.
// hooks/useGraphInfo.tsx
const { data: graphInfoResponse } = useQuery(
['getGraphInfo', firstLevelInfo ? firstLevelInfo[0] : ''], // query key
async () => { // query function
if (!firstLevelInfo) {
return null;
}
const result = await apis.getPastInformation(firstLevelInfo[0]);
return result;
},
{
enabled,
staleTime: QUERY_TIME.STALE_TIME, // 5분
cacheTime: QUERY_TIME.CACHE_TIME, // 30분
onSuccess: data => {
success(data);
},
onError: e => {
console.log('error', e);
}
}
);
return [graphInfoResponse];
};
export default useGraphInfo;
-
query key
-
지역에 따라
24시간 이내의 인구 밀도 정보
를 요청하므로 1번째 인덱스에 지역 이름을 붙여주었다.ex) [’getGraphInfo’, ‘서울역’]
-
-
query function
- 모달창을 열지 않았을 때는 null을 return하고 그 외에는 서버에 요청한다.
-
query options
- enabled : 특정 상황에만 query function이 실행되도록 설정(후에 추가 설명)했다.
- staleTime : 캐시 데이터가 언제 상하게(?) 되는지 결정, 5분으로 설정했다.
- cacheTime : 캐시된 데이터가 30분 동안 메모리에 저장되는 것으로 설정했다.
- onSuccess : 요청에 성공 시 데이터로 그래프를 그려준다.
-
enabled
const enabled = () => { // 1. if (!isSecondLevel) { return false; } // 2. if (!firstLevelInfo || !prevFirstLevelInfo) { return true; } // 3. if (prevFirstLevelInfo[0] === firstLevelInfo[0]) { return false; } // 4. return true; };
-
2단계 모달창이 열리지 않았을 때는 요청하지 않는다.
-
이전에 2단계 모달창이 한 번도 열리지 않았을 때, 즉 처음으로 2단계 모달창이 열릴 때는 요청한다.
-
이전에 선택됐던 지역과 같은 지역을 선택했을 때는 요청하지 않는다.
ex) 서울역 마커를 누르고, 2단계 모달창을 닫고, 다시 서울역 마커를 눌렀을 때
-
이전에 선택됐던 지역과 다른 지역을 선택했을 때는 요청한다.
ex) 서울역 마커를 누르고, 명동 마커를 눌렀을 때
-
-
그래프 적용
// components/SecondLevelComponent/SecondLevelComponent.tsx // 1. const success = (data: graphInfoResponseTypes | null) => { if (data) { setGraphInfo(data.data); setPrevFirstLevelInfo(firstLevelInfo); } }; const setPastInformation = async (): Promise<undefined> => { if (!firstLevelInfo) { return; } if (graphInfoResponse) { setGraphInfo(graphInfoResponse.data); setPrevFirstLevelInfo(firstLevelInfo); } // eslint-disable-next-line no-useless-return return; }; // 2. useEffect(() => { if (!isSecondLevel) { ... } setPastInformation(); }, [isSecondLevel]);
- 요청을 성공했을 땐
success
함수가 실행된다. - 이미 캐싱된 데이터를 가져올 땐
setPastInformation
함수가 실행된다!
- 요청을 성공했을 땐
- 서울역을 다시 열었을 때 그래프 그려지는 속도가 매우 빨라짐
- 한 번 요청한 데이터(서울역)는 다시 요청되지 않음