서비스 워커의 삶

서비스 워커의 수명 주기를 이해하지 않고는 어떤 작업을 수행하고 있는지 파악하기는 어렵습니다. 내부가 불분명하고 임의적으로 보일 수도 있습니다. 다른 브라우저 API와 마찬가지로 서비스 워커 동작은 잘 정의되어 있다는 점을 기억하는 데 도움이 됩니다. 오프라인 애플리케이션을 가능하게 하고, 사용자 환경을 방해하지 않고 업데이트를 촉진할 수 있습니다.

Workbox를 살펴보기 전에 서비스 워커 수명 주기를 이해하여 Workbox의 기능이 타당하도록 하는 것이 중요합니다.

용어 정의

서비스 워커 수명 주기를 시작하기 전에 해당 수명 주기의 작동 방식에 대한 몇 가지 용어를 정의하는 것이 좋습니다.

컨트롤 및 범위

제어 개념은 서비스 워커의 작동 방식을 이해하는 데 매우 중요합니다. 서비스 워커가 제어하는 것으로 설명된 페이지는 서비스 워커가 자신을 대신하여 네트워크 요청을 가로챌 수 있는 페이지입니다. 서비스 워커가 존재하고 주어진 범위 내의 페이지에서 작업을 수행할 수 있습니다.

범위

서비스 워커의 범위는 웹 서버상의 위치에 따라 결정됩니다. 서비스 워커가 /subdir/index.html에 있는 페이지에서 실행되고 /subdir/sw.js에 있는 경우 서비스 워커의 범위는 /subdir/입니다. 실제 범위의 개념을 보려면 다음 예를 확인하세요.

  1. 다음으로 이동 https://service-worker-scope-viewer.glitch.me/subdir/index.html. 페이지를 제어하는 서비스 워커가 없다는 메시지가 나타납니다. 그러나 이 페이지는 https://service-worker-scope-viewer.glitch.me/subdir/sw.js에서 서비스 워커를 등록합니다.
  2. 페이지를 새로고침합니다. 서비스 워커가 등록되었고 현재 활성 상태이기 때문에 페이지를 제어하고 있습니다. 서비스 워커의 범위, URL이 표시됩니다. 참고: 페이지를 새로고침해야 하는 것은 범위와 관련이 없습니다. 서비스 워커 수명 주기보다 중요합니다.
  3. 이제 https://service-worker-scope-viewer.glitch.me/index.html로 이동합니다. 서비스 워커가 이 출처에 등록되었지만 현재 서비스 워커가 없다는 메시지가 여전히 있습니다. 이 페이지는 등록된 서비스 워커의 범위 내에 있지 않기 때문입니다.

범위는 서비스 워커가 제어하는 페이지를 제한합니다. 이 예에서 /subdir/sw.js에서 로드된 서비스 워커는 /subdir/ 또는 하위 트리에 있는 페이지만 제어할 수 있습니다.

위의 내용은 기본적으로 범위 지정이 작동하는 방식이며, 하지만 최대 허용 범위는 Service-Worker-Allowed 응답 헤더, gcloud 명령어를 사용하여 register 메서드에 scope 옵션을 추가합니다.

서비스 워커 범위를 원본의 하위 집합으로 제한할 만한 합당한 이유가 없다면 웹 서버의 루트 디렉터리에서 서비스 워커를 로드하여 그 범위가 최대한 넓게 하고, Service-Worker-Allowed 헤더는 신경 쓰지 않아도 됩니다. 이렇게 하면 모든 사람이 훨씬 간단하게 작업할 수 있습니다.

클라이언트

서비스 워커가 페이지를 제어한다고 말하면, 실제로 클라이언트를 제어하는 것입니다. 클라이언트란 URL이 해당 서비스 워커의 범위에 속하는 열린 페이지를 말합니다. 특히 WindowClient의 인스턴스입니다.

새 서비스 워커의 수명 주기

서비스 워커가 페이지를 제어하기 위해서는 말하자면 먼저 생겨나야 합니다. 활성 서비스 워커가 없는 웹사이트에 새로운 서비스 워커가 배포되면 어떻게 되는지부터 살펴보겠습니다.

등록

등록은 서비스 워커 수명 주기의 첫 단계입니다.

<!-- In index.html, for example: -->
<script>
  // Don't register the service worker
  // until the page has fully loaded
  window.addEventListener('load', () => {
    // Is service worker available?
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js').then(() => {
        console.log('Service worker registered!');
      }).catch((error) => {
        console.warn('Error registering service worker:');
        console.warn(error);
      });
    }
  });
</script>

이 코드는 기본 스레드에서 실행되며 다음 작업을 실행합니다.

  1. 사용자가 웹사이트를 처음 방문하는 것은 등록된 서비스 워커가 없는 상태에서 이루어지기 때문입니다. 페이지가 완전히 로드될 때까지 기다린 후에 등록할 수 있습니다. 이렇게 하면 서비스 워커가 무언가를 사전 캐시하는 경우 대역폭 경합을 방지할 수 있습니다.
  2. 서비스 워커는 잘 지원되지만 빠른 검사를 통해 지원되지 않는 브라우저에서 발생하는 오류를 방지할 수 있습니다.
  3. 페이지가 완전히 로드되고 서비스 워커가 지원되는 경우 /sw.js를 등록합니다.

이해해야 할 몇 가지 주요 사항은 다음과 같습니다.

  • 서비스 워커는 HTTPS 또는 localhost를 통해서만 사용할 수 있습니다.
  • 서비스 워커의 콘텐츠에 구문 오류가 포함되어 있으면 등록이 실패하고 서비스 워커가 삭제됩니다.
  • 알림: 서비스 워커는 범위 내에서 작동합니다. 여기서 범위는 루트 디렉터리에서 로드되었으므로 전체 출처입니다.
  • 등록이 시작되면 서비스 워커 상태가 'installing'로 설정됩니다.

등록이 완료되면 설치가 시작됩니다.

설치

서비스 워커는 등록 후 install 이벤트 발생 install는 서비스 워커당 한 번만 호출되며 업데이트될 때까지 다시 실행되지 않습니다. install 이벤트의 콜백은 addEventListener를 사용하여 작업자 범위에 등록할 수 있습니다.

// /sw.js
self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v1';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v1'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.bc7b80b7.css',
      '/css/home.fe5d0b23.css',
      '/js/home.d3cc4ba4.js',
      '/js/jquery.43ca4933.js'
    ]);
  }));
});

그러면 새 Cache 인스턴스가 생성되고 애셋이 사전 캐시됩니다. 나중에 프리캐싱에 대해 이야기할 많은 기회가 있을 것입니다. 그럼 이제 데이터 애널리스트의 event.waitUntil event.waitUntil는 프로미스를 수락합니다. 해당 프라미스가 해결될 때까지 기다립니다 이 예에서 해당 프라미스는 두 가지 비동기 작업을 수행합니다.

  1. 'MyFancyCache_v1'라는 새 Cache 인스턴스를 만듭니다.
  2. 캐시가 생성된 후 자산 URL의 배열은 비동기 addAll 메서드

event.waitUntil에 전달된 프로미스가 다음에 해당하는 경우 설치에 실패합니다. 거부됨. 이 경우, 서비스 워커는 삭제됩니다.

프라미스가 해결되는 경우 설치가 성공하고 서비스 워커의 상태가 'installed'로 변경된 후 활성화됩니다.

활성화

등록과 설치가 성공하면 서비스 워커가 활성화되고 상태가 'activating'가 됩니다. 활성화 도중에 서비스 워커의 activate 이벤트. 이 이벤트의 일반적인 작업은 오래된 캐시를 프루닝하는 것입니다. 완전히 새로운 서비스 워커의 경우 지금은 관련이 없지만 그리고 서비스 워커 업데이트에 대해 이야기할 때 더 확장될 것입니다.

새 서비스 워커의 경우 install가 성공한 직후 activate가 실행됩니다. 활성화가 완료되면 서비스 워커의 상태는 'activated'이 됩니다. 기본적으로 새 서비스 워커는 다음 탐색 또는 페이지 새로고침이 실행될 때까지 페이지를 제어하지 않습니다.

서비스 워커 업데이트 처리

첫 번째 서비스 워커가 배포되면 나중에 업데이트해야 할 수 있습니다 예를 들어 요청 처리 또는 사전 캐싱 로직이 변경되면 업데이트가 필요할 수 있습니다.

업데이트가 이루어지는 경우

브라우저는 다음과 같은 경우에 서비스 워커에 대한 업데이트를 확인합니다.

  • 사용자가 서비스 워커 범위 내의 페이지로 이동합니다.
  • navigator.serviceWorker.register() 드림 가 현재 설치된 서비스 워커와 다른 URL로 호출됩니다. 서비스 워커의 URL은 변경하지 마세요!
  • navigator.serviceWorker.register() 드림 설치된 서비스 워커와 동일한 URL로 호출되므로 범위가 다릅니다 다시 말하지만 가능하면 범위를 출처의 루트로 유지하여 이를 피하세요.
  • 'push' 또는 'sync'와 같은 이벤트인 경우 지난 24시간 이내에 트리거되었지만 아직 이러한 이벤트는 신경 쓰지 않아도 됩니다.

업데이트 방식

브라우저가 서비스 워커를 업데이트하는 시점을 아는 것이 중요하므로 '방법'도 마찬가지입니다. 서비스 워커의 URL 또는 범위가 변경되지 않았다고 가정하면, 현재 설치된 서비스 워커는 콘텐츠가 변경된 경우에만 새 버전으로 업데이트됩니다.

브라우저는 다음과 같은 두 가지 방법으로 변경사항을 감지합니다.

  • 다음에 의해 요청된 스크립트의 모든 바이트 단위 변경 importScripts(해당하는 경우).
  • 서비스 워커의 최상위 코드 변경 브라우저가 생성한 디지털 지문에 영향을 줍니다.

브라우저는 여기서 많은 어려운 작업을 수행합니다. 브라우저가 서비스 워커의 콘텐츠 변경을 안정적으로 감지하는 데 필요한 모든 것을 갖추도록 하려면 HTTP 캐시에 보관하라고 명령하지 않고 파일 이름을 변경하지 마세요. 서비스 워커의 범위 내에 있는 새 페이지로의 탐색이 있을 때 브라우저가 자동으로 업데이트 검사를 수행합니다.

수동으로 업데이트 검사 트리거

업데이트의 경우 등록 로직은 일반적으로 변경되지 않습니다. 하지만 한 가지 예외는 웹사이트에서 세션의 수명이 긴 경우입니다. 이러한 문제는 단일 페이지 애플리케이션에서 발생할 수 있으며 탐색 요청은 드물지만 애플리케이션에서 일반적으로 하나의 탐색 요청이 애플리케이션의 수명 주기가 시작될 때 발생하기 때문입니다. 이러한 상황에서는 기본 스레드에서 수동 업데이트가 트리거될 수 있습니다.

navigator.serviceWorker.ready.then((registration) => {
  registration.update();
});

기존 웹사이트의 경우 사용자 세션이 오래 지속되지 않는 경우 아마도 수동 업데이트를 트리거할 필요가 없을 것입니다.

설치

번들러를 사용해 정적 애셋을 생성할 때는 이러한 애셋의 이름에 해시가 포함되어 있고 framework.3defa9d2.js 등). 이러한 애셋 중 일부가 나중에 오프라인 액세스를 위해 사전 캐시된다고 가정해 보겠습니다. 이렇게 하려면 업데이트된 애셋을 사전 캐시하기 위해 서비스 워커 업데이트가 필요합니다.

self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v2';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v2'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.ced4aef2.css',
      '/css/home.cbe409ad.css',
      '/js/home.109defa4.js',
      '/js/jquery.38caf32d.js'
    ]);
  }));
});

두 가지 사항이 앞서 살펴본 첫 번째 install 이벤트 예와 다릅니다.

  1. 키가 'MyFancyCacheName_v2'인 새 Cache 인스턴스가 생성됩니다.
  2. 사전 캐시된 애셋 이름이 변경되었습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

한 가지 주의할 점은 업데이트된 서비스 워커가 이전 서비스 워커와 함께 설치된다는 것입니다. 즉, 이전 서비스 워커가 여전히 열려 있는 페이지를 제어하고 설치 후에 새 항목은 활성화될 때까지 대기 상태로 전환됩니다

기본적으로, 이전 서비스 워커로 제어 중인 클라이언트가 없는 경우 새 서비스 워커가 활성화됩니다. 해당 웹사이트의 열린 탭이 모두 닫히면 나타나는 오류입니다.

활성화

업데이트된 서비스 워커가 설치되고 대기 단계가 종료되면 활성화되고 이전 서비스 워커는 삭제됩니다. 업데이트된 서비스 워커의 activate 이벤트에서 수행하는 일반적인 작업은 기존 캐시를 프루닝하는 것입니다. 열려 있는 모든 Cache 인스턴스의 키를 가져와 이전 캐시를 삭제합니다. caches.keys 정의된 허용 목록에 없는 캐시를 삭제하고 caches.delete:

self.addEventListener('activate', (event) => {
  // Specify allowed cache keys
  const cacheAllowList = ['MyFancyCacheName_v2'];

  // Get all the currently active `Cache` instances.
  event.waitUntil(caches.keys().then((keys) => {
    // Delete all caches that aren't in the allow list:
    return Promise.all(keys.map((key) => {
      if (!cacheAllowList.includes(key)) {
        return caches.delete(key);
      }
    }));
  }));
});

오래된 캐시는 깔끔하게 정리되지 않습니다. 직접 해야 합니다. 그렇지 않으면 스토리지 할당량. 첫 번째 서비스 워커의 'MyFancyCacheName_v1'이 오래되었기 때문에 캐시 허용 목록이 업데이트되어 'MyFancyCacheName_v2'를 지정합니다. 이 작업을 수행하면 이름이 다른 캐시를 삭제합니다.

activate 이벤트는 이전 캐시가 삭제된 후에 완료됩니다. 이 시점에서 새 서비스 워커가 페이지를 제어하고 드디어 기존 제품을 교체합니다!

수명 주기는 계속하여

Workbox가 서비스 워커 배포 및 업데이트를 처리하는 데 사용되는지 여부 Service Worker API가 직접 사용되는 경우 서비스 워커 수명 주기를 이해하는 것이 도움이 됩니다. 이러한 이해를 바탕으로, 서비스 워커의 행동은 이상하기보다는 좀 더 논리적으로 보일 것입니다.

이 주제를 자세히 살펴보고 싶다면 이 전문 분야의 이 기사에서 확인할 수 있습니다. 서비스 수명 주기를 중심으로 하는 전체 작업에는 미묘한 차이가 있습니다. 알 수 있고 Workbox를 사용할 때 이러한 지식의 범위를 넓힐 수 있습니다.