Service Worker Caching Strategy - team-yaza/mozi-client GitHub Wiki

κ°œμš”

μ„œλΉ„μŠ€ μ›Œμ»€λ₯Ό 효과적으둜 μ‚¬μš©ν•˜λ €λ©΄ 캐싱 μ „λž΅μ„ 잘 μ„ νƒν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€. 캐싱 μ „λž΅μ„ μ„Έμš°λ €λ©΄ Cache μΈν„°νŽ˜μ΄μŠ€μ— λŒ€ν•œ 이해가 ν•„μš”ν•˜λ‹€.

캐싱 μ „λž΅μ€ μ„œλΉ„μŠ€ μ›Œμ»€μ˜ fetch μ΄λ²€νŠΈμ™€ Cache μΈν„°νŽ˜μ΄μŠ€ κ°„μ˜ μƒν˜Έ μž‘μš©μ΄λ‹€. 예λ₯Ό λ“€μ–΄ 정적 μš”μ†Œμ— λŒ€ν•œ μš”μ²­μ„ λ¬Έμ„œμ™€ λ‹€λ₯΄κ²Œ μ²˜λ¦¬ν•˜λŠ” 것이 λ°”λžŒμ§ν•  수 있으며 μ΄λŠ” 캐싱 μ „λž΅μ΄ κ΅¬μ„±λ˜λŠ” 방식에 영ν–₯을 λ―ΈμΉœλ‹€.

Cache interface vs HTTP Cache

Cache μΈν„°νŽ˜μ΄μŠ€μ™€ HTTP CacheλŠ” 관련이 μ—†λ‹€.

  • The Cache interface is a caching mechanism entirely separate from the HTTP cache.
  • Whatever Cache-Control configuration you use to influence the HTTP cache has no influence on what assets get stored in the Cache interface.

HTTP μΊμ‹œμ— 영ν–₯을 μ£ΌκΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” Cache-Control ꡬ성은 Cache μΈν„°νŽ˜μ΄μŠ€μ— 영ν–₯을 λ―ΈμΉ˜μ§€ λͺ»ν•˜λ―€λ‘œ, λΈŒλΌμš°μ € μΊμ‹œλ₯Ό κ³„μΈ΅ν™”λœ κ²ƒμœΌλ‘œ 생각할 수 μžˆλ‹€.

HTTP μΊμ‹œλŠ” HTTP 헀더에 ν‘œν˜„λœ μ§€μ‹œλ¬Έκ³Ό ν•¨κ»˜ ν‚€-κ°’ μŒμ— μ˜ν•΄ κ΅¬λ™λ˜λŠ” μ €μˆ˜μ€€ μΊμ‹œμ΄λ‹€. 이와 λŒ€μ‘°μ μœΌλ‘œ Cache μΈν„°νŽ˜μ΄μŠ€ JavaScript API에 μ˜ν•΄ κ΅¬λ™λ˜λŠ” κ³ κΈ‰ μΊμ‹œμ΄λ‹€.

Cache μΈν„°νŽ˜μ΄μŠ€λŠ” μƒλŒ€μ μœΌλ‘œ λ‹¨μˆœν•œ HTTP ν‚€-κ°’ μŒμ„ μ‚¬μš©ν•  λ•Œλ³΄λ‹€ 더 λ§Žμ€ μœ μ—°μ„±μ„ μ œκ³΅ν•˜λ©° 캐싱 μ „λž΅μ„ κ°€λŠ₯ν•˜κ²Œν•©λ‹ˆλ‹€. μ„œλΉ„μŠ€ μ›Œμ»€ μΊμ‹œμ™€ κ΄€λ ¨λœ λͺ‡κ°€μ§€ μ€‘μš”ν•œ APIλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • CacheStorage.open
  • Cache.add & Cache.put
  • Cache.match (to locate a cached response in a Cache instance)
  • Cache.delete (to remove a cached response from a Cache instance)

fetch event

μ•„λž˜λŠ” fetch event와 Cache μΈν„°νŽ˜μ΄μŠ€μ˜ 예제 μ½”λ“œμ΄λ‹€.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

  1. Inspect the request's destination property to see if this is an image request.
  2. If the image is in the service worker cache, serve it from there. If not, fetch the image from the network, store the response in the cache, and return the network response.
  3. All other requests are passed through the service worker with no interaction with the cache.

fetch 이벀트 κ°μ²΄λŠ” 각 μš”μ²­μ˜ μœ ν˜•μ„ μ‹λ³„ν•˜λŠ”λ° 도움이 λ˜λŠ” λͺ‡κ°€μ§€ μœ μš©ν•œ 정보가 μžˆλŠ” μš”μ²­ 속성이 ν¬ν•¨λ˜μ–΄ μžˆλ‹€.

  • url: which is the URL for the network request currently being handled by the fetch event.
  • method: which is the request method (e.g, GET or POST)
  • mode: which describes the request's mode. A value of 'navigate' is often used to distinguish requests for HTML documents from other requests.
  • destination: which describes the type of content being requested in a way that avoids using the requested asset's file extension.

Caching Strategies

Cache Only

image

Cache OnlyλŠ” μ„œλΉ„μŠ€ μ›Œμ»€κ°€ νŽ˜μ΄μ§€λ₯Ό μ œμ–΄ν•  λ•Œ μΌμΉ˜ν•˜λŠ” μš”μ²­μ€ μΊμ‹œλ‘œλ§Œ μ΄λ™ν•©λ‹ˆλ‹€. 즉, 이 νŒ¨ν„΄μ΄ μž‘λ™ν•˜λ €λ©΄ asset을 미리 μΊμ‹œν•΄μ•Όλ˜λ©° μ„œλΉ„μŠ€ μ›Œμ»€κ°€ μ—…λ°μ΄νŠΈλ  λ•ŒκΉŒμ§€ ν•΄λ‹Ή μžμ‚°μ΄ μΊμ‹œμ—μ„œ μ—…λ°μ΄νŠΈλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

μœ„ μ½”λ“œμ—μ„œ asset듀은 install νƒ€μž„μ— 미리 μΊμ‹œλœλ‹€.

μ„œλΉ„μŠ€μ›Œμ»€κ°€ fetch 이벀트λ₯Ό μ²˜λ¦¬ν•  λ•Œ μš”μ²­ URL이 precached assets에 μžˆλŠ”μ§€ ν™•μΈν•˜κ³  μžˆλ‹€λ©΄ cacheλ₯Ό λ¦¬ν„΄ν•œλ‹€. λ‹€λ₯Έ λ¦¬μ†ŒμŠ€λŠ” network둜 μš”μ²­μ„ 보낼 것이닀.

Cache first, falling back to network

image

  1. The request hits the cache. If the asset is in the cache, serve it from there.
  2. If the request is not in the cache, go to the network.
  3. Once the network request finishes, add it to the cache, then return the response from the network.
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Network first, falling back to cache

Stale While Revalidate

stale-while-revalidateλŠ” κ°œλ°œμžκ°€ μΊμ‹œλœ μ½˜ν…μΈ λ₯Ό μ¦‰μ‹œ λ‘œλ“œν•˜λŠ” μ¦‰μ‹œμ„±κ³Ό μΊμ‹œλœ 컨텐츠에 λŒ€ν•œ μ—…λ°μ΄νŠΈκ°€ ν–₯후에 μ‚¬μš©λ˜λ„λ‘ 보μž₯ν•˜λŠ” μ΅œμ‹ μ„± κ°„μ˜ κ· ν˜•μ„ μœ μ§€ν•˜λŠ”λ° λ„μ›€μ΄λœλ‹€. μ •κΈ°μ μœΌλ‘œ μ—…λ°μ΄νŠΈλ˜λŠ” μ„œλ“œνŒŒν‹° μ›Ή μ„œλΉ„μŠ€ λ˜λŠ” 라이브러리λ₯Ό μœ μ§€ κ΄€λ¦¬ν•˜κ±°λ‚˜ asset 수λͺ…이 μž›μ€ κ²½ν–₯이 μžˆλŠ” 경우 stale-while-revalidateκ°€ κΈ°μ‘΄ 캐싱 정책에 μœ μš©ν•œ μΆ”κ°€ κΈ°λŠ₯이 될 수 μžˆλ‹€.