기본적으로 서비스 워커 수명 주기에서는 업데이트된 서비스 워커가 발견되고 설치될 때 업데이트된 서비스 워커가 활성화되고 제어되기 전에 현재 서비스 워커가 제어하는 열려 있는 모든 탭을 닫거나 탐색을 수행해야 합니다.
대부분의 경우 당연히 이 작업이 이루어지도록 하는 것이 괜찮지만, 경우에 따라 사용자에게 보류 중인 서비스 워커 업데이트가 있음을 미리 알리고 새 서비스 워커로 전환하는 프로세스를 자동화하는 것이 좋습니다. 이렇게 하려면 페이지와 서비스 워커에 코드를 추가해야 합니다.
페이지에 삽입할 코드
다음 코드는 workbox-window
의 CDN 호스팅 버전에서 가져온 JavaScript 모듈을 사용하여 인라인 <script>
요소에서 실행됩니다. workbox-window
를 사용하여 서비스 워커를 등록하고 서비스 워커가 대기 단계에서 멈추면 반응합니다. 대기 중인 서비스 워커가 발견되면 이 코드는 사이트의 업데이트된 버전을 사용할 수 있음을 사용자에게 알리고 새로고침하라는 메시지를 표시합니다.
<!-- This script tag uses JavaScript modules, so the proper `type` attribute value is required -->
<script type="module">
// This code sample uses features introduced in Workbox v6.
import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js');
let registration;
const showSkipWaitingPrompt = async (event) => {
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
wb.addEventListener('controlling', () => {
// At this point, reloading will ensure that the current
// tab is loaded under the control of the new service worker.
// Depending on your web app, you may want to auto-save or
// persist transient state before triggering the reload.
window.location.reload();
});
// When `event.wasWaitingBeforeRegister` is true, a previously
// updated service worker is still waiting.
// You may want to customize the UI prompt accordingly.
// This code assumes your app has a promptForUpdate() method,
// which returns true if the user wants to update.
// Implementing this is app-specific; some examples are:
// https://open-ui.org/components/alert.research or
// https://open-ui.org/components/toast.research
const updateAccepted = await promptForUpdate();
if (updateAccepted) {
wb.messageSkipWaiting();
}
};
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', (event) => {
showSkipWaitingPrompt(event);
});
wb.register();
}
</script>
수락하면 messageSkipWaiting()
는 대기 중인 서비스 워커에 self.skipWaiting()
를 호출하도록 지시합니다. 즉, 서비스가 활성화되기 시작합니다. 활성화되면 새 서비스 워커가 기존 클라이언트를 제어하고 workbox-window
에서 controlling
이벤트를 트리거합니다. 이 경우 현재 페이지는 사전 캐시된 모든 자산의 최신 버전과 업데이트된 서비스 워커에 있는 새로운 라우팅 로직을 사용하여 다시 로드됩니다.
서비스 워커에 넣을 코드
페이지의 이전 섹션에서 코드를 가져온 후에는 대기 단계를 건너뛸 시기를 알려주는 코드를 서비스 워커에 추가해야 합니다. workbox-build
의 generateSW
를 사용 중이며 skipWaiting
옵션이 false
(기본값)로 설정되어 있는 경우 아래 코드가 생성된 서비스 워커 파일에 자동으로 포함되므로 계속 사용해도 됩니다.
injectManifest
모드에서 Workbox의 빌드 도구 중 하나와 함께 자체 서비스 워커를 작성하는 경우 다음 코드를 직접 추가해야 합니다.
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
이는 workbox-window
에서 type
값이 SKIP_WAITING
인 서비스 워커로 전송된 메시지를 리슨하고 이 경우 self.skipWaiting()
를 호출합니다. 이전 코드 샘플에 표시된 workbox-window
의 messageSkipWaiting()
메서드가 이 메시지를 전송합니다.
프롬프트를 표시해야 하나요?
이는 서비스 워커를 배포하는 모든 애플리케이션이 따라야 하는 패턴이 아닙니다. 서비스 워커 업데이트에서 페이지를 새로고침하지 못하면 예기치 않은 동작이 발생할 수 있는 일부 시나리오에 해당됩니다. 새로고침 메시지 표시 여부를 결정하는 엄격한 규칙은 없지만 다음과 같은 몇 가지 상황이 적합합니다.
- 사전 캐싱을 광범위하게 사용합니다. 정적 애셋의 경우 나중에 탐색 요청에 네트워크 중심 또는 네트워크 전용 전략을 사용하고 정적 애셋을 지연 로드하는 경우 문제가 발생할 수 있습니다. 이로 인해 버전이 지정된 애셋이 변경될 수도 있고 서비스 워커가 이를 사전 캐시하지 않을 수도 있습니다. 여기에서 새로고침 버튼을 제공하면 예기치 않은 동작을 방지할 수 있습니다.
- 사전 캐시된 HTML을 제공하는 경우 이 경우 업데이트된 서비스 워커가 제어할 때까지 해당 HTML에 대한 업데이트가 인식되지 않으므로 서비스 워커 업데이트에 새로고침 버튼을 제공하는 것을 적극 고려해야 합니다.
- 주로 런타임 캐싱에 의존하지 않는 경우 런타임 시 리소스를 캐시할 때는 사용자에게 새로고침해야 한다고 알릴 필요가 없습니다. 버전이 지정된 애셋은 변경되면 탐색 요청이 네트워크 우선 또는 네트워크 전용 전략을 사용한다는 가정하에 당연히 런타임 캐시에 추가됩니다.
- stale-while-revalidate 전략을 사용할 때
workbox-broadcast-update
모듈을 사용하여 서비스 워커 업데이트를 사용자에게 알리는 것이 좋습니다.
서비스 워커에 대한 업데이트를 사용자에게 알려야 하는지 여부는 애플리케이션과 애플리케이션의 고유한 요구사항에 따라 다릅니다. 새 서비스 워커를 푸시할 때 사용자가 이상한 동작을 하는 것을 발견하면 아마도 사용자에게 알려야 하는 가장 좋은 신호일 것입니다.