본문 바로가기
웹/프론트엔드

[React/Typescript] 라이브러리없이 웹 캐싱 기능 구현하기

by 이민훈 2022. 10. 5.

React Query같은 라이브러리를 쓰지 않고 내장된 Service Worker API를 통해 간단히 캐싱 기능을 구현할 수 있다.

 

https://developer.mozilla.org/ko/docs/Web/API/Cache

 

Cache - Web API | MDN

Cache 인터페이스는 ServiceWorker 의 생명주기의 일부로 캐시 된 Request와 Response를 나타냅니다.

developer.mozilla.org

 

Cache 인터페이스들의 메서드는 위의 DOCS에서도 확인이 가능하고, 코드로도 쉽게 확인이 가능하다.

 

메서드들을 쉽게 사용하기 위해 유틸 함수 몇 개를 만들어보자.

//utilities/cache.ts
/**
 * 해당 이름을 가진 스토리지를 반환한다.
 * 만들어져 있다면, 만들어진 스토리지를, 없다면 만들어서 반환한다.
 * @param storageName
 * @returns
 */
export const openCache = async (storageName: string): Promise<Cache> => {
  const storage = await window.caches.open(storageName);
  return storage;
};

/**
 * 스토리지 안에 URL을 키로, 데이터를 저장한다.
 * @param storageName
 * @param url
 * @param res
 */
export const putCache = async (
  storageName: string,
  url: string,
  res: Response
) => {
  const storage = await window.caches.open(storageName);
  storage.put(url, res);
};

/**
 * 스토리지 안에 URL를 키로 가진 캐시 데이터를 삭제한다.
 * @param storageName
 * @param url
 */
export const deleteCache = async (storageName: string, url: string) => {
  const storage = await openCache(storageName);
  storage.delete(url);
};

/**
 * 스토리지 안에 요청한 URL을 키로 가진 캐시 데이터가 있는지 확인한다.
 * @param storageName
 * @param url
 * @returns 캐시 데이터가 없다면 undefined를 반환한다.
 */
export const matchCache = async (storageName: string, url: string) => {
  const storage = await openCache(storageName);
  return storage.match(url);
};

/**
 * 스토리지 안에 URL를 키로 가진 캐시 데이터가 있는지 확인 후,
 * 있다면 캐시 데이터를 반환하고
 * 없다면 API를 요청해 response를 반환한다.
 * @param storageName
 * @param requestUrl
 * @param callback
 */
export const call = async <T>(
  storageName: string,
  requestUrl: string,
  callback?: (res: T, resType: string) => void
) => {
  const cacheData = await matchCache(storageName, requestUrl);
  if (cacheData) {
    cacheData.json().then((res: T) => {
      callback && callback(res, "cache data");
    });
  } else {
    fetch(requestUrl).then((res) => {
      putCache(storageName, requestUrl, res);
      res
        .clone()
        .json()
        .then((res: T) => {
          callback && callback(res, "api response");
        });
    });
  }
};

 

이제 call 함수에 요청할 url만 잘 넘겨준다면 해당 요청에 해당하는 캐시 데이터가 있다면 캐싱된 데이터를 반환할 것이고,

없다면 api를 호출한 후 response를 캐싱할 것이다.

간단하게 버튼 2개를 만들어 테스트해보자.

//App.tsx
import { deleteCache, call } from "./utilities/cache";

const url =
  "https://www.7timer.info/bin/astro.php?lon=113.2&lat=23.1&ac=0&unit=metric&output=json&tzshift=0" as const;

const storage = "testStorage" as const;

function App() {
  const handleCall = () => {
    call(storage, url, (res, resType) => console.log(resType, res));
  };

  const handleDeleteCache = () => {
    deleteCache(storage, url);
  };

  return (
    <div style={{ display: "flex", padding: 50, gap: 20 }}>
      <button onClick={handleCall}>API 호출</button>
      <button onClick={handleDeleteCache}>캐시 제거</button>
    </div>
  );
}

export default App;

 

api를 최초 호출한 후엔 캐싱된 데이터를 불러오는 걸 확인할 수 있다.

캐시를 제거하게 되면 다시금 api를 호출한다.

잘 동작하는 캐싱 기능

 

https://github.com/Lee-Minhoon/blog-examples/tree/main/web-cache

 

blog-examples/web-cache at main · Lee-Minhoon/blog-examples

Contribute to Lee-Minhoon/blog-examples development by creating an account on GitHub.

github.com

 

댓글