기본적으로 서비스 워커 수명 주기에서는 업데이트된 서비스 워커를 찾아 설치할 때 업데이트된 서비스 워커가 활성화되고 제어되기 전에 현재 서비스 워커가 제어하고 있는 모든 열린 탭을 닫거나 탐색하도록 요구합니다.
대부분의 경우 당연히 이 작업을 허용하는 것이 괜찮을 수도 있지만, 경우에 따라 사용자에게 대기 중인 서비스 워커 업데이트가 있음을 미리 알려주고 새 서비스 워커로 전환하는 프로세스를 자동화하고 싶을 수 있습니다. 이렇게 하려면 페이지에 코드를 몇 개 추가하고 서비스 워커를 추가해야 합니다.
페이지에 삽입할 코드
다음 코드는 CDN 호스팅 버전의 workbox-window
에서 가져온 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-when-revalidate 전략을 사용할 때는
workbox-broadcast-update
모듈을 사용해 서비스 워커 업데이트를 사용자에게 알릴 수 있습니다.
서비스 워커에 대한 업데이트를 사용자에게 알려야 하는지 여부는 애플리케이션과 애플리케이션의 고유 요구사항에 따라 다릅니다. 새 서비스 워커를 푸시할 때 사용자가 이상한 행동을 하는 것을 발견한 경우, 이는 사용자에게 이를 알려야 하는 최선의 신호일 것입니다.