이 Codelab은 Google Developers 교육팀에서 개발한 프로그레시브 웹 앱 개발 교육 과정의 일부입니다. Codelab을 순서대로 진행하면 이 과정의 학습 효과를 극대화할 수 있습니다.
과정에 관한 자세한 내용은 프로그레시브 웹 앱 개발 개요를 참고하세요.
소개
이 실습에서는 간단한 서비스 워커를 만드는 방법을 안내하고 서비스 워커 수명 주기를 설명합니다.
학습할 내용
- 기본 서비스 워커 스크립트를 만들고 설치하고 간단한 디버깅을 실행합니다.
유의해야 할 사항
- 기본 JavaScript 및 HTML
- ES2015 Promise의 개념 및 기본 문법
- 개발자 콘솔을 사용 설정하는 방법
시작하기 전에 필요한 사항
- 터미널/셸 액세스가 가능한 컴퓨터
- 인터넷 연결
- 서비스 워커를 지원하는 브라우저
- 텍스트 편집기
GitHub에서 pwa-training-labs 저장소를 다운로드하거나 클론하고 필요한 경우 LTS 버전의 Node.js를 설치합니다.
service-worker-lab/app/ 디렉터리로 이동하여 로컬 개발 서버를 시작합니다.
cd service-worker-lab/app npm install node server.js
Ctrl-c을 사용하여 언제든지 서버를 종료할 수 있습니다.
브라우저를 열고 localhost:8081/으로 이동합니다.
참고: 서비스 워커가 실습을 방해하지 않도록 서비스 워커를 등록 취소하고 localhost의 모든 서비스 워커 캐시를 지우세요. Chrome DevTools에서는 Application(애플리케이션) 탭의 Clear storage(저장소 지우기) 섹션에서 Clear site data(사이트 데이터 지우기)를 클릭하여 이를 달성할 수 있습니다.
선호하는 텍스트 편집기에서 service-worker-lab/app/ 폴더를 엽니다. app/ 폴더에서 실습을 빌드합니다.
이 폴더에는 다음이 포함됩니다.
below/another.html,js/another.js,js/other.js,other.html는 서비스 워커 범위 실험에 사용되는 샘플 리소스입니다.styles/폴더에는 이 실습의 캐스케이딩 스타일시트가 포함되어 있습니다.test/폴더에는 진행 상황을 테스트하기 위한 파일이 포함되어 있습니다.index.html은 샘플 사이트/애플리케이션의 기본 HTML 페이지입니다.service-worker.js은 서비스 워커를 만드는 데 사용되는 JavaScript 파일입니다.package.json및package-lock.json은 이 프로젝트에서 사용된 노드 패키지를 추적합니다.server.js는 앱을 호스팅하는 데 사용하는 간단한 Express 서버입니다.
텍스트 편집기에서 service-worker.js을 엽니다. 파일이 비어 있습니다. 아직 서비스 워커 내에서 실행할 코드를 추가하지 않았습니다.
텍스트 편집기에서 index.html을 엽니다.
<script> 태그 내에 다음 코드를 추가하여 서비스 워커를 등록합니다.
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service-worker.js')
.then(registration => {
console.log('Service Worker is registered', registration);
})
.catch(err => {
console.error('Registration failed:', err);
});
});
}스크립트를 저장하고 페이지를 새로고침합니다. 콘솔에 서비스 워커가 등록되었음을 나타내는 메시지가 반환됩니다. Chrome에서 개발자 도구 (Windows 및 Linux의 경우 Ctrl + Shift + I, Mac의 경우 ⌘ + alt + I)를 열고 애플리케이션 탭을 클릭한 다음 서비스 워커 옵션을 클릭하여 서비스 워커가 등록되었는지 확인할 수 있습니다. 다음과 비슷한 내용이 표시됩니다.

선택사항: 지원되지 않는 브라우저에서 사이트를 열고 지원 확인 조건이 작동하는지 확인합니다.
설명
위 코드는 service-worker.js 파일을 서비스 워커로 등록합니다. 먼저 브라우저가 서비스 워커를 지원하는지 확인합니다. 일부 브라우저에서는 서비스 워커를 지원하지 않을 수 있으므로 서비스 워커를 등록할 때마다 이 작업을 실행해야 합니다. 그런 다음 코드는 창의 Navigator 인터페이스에 포함된 ServiceWorkerContainer API의 register 메서드를 사용하여 서비스 워커를 등록합니다.
navigator.serviceWorker.register(...)는 서비스 워커가 성공적으로 등록되면 registration 객체로 확인되는 프로미스를 반환합니다. 등록에 실패하면 프로미스가 거부됩니다.
서비스 워커의 상태가 변경되면 서비스 워커에서 이벤트가 트리거됩니다.
이벤트 리스너 추가
텍스트 편집기에서 service-worker.js을 엽니다.
서비스 워커에 다음 이벤트 리스너를 추가합니다.
self.addEventListener('install', event => {
console.log('Service worker installing...');
// Add a call to skipWaiting here
});
self.addEventListener('activate', event => {
console.log('Service worker activating...');
});파일을 저장합니다.
서비스 워커를 수동으로 등록 취소하고 페이지를 새로고침하여 업데이트된 서비스 워커를 설치하고 활성화합니다. 콘솔 로그에 새 서비스 워커가 등록, 설치, 활성화되었음을 나타내야 합니다.
참고: 등록 로그가 다른 로그 (설치 및 활성화)와 순서가 다르게 표시될 수 있습니다. 서비스 워커는 페이지와 동시에 실행되므로 로그 순서를 보장할 수 없습니다 (등록 로그는 페이지에서 가져오고 설치 및 활성화 로그는 서비스 워커에서 가져옴). 설치, 활성화, 기타 서비스 워커 이벤트는 서비스 워커 내에서 정의된 순서로 발생하지만 항상 예상되는 순서로 표시되어야 합니다.
설명
서비스 워커는 등록이 끝나면 install 이벤트를 내보냅니다. 위 코드에서는 install 이벤트 리스너 내에 메시지가 로깅되지만 실제 앱에서는 정적 애셋을 캐싱하는 것이 좋습니다.
서비스 워커가 등록되면 브라우저는 서비스 워커가 새 것인지 감지합니다 (이전에 설치된 서비스 워커와 다르거나 이 사이트에 등록된 서비스 워커가 없기 때문). 서비스 워커가 새로운 경우 (이 경우와 같이) 브라우저가 이를 설치합니다.
서비스 워커가 페이지를 제어하면 activate 이벤트가 발생합니다. 위의 코드는 여기에 메시지를 로깅하지만 이 이벤트는 캐시를 업데이트하는 데 자주 사용됩니다.
지정된 범위에서는 한 번에 하나의 서비스 워커만 활성화될 수 있으므로 (서비스 워커 범위 살펴보기 참고) 새로 설치된 서비스 워커는 기존 서비스 워커가 더 이상 사용되지 않을 때까지 활성화되지 않습니다. 이러한 이유로 새 서비스 워커가 인계받기 전에 서비스 워커로 제어되는 모든 페이지를 닫아야 합니다. 기존 서비스 워커가 등록 취소되었으므로 새 서비스 워커가 즉시 활성화되었습니다.
참고: 페이지를 새로고침하는 것만으로는 제어를 새 서비스 워커로 이전할 수 없습니다. 새 페이지는 현재 페이지가 언로드되기 전에 요청되며 이전 서비스 워커가 사용되지 않는 시간이 없기 때문입니다.
참고: 일부 브라우저의 개발자 도구를 사용하여 새 서비스 워커를 수동으로 활성화할 수도 있고 3.4절에서 설명하는 skipWaiting()를 사용하여 프로그래매틱 방식으로 활성화할 수도 있습니다.
서비스 워커 업데이트
service-worker.js의 아무 곳에나 다음 주석을 추가합니다.
// I'm a new service worker파일을 저장하고 페이지를 새로고침합니다. 콘솔의 로그를 확인합니다. 새 서비스 워커가 설치되지만 활성화되지는 않습니다. Chrome에서는 DevTools의 Application 탭에서 대기 중인 서비스 워커를 확인할 수 있습니다.

서비스 워커와 연결된 모든 페이지를 닫습니다. 그런 다음 localhost:8081/을 다시 엽니다. 콘솔 로그에 새 서비스 워커가 활성화되었음이 표시됩니다.
참고: 예기치 않은 결과가 표시되면 개발자 도구에서 HTTP 캐시가 사용 중지되어 있는지 확인하세요.
설명
브라우저에서 추가된 주석으로 인해 새 서비스 워커 파일과 기존 서비스 워커 파일 간의 바이트 차이를 감지하므로 새 서비스 워커가 설치됩니다. 한 번에 하나의 서비스 워커만 활성화될 수 있으므로 (특정 범위의 경우) 새 서비스 워커가 설치되더라도 기존 서비스 워커가 더 이상 사용되지 않을 때까지 활성화되지 않습니다. 이전 서비스 워커의 제어 하에 있는 모든 페이지를 닫으면 새 서비스 워커를 활성화할 수 있습니다.
대기 단계 건너뛰기
대기 단계를 건너뛰면 기존 서비스 워커가 있더라도 새 서비스 워커가 즉시 활성화될 수 있습니다.
service-worker.js에서 install 이벤트 리스너에 skipWaiting 호출을 추가합니다.
self.skipWaiting();파일을 저장하고 페이지를 새로고침합니다. 이전 서비스 워커가 제어하고 있었음에도 새 서비스 워커가 즉시 설치되고 활성화됩니다.
설명
skipWaiting() 메서드를 사용하면 서비스 워커가 설치를 완료하는 즉시 활성화될 수 있습니다. 설치 이벤트 리스너는 skipWaiting() 호출을 배치하는 일반적인 위치이지만 대기 단계 중이나 대기 단계 전에 어디서든 호출할 수 있습니다. skipWaiting() 사용 시기와 방법에 관한 자세한 내용은 이 문서를 참고하세요. 이제 실습의 나머지 부분에서는 서비스 워커를 수동으로 등록 해제하지 않고도 새 서비스 워커 코드를 테스트할 수 있습니다.
추가 정보
서비스 워커는 웹 앱과 네트워크 간의 프록시 역할을 할 수 있습니다.
도메인에서 요청을 가로채는 가져오기 리스너를 추가해 보겠습니다.
다음 코드를 service-worker.js에 추가합니다.
self.addEventListener('fetch', event => {
console.log('Fetching:', event.request.url);
});스크립트를 저장하고 페이지를 새로고침하여 업데이트된 서비스 워커를 설치하고 활성화합니다.
콘솔을 확인하고 가져오기 이벤트가 로깅되지 않았는지 확인합니다. 페이지를 새로고침하고 콘솔을 다시 확인합니다. 이번에는 페이지와 페이지의 애셋 (예: CSS)에 대한 가져오기 이벤트가 표시됩니다.
기타 페이지, 다른 페이지, 뒤로 링크를 클릭합니다.
각 페이지와 해당 페이지의 애셋에 대한 가져오기 이벤트가 콘솔에 표시됩니다. 모든 로그가 이해되나요?
참고: 페이지를 방문할 때 HTTP 캐시가 사용 중지되어 있지 않으면 CSS 및 JavaScript 애셋이 로컬에 캐시될 수 있습니다. 이 경우 이러한 리소스의 가져오기 이벤트가 표시되지 않습니다.
설명
서비스 워커는 범위 내에 있는 브라우저가 수행한 모든 HTTP 요청에 대한 가져오기 이벤트를 수신합니다. fetch 이벤트 객체에는 요청이 포함됩니다. 서비스 워커에서 가져오기 이벤트를 수신 대기하는 것은 DOM에서 클릭 이벤트를 수신 대기하는 것과 비슷합니다. 코드에서 가져오기 이벤트가 발생하면 요청된 URL을 콘솔에 로깅합니다. 실제로 임의의 리소스를 사용하여 자체 맞춤 응답을 만들고 반환할 수도 있습니다.
첫 번째 새로고침에서 가져오기 이벤트가 로깅되지 않은 이유는 무엇인가요? 기본적으로 페이지의 가져오기 이벤트는 페이지 요청 자체가 서비스 워커를 통과하지 않는 한 서비스 워커를 통과하지 않습니다. 이렇게 하면 사이트의 일관성이 유지됩니다. 서비스 워커 없이 페이지가 로드되면 하위 리소스도 로드됩니다.
추가 정보
솔루션 코드
작동하는 코드의 사본을 가져오려면 04-intercepting-network-requests/ 폴더로 이동하세요.
서비스 워커에는 범위가 있습니다. 서비스 워커의 범위에 따라 서비스 워커가 요청을 가로채는 경로가 결정됩니다.
범위 찾기
index.html의 등록 코드를 다음과 같이 업데이트합니다.
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service-worker.js')
.then(registration => {
console.log('SW registered with scope:', registration.scope);
})
.catch(err => {
console.error('Registration failed:', err);
});
});
}브라우저를 새로고침합니다. 콘솔에 서비스 워커의 범위가 표시됩니다 (이 경우 http://localhost:8081/).
설명
register()에서 반환된 프로미스는 서비스 워커의 범위를 포함하는 등록 객체로 확인됩니다.
기본 범위는 서비스 워커 파일의 경로이며 모든 하위 디렉터리로 확장됩니다. 따라서 앱의 루트 디렉터리에 있는 서비스 워커는 앱의 모든 파일에서 발생하는 요청을 제어합니다.
서비스 워커 이동
service-worker.js를 below/ 디렉터리로 이동하고 index.html의 등록 코드에서 서비스 워커 URL을 업데이트합니다.
브라우저에서 현재 서비스 워커를 등록 취소하고 페이지를 새로고침합니다.
콘솔에 서비스 워커의 범위가 이제 http://localhost:8081/below/로 표시됩니다. Chrome에서는 DevTools의 애플리케이션 탭에서 서비스 워커 범위를 확인할 수도 있습니다.

기본 페이지로 돌아가서 기타 페이지, 다른 페이지, 뒤로를 클릭합니다. 어떤 가져오기 요청이 로깅되나요? 어떤 항목이 아닌가요?
설명
서비스 워커의 기본 범위는 서비스 워커 파일의 경로입니다. 이제 서비스 워커 파일이 below/에 있으므로 범위는 below/입니다. 이제 콘솔은 서비스 워커의 범위 내에 있는 리소스인 another.html, another.css, another.js의 가져오기 이벤트만 로깅합니다.
임의 범위 설정
서비스 워커를 프로젝트 루트 디렉터리 (app/)로 다시 이동하고 index.html의 등록 코드에서 서비스 워커 URL을 업데이트합니다.
MDN의 참조를 사용하여 register()의 선택적 매개변수를 통해 서비스 워커의 범위를 below/ 디렉터리로 설정합니다.
서비스 워커를 등록 취소하고 페이지를 새로고침합니다. 기타 페이지, 다른 페이지, 뒤로를 클릭합니다.
콘솔에 서비스 워커의 범위가 이제 http://localhost:8081/below/로 표시되고 another.html, another.css, another.js에 대해서만 가져오기 이벤트가 로깅됩니다.
설명
등록 시 추가 매개변수를 전달하여 임의의 범위를 설정할 수 있습니다. 예를 들면 다음과 같습니다.
navigator.serviceWorker.register('/service-worker.js', {
scope: '/kitten/'
});위 예에서 서비스 워커의 범위는 /kitten/로 설정됩니다. 서비스 워커는 /kitten/ 및 /kitten/lower/의 페이지에서 요청을 가로채지만 /kitten 또는 /와 같은 페이지에서는 요청을 가로채지 않습니다.
참고: 서비스 워커의 실제 위치보다 높은 임의의 범위를 설정할 수 없습니다. 하지만 Service-Worker-Allowed 헤더로 제공되는 클라이언트에서 서버 작업자가 활성 상태인 경우 서비스 작업자의 위치 위에 해당 서비스 작업자의 최대 범위를 지정할 수 있습니다.
추가 정보
솔루션 코드
작동하는 코드의 사본을 가져오려면 solution/ 폴더로 이동하세요.
이제 간단한 서비스 워커가 실행되고 서비스 워커 수명 주기를 이해합니다.
추가 정보
PWA 교육 과정의 모든 Codelab을 확인하려면 과정의 환영 Codelab을 참고하세요.