refactor client cache - boostcampwm-2022/web33-Mildo GitHub Wiki
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ (์บ์ฑ ์ ์ฉ)
- apis ๊ฐ์ (axios ํ์ , ์ธ์คํด์ค)
- ๋ก๋ฉ jotai suspense
- eject ๋๋๋ฆฌ๊ธฐ
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ ์ ์ฉ
- eject ๋๋๋ฆฌ๊ธฐ
- ๋ก๋ฉ jotai suspense๋ก ๋ณ๊ฒฝ
- apis ๋ชจ๋ ๊ฐ์
- ๋ฆฌ์กํธ ์ฟผ๋ฆฌ ํ์ต ๋ฐ ์ ์ฉ
- ๋ง์ปค๋ฅผ ํด๋ฆญํ๋ฉด ๋ชจ๋ฌ์ฐฝ์ด ํ์๋๋ค. ๋ชจ๋ฌ์ฐฝ์ 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);
-
๋ฆฌ์กํธ ์ฟผ๋ฆฌ ์ ์ฉ ํ
// hooks/useGraphInfo.tsx const useGraphInfo = ( enabled: boolean, firstLevelInfo: SortAllAreasTypes | null, success: (data: graphInfoResponseTypes | null) => void ) => { const { data: graphInfoResponse } = useQuery( ['getGraphInfo', firstLevelInfo ? firstLevelInfo[0] : ''], async () => { 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/InfoDetailModal/InfoDetailModal.tsx 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; }; useEffect(() => { if (!isSecondLevel) { ... } setPastInformation(); }, [isSecondLevel]);
- ์์ฒญ์ ์ฑ๊ณตํ์ ๋
success
ํจ์๊ฐ ์คํ๋๊ณ , ์ด๋ฏธ ์บ์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋setPastInformation
ํจ์๊ฐ ์คํ๋๋ค!
- ์์ฒญ์ ์ฑ๊ณตํ์ ๋
- ์์ธ์ญ์ ๋ค์ ์ด์์ ๋ ๊ทธ๋ํ ๊ทธ๋ ค์ง๋ ์๋๊ฐ ๋งค์ฐ ๋นจ๋ผ์ง
- ํ ๋ฒ ์์ฒญํ ๋ฐ์ดํฐ(์์ธ์ญ)๋ ๋ค์ ์์ฒญ๋์ง ์์
-
๋ฌธ์ ์
-
firstLevelInfo
๋ null์ผ ์๋ ์์ -
useQuery๋ฅผ ์ด์ฉํ๋ ค๋ฉด axios ์ฃผ์์
firstLevelInfo[0]
(์ง์ญ ์ด๋ฆ)๋ฅผ ์ฌ์ฉํด์ผ ํจ -
ํ์ง๋ง ์กฐ๊ฑด๋ฌธ์ผ๋ก
firstLevelInfo
๊ฐ ์์ ๋๋ง useQuery๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ์์ฑํ์๋๋, ๋ค์๊ณผ ๊ฐ์ ์๋ฌ ๋ฐ์Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app
-
-
ํด๊ฒฐ
const { data: graphInfoResponse } = useQuery( ..., async () => { if (!firstLevelInfo) { return null; } const result = await apis.getPastInformation(firstLevelInfo[0]); return result; }, { ... } );
- query Function์์
firstLevelInfo
๊ฐ ์์ ๋๋ง ์์ฒญํ๋๋ก ๋ณ๊ฒฝ - query Funciton์ ๋ํด ์ ์ดํดํ์ง ๋ชปํ๊ณ ์ ๊ทผํ ๊ฒ์ด ์์ธ
- query Function์์