광고 게재중

프로그레시브 웹 앱의 핵심 측면은 안정적이라는 점입니다. 자산을 빠르게 로드하여 네트워크 상태가 좋지 않더라도 사용자의 참여를 유도하고 피드백을 즉시 제공할 수 있습니다. 어떻게 이런 일이 가능한가요? 서비스 워커 fetch 이벤트 덕분에

브라우저 지원

  • Chrome: 40. <ph type="x-smartling-placeholder">
  • Edge: 17. <ph type="x-smartling-placeholder">
  • Firefox: 44. <ph type="x-smartling-placeholder">
  • Safari: 11.1. <ph type="x-smartling-placeholder">

소스

fetch 이벤트를 사용하면 동일한 출처 및 교차 출처 요청에서 모두 서비스 워커의 범위에서 PWA가 생성한 모든 네트워크 요청을 가로챌 수 있습니다. 탐색 및 자산 요청 외에도, 설치된 서비스 워커에서 가져오기를 수행하면 사이트의 첫 번째 로드 후 페이지 방문을 네트워크 호출 없이 렌더링할 수 있습니다.

fetch 핸들러는 URL과 HTTP 헤더를 포함한 앱의 모든 요청을 수신하고 앱 개발자가 요청 처리 방법을 결정할 수 있도록 합니다.

서비스 워커는 클라이언트와 네트워크 사이에 위치합니다.

서비스 워커는 요청을 네트워크로 전달하거나, 이전에 캐시된 응답으로 응답하거나, 새 응답을 만들 수 있습니다. 선택은 사용자님의 몫입니다. 다음 예를 참조하세요.

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});
드림

요청에 응답

요청이 서비스 워커에 들어오면 다음 두 가지 작업을 할 수 있습니다. 이를 무시할 수도 있고, 그렇게 해서 네트워크로 전송되도록 하거나, 사용자가 이에 응답할 수도 있습니다. 서비스 워커 내에서 요청에 응답하는 것은 사용자가 오프라인 상태일 때도 무엇을 선택할지, 그리고 그 요청이 PWA로 반환되는 방법을 선택하는 방법입니다.

수신 요청에 응답하려면 다음과 같이 fetch 이벤트 핸들러 내에서 event.respondWith()를 호출합니다.

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

respondWith()는 동기식으로 호출해야 하며 응답 객체를 반환해야 합니다. 그러나 비동기 호출 내에서와 같이 가져오기 이벤트 핸들러가 완료된 후에는 respondWith()를 호출할 수 없습니다. 전체 응답을 기다려야 하는 경우 응답으로 해결되는 프로미스를 respondWith()에 전달하면 됩니다.

응답 만들기

Fetch API를 사용하여 JavaScript 코드에서 HTTP 응답을 만들 수 있으며 Cache Storage API를 사용하여 응답을 캐시하고 웹 서버에서 들어오는 것처럼 반환할 수 있습니다.

응답을 만들려면 새 Response 객체를 만들고 본문과 옵션(예: 상태, 헤더)을 설정합니다.

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)
드림

캐시에서 응답

이제 서비스 워커에서 HTTP 응답을 제공하는 방법을 알게 되었으니 이제 Caching Storage 인터페이스를 사용하여 기기에 애셋을 저장해야 합니다.

캐시 저장소 API를 사용하여 PWA에서 수신한 요청을 캐시에서 사용할 수 있는지 확인하고, 사용할 수 있는 경우 이를 사용하여 respondWith()에 응답할 수 있습니다. 이렇게 하려면 먼저 캐시 내에서 검색해야 합니다. 최상위 caches 인터페이스에서 사용할 수 있는 match() 함수는 원본의 모든 스토어 또는 열려 있는 단일 캐시 객체에서 모든 스토어를 검색합니다.

match() 함수는 HTTP 요청 또는 URL을 인수로 받고 해당 키와 연결된 응답으로 확인되는 프로미스를 반환합니다.

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});
드림

캐싱 전략

브라우저 캐시에서만 파일을 제공하는 것이 모든 사용 사례에 적합한 것은 아닙니다. 예를 들어 사용자 또는 브라우저가 캐시를 제거할 수 있습니다. 따라서 PWA에 애셋을 제공하기 위한 자체 전략을 정의해야 합니다. 하나의 캐싱 전략으로 제한되지 않습니다. 다른 URL 패턴에 대해 서로 다른 URL을 정의할 수 있습니다. 예를 들어 최소 UI 애셋, API 호출, 이미지 및 데이터 URL에 대한 전략을 각각 설정할 수 있습니다. 이렇게 하려면 ServiceWorkerGlobalScope.onfetch에서 event.request.url를 읽고 정규 표현식 또는 URL 패턴을 통해 파싱합니다. 이 문서 작성 시점을 기준으로 URL 패턴은 일부 플랫폼에서만 지원됩니다.

가장 일반적인 전략은 다음과 같습니다.

캐시 우선
캐시된 응답을 먼저 검색하고 응답을 찾을 수 없는 경우 네트워크로 대체합니다.
네트워크 우선
먼저 네트워크에 응답을 요청하고, 반환되는 응답이 없으면 캐시의 응답을 확인합니다.
재검증 중 비활성
백그라운드에서 최신 버전을 요청하고 다음번에 애셋이 요청될 때 사용할 수 있도록 캐시에 저장하는 동안 캐시에서 응답을 제공합니다.
네트워크 전용
항상 네트워크 응답 또는 오류를 포함하여 응답합니다. 캐시는 절대 참조되지 않습니다.
캐시 전용
항상 캐시의 응답 또는 오류가 발생하여 응답합니다. 네트워크에는 절대 컨설트되지 않습니다. 이 전략을 사용하여 제공될 애셋은 요청되기 전에 캐시에 추가되어야 합니다.

캐시 우선

이 전략을 사용하면 서비스 워커가 캐시에서 일치하는 요청을 찾고, 캐시된 경우 해당 응답을 반환합니다. 그렇지 않으면 네트워크에서 응답을 검색합니다 (선택적으로 향후 호출을 위해 캐시를 업데이트함). 캐시 응답과 네트워크 응답이 모두 없으면 요청에서 오류가 발생합니다. 네트워크로 이동하지 않고 애셋을 제공하는 것이 더 빠른 경향이 있으므로 이 전략은 최신성보다 성능을 우선시합니다.

캐시 우선 전략

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

네트워크 우선

이 전략은 캐시 우선 전략을 미러링합니다. 요청이 네트워크에서 수행될 수 있는지 확인하고 처리할 수 없는 경우 캐시에서 검색하려고 시도합니다. 캐시가 우선입니다. 네트워크 응답과 캐시 응답이 모두 없으면 요청에서 오류가 발생합니다. 네트워크에서 응답을 가져오는 것은 일반적으로 캐시에서 응답을 가져오는 것보다 느리며, 이 전략은 성능 대신 업데이트된 콘텐츠를 우선시합니다.

네트워크 우선 전략

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

재검증 중 비활성

비활성 기간 재검증 전략은 캐시된 응답을 즉시 반환한 다음 네트워크에서 업데이트를 확인하고 캐시된 응답이 있는 경우 이를 대체합니다. 이 전략은 항상 네트워크를 요청합니다. 캐시된 리소스가 발견되더라도, 캐시에 있는 내용을 네트워크에서 수신한 내용으로 업데이트하려고 시도하고 다음 요청에서 업데이트된 버전을 사용하려고 하기 때문입니다. 따라서 이 전략은 캐시 우선 전략의 빠른 제공의 이점을 활용하고 백그라운드에서 캐시를 업데이트할 수 있는 방법을 제공합니다.

재검증 중 비활성 전략

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})
드림

네트워크 전용

네트워크 전용 전략은 서비스 워커나 Cache Storage API가 없는 브라우저가 작동하는 방식과 유사합니다. 요청은 네트워크에서 리소스를 가져올 수 있는 경우에만 리소스를 반환합니다. 이는 온라인 전용 API 요청과 같은 리소스에 유용할 때가 많습니다.

네트워크만 전략

캐시 전용

캐시 전용 전략은 요청이 네트워크로 이동하지 않도록 합니다. 수신되는 모든 요청은 미리 채워진 캐시 항목으로 응답됩니다. 다음 코드는 fetch 이벤트 핸들러를 캐시 저장소의 match 메서드와 함께 사용하여 캐시만 응답합니다.

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});
드림

캐시 전용 전략입니다.

맞춤 전략

위의 방법은 일반적인 캐싱 전략이지만, 서비스 워커와 요청 처리 방식은 사용자가 맡습니다. 이 중에서 원하는 것이 없으면 직접 만드세요.

예를 들어, 응답이 설정된 기준 내에 있는 경우에만 시간 제한이 있는 네트워크 우선 전략을 사용하여 업데이트된 콘텐츠의 우선순위를 정할 수 있습니다. 또한 캐시된 응답을 네트워크 응답과 병합하고 서비스 워커로부터 복잡한 응답을 빌드할 수 있습니다.

저작물 업데이트

PWA의 캐시된 애셋을 최신 상태로 유지하기는 어려울 수 있습니다. 재검증하는 동안 오래된 전략이 이를 위한 한 가지 방법이지만 유일한 방법은 아닙니다. 업데이트 챕터에서는 앱의 콘텐츠와 애셋을 최신 상태로 유지하기 위한 다양한 기법을 알아봅니다.

리소스