서비스 워커 캐싱 전략

지금까지는 Cache 인터페이스. 서비스 워커를 효과적으로 사용하려면 하나 이상의 캐싱 전략을 채택해야 합니다. Cache 인터페이스를 어느 정도 숙지해야 합니다.

캐싱 전략은 서비스 워커의 fetch 이벤트와 Cache 인터페이스 간의 상호작용입니다. 캐싱 전략을 작성하는 방법은 다음과 같습니다. 예를 들어 정적 애셋에 대한 요청을 문서와 다른 방식으로 처리하는 것이 이는 캐싱 전략이 구성되는 방식에 영향을 미칩니다.

본격적으로 시작하기 전에 잠시 시간을 내어 Cache 인터페이스가 무엇이고, 무엇이고, 그리고 서비스 워커 캐시를 관리하기 위해 제공하는 몇 가지 메서드에 대한 간단한 설명을 제공합니다.

Cache 인터페이스와 HTTP 캐시 비교

이전에 Cache 인터페이스를 사용한 적이 없다면 그것을 다음과 같이 생각하고 싶을 수 있습니다. 적어도 HTTP 캐시와 관련이 있어야 합니다. 호출은 건너뛸 수 없습니다.

  • Cache 인터페이스는 HTTP 캐시와 완전히 분리된 캐싱 메커니즘입니다.
  • Cache-Control HTTP 캐시에 영향을 주기 위해 사용하는 구성은 Cache 인터페이스에 저장되는 애셋에 영향을 미치지 않습니다.

브라우저 캐시는 계층화된 것으로 생각하는 것이 좋습니다. HTTP 캐시는 HTTP 헤더에 표현된 지시문을 포함하는 키-값 쌍에 의해 구동되는 하위 수준 캐시입니다.

반면에 Cache 인터페이스는 JavaScript API로 구동되는 상위 수준 캐시입니다. 이렇게 하면 비교적 단순한 HTTP 키-값 쌍을 사용할 때보다 유연성이 높아지며, 캐싱 전략을 가능하게 만드는 요소 중 절반입니다. 서비스 워커 캐시와 관련된 몇 가지 중요한 API 메서드는 다음과 같습니다.

  • CacheStorage.open 드림 새 Cache 인스턴스를 만듭니다.
  • Cache.add 드림 및 Cache.put 서비스 워커 캐시에 저장할 수 있습니다.
  • Cache.match 드림 Cache 인스턴스에서 캐시된 응답을 찾습니다.
  • Cache.delete 드림 Cache 인스턴스에서 캐시된 응답을 삭제합니다.

예시는 극히 일부에 불과합니다. 다른 유용한 메서드도 있지만 이 가이드의 뒷부분에서 사용하게 될 기본적인 것들입니다.

소박한 fetch 이벤트

캐싱 전략의 나머지 절반은 서비스 워커의 fetch 이벤트를 호출합니다. 지금까지 이 문서에서는 '네트워크 요청 가로채기'에 대해 알아봤습니다. 서비스 워커 내부의 fetch 이벤트에서 다음과 같은 일이 발생합니다.

// 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. 요청의 destination 속성을 검사하여 이미지 요청인지 확인합니다.
  2. 이미지가 서비스 워커 캐시에 있으면 거기에서 이미지를 제공합니다. 그렇지 않으면 네트워크에서 이미지를 가져옵니다. 응답을 캐시에 저장하고 네트워크 응답을 반환합니다.
  3. 다른 모든 요청은 캐시와의 상호작용 없이 서비스 워커를 통해 전달됩니다.

가져오기의 event 객체에는 속성 request 다음은 각 요청의 유형을 식별하는 데 도움이 되는 유용한 정보입니다.

  • url님, 이는 현재 fetch 이벤트에 의해 처리 중인 네트워크 요청의 URL입니다.
  • method님, 이는 요청 메서드입니다 (예: GET 또는 POST).
  • mode님, - 요청 모드를 설명합니다. 'navigate' 값은 HTML 문서에 대한 요청을 다른 요청과 구분하는 데 자주 사용됩니다.
  • destination님, 요청된 저작물의 파일 확장자를 사용하지 않는 방식으로 요청 중인 콘텐츠 유형을 설명합니다.

다시 말하지만, asynchrony가 게임의 이름입니다. install 이벤트는 event.waitUntil 메서드를 호출합니다. fetch 이벤트는 유사한 event.respondWith 메서드 비동기 호출의 결과를 반환하는 데 사용할 수 있는 요청 fetch 또는 Cache 인터페이스의 match 메서드를 표시합니다.

캐싱 전략

이제 Cache 인스턴스와 fetch 이벤트 핸들러를 약간 알아봤습니다. 몇 가지 서비스 워커 캐싱 전략을 배울 준비가 되었습니다. 가능성은 사실상 무궁무진하지만 이 가이드에서는 Workbox와 함께 제공되는 전략을 집중적으로 살펴보겠습니다 Workbox 내부에서 일어나는 일을 파악할 수 있습니다.

캐시 전용

페이지에서 서비스 워커와 캐시로의 흐름을 보여줍니다.

'Cache Only'라는 간단한 캐싱 전략부터 시작해 보겠습니다. 서비스 워커가 페이지를 제어할 때, 캐시로만 이동합니다 즉, 캐시된 모든 자산을 사전 캐시해야 패턴이 작동할 수 있습니다. 서비스 워커가 업데이트될 때까지 이러한 자산이 캐시에서 업데이트되지 않습니다.

// 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;
  }
});

위에서 애셋 배열은 설치 시 사전 캐시됩니다. 서비스 워커가 가져오기를 처리할 때 fetch 이벤트에 의해 처리된 요청 URL이 사전 캐시된 애셋 배열에 있는지 확인합니다. 만약 그렇다면, 캐시에서 리소스를 가져와서 네트워크를 건너뜁니다. 다른 요청은 네트워크로 전달되며 네트워크에서만 사용할 수 있습니다. 이 전략의 예를 확인하려면 콘솔을 연 상태에서 이 데모를 확인하세요.

네트워크 전용

페이지에서 서비스 워커와 네트워크로의 흐름을 보여줍니다.

'Cache Only'의 반대 의미 '네트워크만'이고 이 경우 요청이 서비스 워커 캐시와의 상호작용 없이 서비스 워커를 통해 네트워크로 전달됩니다. 이는 마크업과 같이 콘텐츠를 최신 상태로 유지하는 데 좋은 전략입니다. 단점은 사용자가 오프라인일 때는 작동하지 않는다는 것입니다.

요청이 네트워크로 전달되면 일치하는 요청에 대해 event.respondWith를 호출하지 않습니다. 명시적으로 표현하려면 네트워크로 전달하려는 요청의 fetch 이벤트 콜백에서 빈 return;를 칠 수 있습니다. 'Cache Only' 옵션에서 사전 캐시되지 않은 요청에 대한 전략 데모입니다.

먼저 캐시한 후 네트워크로 대체

캐시에 없는 경우 페이지에서, 서비스 워커, 캐시 및 네트워크로의 흐름을 보여줍니다.

이 전략에서는 더 많은 작업을 수행할 수 있습니다. 일치하는 요청의 경우 프로세스는 다음과 같습니다.

  1. 요청이 캐시에 도달합니다. 애셋이 캐시에 있으면 캐시에서 제공합니다.
  2. 요청이 캐시에 없는 경우 네트워크로 이동합니다.
  3. 네트워크 요청이 완료되면 캐시에 추가합니다. 네트워크에서 응답을 반환합니다

이 전략의 예는 라이브 데모:

// 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;
  }
});

이 예에서는 이미지만 다루지만 이는 모든 정적 자산 (예: CSS, JavaScript, 이미지 및 글꼴)에 적용할 수 있는 훌륭한 전략입니다. 특히 해시 버전이 지정된 포드를 제공합니다 HTTP 캐시가 시작될 수 있는 서버에서 콘텐츠 최신성 검사를 생략하여 변경할 수 없는 자산에 대해 속도를 향상합니다. 무엇보다 캐시된 애셋은 오프라인에서도 사용할 수 있습니다.

네트워크 우선, 캐시로 대체

네트워크에서 사용할 수 없는 경우 페이지에서, 서비스 워커, 네트워크, 캐시로 이동하는 흐름을 표시합니다.

'캐시 우선, 네트워크 두 번째'로 바꾸려면 고양이가 머리 위에 있다면 '네트워크 우선, 캐시 두 번째'가 되고 말씀드리겠습니다.

  1. 먼저 요청을 위해 네트워크로 이동한 다음 응답을 캐시에 배치합니다.
  2. 나중에 오프라인 상태라면 캐시에서 해당 응답의 최신 버전으로 돌아갑니다.

이 전략은 다음과 같은 HTML 또는 API 요청에 적합합니다. 가장 최신 버전의 리소스가 필요할 때 사용 가능한 최신 버전에 대한 오프라인 액세스 권한을 부여하고 싶을 수 있습니다. HTML 요청에 적용하면 다음과 같이 표시될 수 있습니다.

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

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

데모를 통해 사용해 볼 수 있습니다. 먼저 페이지로 이동합니다. HTML 응답이 캐시에 배치되기 전에 새로고침해야 할 수도 있습니다. 그런 다음 개발자 도구에서 오프라인 연결 시뮬레이션 다시 새로고침합니다 사용 가능한 마지막 버전이 캐시에서 즉시 제공됩니다.

오프라인 기능이 중요한 상황에서는 그러나 최신 버전의 마크업 또는 API 데이터에 대한 액세스와 이 기능 간의 균형을 맞춰야 합니다. "네트워크 우선, 캐시 두 번째" 이 목표를 달성하는 견고한 전략입니다.

재검증 중 비활성

페이지에서부터 서비스 워커로, 캐시로 이동한 후 네트워크에서 캐시로 이동하는 흐름을 보여줍니다.

지금까지 살펴본 전략 중 'Stale-while-revalidate' 가장 복잡합니다 마지막 두 가지 전략과 몇 가지 면에서 비슷합니다. 절차는 리소스에 대한 액세스 속도에 우선순위를 두고 백그라운드에서 최신 상태로 유지됩니다 이 전략은 다음과 같습니다.

  1. 애셋에 대한 첫 번째 요청에서 애셋을 네트워크에서 가져옵니다. 캐시에 배치하고 네트워크 응답을 반환합니다.
  2. 후속 요청 시에는 먼저 캐시에서 애셋을 제공한 다음 '백그라운드에서' 네트워크에서 다시 요청하고 자산의 캐시 항목을 업데이트합니다.
  3. 이후 요청의 경우 이전 단계에서 캐시에 배치된 네트워크에서 가져온 마지막 버전을 수신합니다.

이것은 최신 상태를 유지하는 중요한 항목을 위한 훌륭한 전략입니다. 중요하지 않습니다. 소셜 미디어 사이트에서 아바타 같은 것을 생각해 보세요. 사용자가 이리저리 돌아다닐 때 업데이트됩니다. 최신 버전이 모든 요청에 반드시 필요한 것은 아닙니다.

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

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

자세한 내용은 또 다른 라이브 데모, 특히 브라우저의 개발자 도구에서 네트워크 탭에 주의를 기울이면 및 CacheStorage 뷰어 (브라우저의 개발자 도구에 이러한 도구가 있는 경우)가 포함됩니다.

Workbox로 나아가세요!

이 문서에서는 서비스 워커의 API에 대한 검토를 마무리합니다. 관련 API도 제공합니다 즉, 서비스 워커를 직접 사용하여 Workbox를 조작하는 방법을 충분히 배웠다는 것을 의미합니다.