서비스 워커의 수명 주기는 가장 복잡한 부분입니다. AI가 무엇을 하고 있는지, 어떤 이점이 있는지 모르면 AI가 나와 싸우고 있다고 느낄 수 있습니다. 하지만 작동 방식을 알게 되면 웹과 네이티브 패턴의 장점을 결합하여 사용자에게 원활하고 눈에 잘 띄지 않는 업데이트를 제공할 수 있습니다.
자세히 살펴보겠지만 각 섹션의 시작 부분에 있는 글머리기호로 알아야 할 내용의 대부분을 확인할 수 있습니다.
인텐트
수명 주기의 목적은 다음과 같습니다.
- 오프라인 우선을 지원합니다.
- 새 서비스 워커가 기존 워커를 방해하지 않고 준비되도록 허용합니다.
- 범위 내 페이지가 전체적으로 동일한 서비스 워커 (또는 서비스 워커 없음)로 제어되는지 확인합니다.
- 한 번에 하나의 사이트 버전만 실행되는지 확인합니다.
마지막 항목은 매우 중요합니다. 서비스 워커가 없으면 사용자가 사이트에 탭 하나를 로드한 후 나중에 다른 탭을 열 수 있습니다. 이로 인해 두 버전의 사이트가 동시에 실행될 수 있습니다. 이는 괜찮은 경우도 있지만 스토리지를 다루는 경우 공유 스토리지를 어떻게 관리해야 하는지에 대한 의견이 두 탭에서 서로 매우 달라질 수 있습니다. 이로 인해 오류가 발생하거나 심각한 경우 데이터가 손실될 수 있습니다.
첫 번째 서비스 워커
간단히 말하면 다음과 같습니다.
install
이벤트는 서비스 워커가 받는 첫 번째 이벤트이며 한 번만 발생합니다.installEvent.waitUntil()
에 전달된 프라미스는 설치 시간과 설치의 성공 또는 실패를 나타냅니다.- 서비스 워커는 설치가 완료되고 '활성' 상태가 될 때까지
fetch
및push
와 같은 이벤트를 수신하지 않습니다. - 기본적으로 페이지 요청 자체가 서비스 워커를 거치지 않는 한 페이지 가져오기는 서비스 워커를 거치지 않습니다. 따라서 서비스 워커의 효과를 보려면 페이지를 새로고침해야 합니다.
clients.claim()
는 이 기본값을 재정의하고 제어되지 않는 페이지를 제어할 수 있습니다.
다음 HTML을 살펴보세요.
<!DOCTYPE html>
An image will appear here in 3 seconds:
<script>
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered!', reg))
.catch(err => console.log('Boo!', err));
setTimeout(() => {
const img = new Image();
img.src = '/dog.svg';
document.body.appendChild(img);
}, 3000);
</script>
서비스 워커를 등록하고 3초 후에 강아지 이미지를 추가합니다.
서비스 워커 sw.js
는 다음과 같습니다.
self.addEventListener('install', event => {
console.log('V1 installing…');
// cache a cat SVG
event.waitUntil(
caches.open('static-v1').then(cache => cache.add('/cat.svg'))
);
});
self.addEventListener('activate', event => {
console.log('V1 now ready to handle fetches!');
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the cat SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/cat.svg'));
}
});
고양이 이미지를 캐시하고 /dog.svg
요청이 있을 때마다 제공합니다. 하지만 위 예시를 실행하면 페이지를 처음 로드할 때 강아지가 표시됩니다. 새로고침하면 고양이가 표시됩니다.
범위 및 제어
서비스 워커 등록의 기본 범위는 스크립트 URL을 기준으로 ./
입니다. 즉, //example.com/foo/bar.js
에 서비스 워커를 등록하면 기본 범위가 //example.com/foo/
입니다.
페이지, 작업자, 공유 작업자를 clients
라고 합니다. 서비스 워커는 범위 내에 있는 클라이언트만 제어할 수 있습니다. 클라이언트가 '제어'되면 가져오기가 범위 내 서비스 워커를 거칩니다. 클라이언트가 null 또는 서비스 워커 인스턴스인 navigator.serviceWorker.controller
를 통해 제어되는지 감지할 수 있습니다.
다운로드, 파싱, 실행
.register()
를 호출하면 첫 번째 서비스 워커가 다운로드됩니다. 스크립트가 다운로드 또는 파싱되지 않거나 초기 실행에서 오류가 발생하면 등록 약속이 거부되고 서비스 워커가 삭제됩니다.
Chrome의 DevTools에 콘솔과 애플리케이션 탭의 서비스 워커 섹션에 오류가 표시됩니다.

설치
서비스 워커가 받는 첫 번째 이벤트는 install
입니다. 작업자가 실행되는 즉시 트리거되며 서비스 워커당 한 번만 호출됩니다. 서비스 워커 스크립트를 변경하면 브라우저에서 이를 다른 서비스 워커로 간주하고 자체 install
이벤트를 가져옵니다. 업데이트는 나중에 자세히 다루겠습니다.
install
이벤트는 클라이언트를 제어하기 전에 필요한 모든 항목을 캐시할 수 있는 기회입니다. event.waitUntil()
에 전달하는 프라미스를 통해 브라우저에 설치가 완료된 시점과 설치가 성공했는지 여부를 알릴 수 있습니다.
프로미스가 거부되면 설치에 실패했다는 신호가 전달되고 브라우저에서 서비스 워커를 삭제합니다. 클라이언트를 제어하지 않습니다. 즉, cat.svg
가 fetch
이벤트의 캐시에 있는지 확인할 수 있습니다. 종속 항목입니다.
활성화
서비스 워커가 클라이언트를 제어하고 push
및 sync
와 같은 기능 이벤트를 처리할 준비가 되면 activate
이벤트가 발생합니다. 하지만 .register()
를 호출한 페이지가 제어되는 것은 아닙니다.
데모를 처음 로드할 때는 서비스 워커가 활성화된 후 dog.svg
가 요청되었지만 요청을 처리하지 않고 강아지 이미지가 계속 표시됩니다. 기본값은 일관성입니다. 페이지가 서비스 워커 없이 로드되면 하위 리소스도 로드되지 않습니다. 데모를 다시 로드하면 (즉, 페이지를 새로고침하면) 제어됩니다. 페이지와 이미지 모두 fetch
이벤트를 거치며 고양이가 표시됩니다.
clients.claim
서비스 워커가 활성화되면 서비스 워커 내에서 clients.claim()
를 호출하여 제어되지 않는 클라이언트를 제어할 수 있습니다.
다음은 activate
이벤트에서 clients.claim()
를 호출하는 위의 데모의 변형입니다. 처음에는 고양이가 표시되어야 합니다. 타이밍이 중요하므로 '해야'라고 말합니다. 이미지를 로드하려고 시도하기 전에 서비스 워커가 활성화되고 clients.claim()
이 적용되는 경우에만 고양이가 표시됩니다.
서비스 워커를 사용하여 페이지를 네트워크를 통해 로드하는 것과 다른 방식으로 로드하는 경우 clients.claim()
가 문제가 될 수 있습니다. 서비스 워커가 clients.claim()
없이 로드된 일부 클라이언트를 제어하게 되기 때문입니다.
서비스 워커 업데이트
간단히 말하면 다음과 같습니다.
- 다음 중 하나가 발생하면 업데이트가 트리거됩니다.
- 지원 범위 내 페이지로의 탐색
- 이전 24시간 이내에 업데이트 확인이 이루어지지 않은 경우
push
및sync
와 같은 기능 이벤트 - 서비스 워커 URL이 변경된 경우에만
.register()
를 호출합니다. 하지만 작업자 URL을 변경해서는 안 됩니다.
- Chrome 68 이상을 비롯한 대부분의 브라우저는 등록된 서비스 워커 스크립트의 업데이트를 확인할 때 기본적으로 캐싱 헤더를 무시합니다.
importScripts()
를 통해 서비스 워커 내부에 로드된 리소스를 가져올 때도 캐싱 헤더를 계속 준수합니다. 서비스 워커를 등록할 때updateViaCache
옵션을 설정하여 이 기본 동작을 재정의할 수 있습니다. - 서비스 워커가 브라우저에 이미 있는 서비스 워커와 바이트 단위로 다르면 업데이트된 것으로 간주됩니다. 가져온 스크립트/모듈도 포함하도록 확장하고 있습니다.
- 업데이트된 서비스 워커는 기존 서비스 워커와 함께 실행되며 자체
install
이벤트를 가져옵니다. - 새 작업자의 상태 코드가 정상이 아니거나 (예: 404) 파싱에 실패하거나, 실행 중에 오류가 발생하거나, 설치 중에 거부되면 새 작업자는 삭제되지만 현재 작업자는 활성 상태로 유지됩니다.
- 설치가 완료되면 업데이트된 작업자는 기존 작업자가 0명의 클라이언트를 제어할 때까지
wait
합니다. 새로고침 중에 클라이언트가 겹쳐집니다. self.skipWaiting()
는 대기를 방지합니다. 즉, 서비스 워커는 설치가 완료되는 즉시 활성화됩니다.
고양이 대신 말의 사진으로 응답하도록 서비스 워커 스크립트를 변경했다고 가정해 보겠습니다.
const expectedCaches = ['static-v2'];
self.addEventListener('install', event => {
console.log('V2 installing…');
// cache a horse SVG into a new cache, static-v2
event.waitUntil(
caches.open('static-v2').then(cache => cache.add('/horse.svg'))
);
});
self.addEventListener('activate', event => {
// delete any caches that aren't in expectedCaches
// which will get rid of static-v1
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (!expectedCaches.includes(key)) {
return caches.delete(key);
}
})
)).then(() => {
console.log('V2 now ready to handle fetches!');
})
);
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the horse SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/horse.svg'));
}
});
위의 데모를 확인하세요. 고양이 이미지가 계속 표시됩니다. 그 이유는 다음과 같습니다.
설치
캐시 이름을 static-v1
에서 static-v2
로 변경했습니다. 즉, 이전 서비스 워커가 여전히 사용 중인 현재 캐시의 항목을 덮어쓰지 않고 새 캐시를 설정할 수 있습니다.
이 패턴은 네이티브 앱이 실행 파일과 번들로 묶는 애셋과 유사한 버전별 캐시를 만듭니다. avatars
와 같이 버전별이 아닌 캐시가 있을 수도 있습니다.
대기 중
설치가 완료되면 업데이트된 서비스 워커는 기존 서비스 워커가 더 이상 클라이언트를 제어하지 않을 때까지 활성화를 지연합니다. 이 상태를 '대기 중'이라고 하며, 브라우저가 한 번에 하나의 서비스 워커 버전만 실행되도록 하는 방법입니다.
업데이트된 데모를 실행한 경우 V2 작업자가 아직 활성화되지 않았으므로 고양이 사진이 계속 표시됩니다. DevTools의 'Application'(애플리케이션) 탭에서 대기 중인 새 서비스 워커를 확인할 수 있습니다.

데모 탭이 하나만 열려 있더라도 페이지를 새로고침해도 새 버전이 적용되지 않습니다. 이는 브라우저 탐색의 작동 방식 때문입니다. 탐색할 때는 응답 헤더가 수신될 때까지 현재 페이지가 사라지지 않으며, 응답에 Content-Disposition
헤더가 있는 경우에도 현재 페이지가 유지될 수 있습니다. 이러한 중복으로 인해 현재 서비스 워커는 새로고침 중에 항상 클라이언트를 제어합니다.
업데이트를 받으려면 현재 서비스 워커를 사용하여 모든 탭을 닫거나 탭에서 나가세요. 그런 다음 데모로 다시 이동하면 말이 표시됩니다.
이 패턴은 Chrome 업데이트 방식과 유사합니다. Chrome 업데이트는 백그라운드에서 다운로드되지만 Chrome이 다시 시작될 때까지 적용되지 않습니다. 그동안에는 현재 버전을 중단 없이 계속 사용할 수 있습니다. 하지만 개발 중에는 번거로울 수 있습니다. 하지만 DevTools에는 이를 더 쉽게 하는 방법이 있습니다. 이 도움말의 뒷부분에서 다루겠습니다.
활성화
이는 이전 서비스 워커가 사라지고 새 서비스 워커가 클라이언트를 제어할 수 있게 되면 실행됩니다. 이때는 데이터베이스 이전, 캐시 삭제와 같이 이전 작업자가 사용 중이던 동안 할 수 없었던 작업을 수행하기에 적합합니다.
위의 데모에서는 예상되는 캐시 목록을 유지하고 activate
이벤트에서 다른 캐시를 모두 삭제하여 이전 static-v1
캐시를 삭제합니다.
event.waitUntil()
에 약속을 전달하면 약속이 해결될 때까지 기능 이벤트 (fetch
, push
, sync
등)가 버퍼링됩니다. 따라서 fetch
이벤트가 실행되면 활성화가 완전히 완료된 것입니다.
대기 단계 건너뛰기
대기 단계는 한 번에 하나의 사이트 버전만 실행한다는 것을 의미하지만 이 기능이 필요하지 않은 경우 self.skipWaiting()
를 호출하여 새 서비스 워커가 더 빨리 활성화되도록 할 수 있습니다.
이렇게 하면 서비스 워커가 현재 활성 작업자를 꺼내고 대기 단계에 진입하는 즉시 (또는 이미 대기 단계에 있는 경우 즉시) 자체적으로 활성화됩니다. 작업자가 설치를 건너뛰지 않고 기다리기만 합니다.
대기 중 또는 대기 전에 skipWaiting()
를 호출하는 경우 상관없습니다. install
이벤트에서 호출하는 것이 일반적입니다.
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});
하지만 서비스 워커에 대한 postMessage()
의 결과로 호출하는 것이 좋습니다. 즉, 사용자 상호작용 후 skipWaiting()
를 실행하려고 합니다.
skipWaiting()
를 사용하는 데모 탐색하지 않아도 소 사진이 표시됩니다. clients.claim()
와 마찬가지로 경합이므로 페이지에서 이미지를 로드하려고 시도하기 전에 새 서비스 워커가 가져오고 설치하고 활성화하는 경우에만 소가 표시됩니다.
수동 업데이트
앞서 언급한 것처럼 브라우저는 탐색 및 기능 이벤트 후 자동으로 업데이트를 확인하지만 수동으로 트리거할 수도 있습니다.
navigator.serviceWorker.register('/sw.js').then(reg => {
// sometime later…
reg.update();
});
사용자가 사이트를 새로고침하지 않고 오랫동안 사용할 것으로 예상되는 경우 일정 간격 (예: 시간당)으로 update()
를 호출하는 것이 좋습니다.
서비스 워커 스크립트의 URL을 변경하지 않음
캐싱 권장사항에 관한 게시물을 읽은 경우 서비스 워커의 각 버전에 고유한 URL을 지정하는 것이 좋습니다. 이렇게 하지 마세요. 이는 일반적으로 서비스 워커에 좋지 않은 방법입니다. 현재 위치에서 스크립트를 업데이트하면 됩니다.
다음과 같은 문제가 발생할 수 있습니다.
index.html
는sw-v1.js
를 서비스 워커로 등록합니다.sw-v1.js
는index.html
를 캐시하고 제공하므로 오프라인 우선으로 작동합니다.index.html
를 업데이트하여 새롭고 반짝이는sw-v2.js
를 등록합니다.
위와 같이 하면 sw-v1.js
가 캐시에서 이전 버전의 index.html
를 제공하므로 사용자는 sw-v2.js
를 받지 못합니다. 서비스 워커를 업데이트하기 위해 서비스 워커를 업데이트해야 하는 상황에 처했습니다. 웩.
하지만 위의 데모에서는 서비스 워커의 URL을 변경했습니다. 이렇게 하면 데모를 위해 버전을 전환할 수 있습니다. 프로덕션에서는 하지 않을 작업입니다.
간편한 개발
서비스 워커 수명 주기는 사용자를 염두에 두고 빌드되지만 개발 중에는 약간 번거로울 수 있습니다. 다행히 다음과 같은 몇 가지 도구가 있습니다.
새로고침 시 업데이트
이 사진이 제일 좋아요.

이렇게 하면 수명 주기가 개발자 친화적으로 변경됩니다. 각 탐색은 다음을 실행합니다.
- 서비스 워커를 다시 가져옵니다.
- 바이트가 동일하더라도 새 버전으로 설치합니다. 즉,
install
이벤트가 실행되고 캐시가 업데이트됩니다. - 새 서비스 워커가 활성화되도록 대기 단계를 건너뜁니다.
- 페이지를 탐색합니다.
즉, 두 번 새로고침하거나 탭을 닫을 필요 없이 각 탐색 (새로고침 포함)에서 업데이트를 받을 수 있습니다.
대기 건너뛰기

대기 중인 작업자가 있는 경우 DevTools에서 '대기 건너뛰기'를 클릭하여 즉시 '활성'으로 승격할 수 있습니다.
Shift-새로고침
페이지를 강제로 새로고침하면 (shift-reload) 서비스 워커가 완전히 우회됩니다. 제어할 수 없습니다. 이 기능은 사양에 포함되어 있으므로 다른 서비스 워커 지원 브라우저에서도 작동합니다.
업데이트 처리
서비스 워커는 확장 가능한 웹의 일부로 설계되었습니다. 브라우저 개발자는 웹 개발에 있어 웹 개발자보다 뛰어나지 않다는 점을 잘 알고 있습니다. 따라서 Google이 좋아하는 패턴을 사용하여 특정 문제를 해결하는 협소한 상위 수준 API를 제공해서는 안 되며 대신 브라우저의 핵심에 액세스할 수 있도록 하고 개발자의 사용자에게 가장 적합한 방식으로 원하는 방식으로 작업할 수 있도록 해야 합니다.
따라서 최대한 많은 패턴을 사용 설정하기 위해 전체 업데이트 주기를 관찰할 수 있습니다.
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.active; // the active worker, or undefined
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;
newWorker.state;
// "installing" - the install event has fired, but not yet complete
// "installed" - install complete
// "activating" - the activate event has fired, but not yet complete
// "activated" - fully active
// "redundant" - discarded. Either failed install, or it's been
// replaced by a newer version
newWorker.addEventListener('statechange', () => {
// newWorker.state has changed
});
});
});
navigator.serviceWorker.addEventListener('controllerchange', () => {
// This fires when the service worker controlling this page
// changes, eg a new worker has skipped waiting and become
// the new active worker.
});
수명 주기는 계속 진행됩니다.
보시다시피 서비스 워커 수명 주기를 이해하는 것이 좋습니다. 이를 이해하면 서비스 워커 동작이 더 논리적이고 신비롭지 않게 보일 것입니다. 이 지식을 바탕으로 서비스 워커를 배포하고 업데이트할 때 더 자신감을 가질 수 있습니다.