서비스워커란?

2025. 5. 11. 23:07·PWA

서비스워커란?

정의

  • 특정 출처(사이트)의 하나 혹은 그 이상의 페이지를 제어하는 스크립트
  • 이벤트 기반 워커로서 JavaScript로 작성된 파일

기능

  • 서비스 워커는 자신이 제어하는 페이지에서 발생하는 이벤트를 수신할 수 있다.
  • 웹에서 네트워크 요청과 같은 이벤트를 가로채어 수정할 수 있고, 이를 다시 페이지로 돌려보낼 수 있다.
  • 또한, 서비스에서 사용하는 리소스를 캐싱할 수 있다.

 

 

서비스워커 사용법

1. 서비스워커 만들기

// app.js
if ("serviceWorker" in navigator) {
  navigator.serviceWorker
    .register("/sw.js")
    .then(function (registration) {
      console.log("Service Worker registered with scope:", registration.scope);
    })
    .catch(function (err) {
      console.log("Service Worker registration failed", err);
    });
}
  • 브라우저가 서비스워커를 지원한다면 → navigator.serviceWorker.register를 호출
  • navigator.serviceWorker.register: 서비스 워커 스크립트의 URL, option 객체의 2개 인자를 받는 함수

2. 웹에서 컨텐츠 가져오기

// /public/sw.js
self.addEventListener("fetch", function (event) {
  console.log("Fetch request for: ", event.request.url);
  if (event.request.url.includes("/img/logo.png")) {
    event.respondWith(fetch("/img/logo-flipped.png"));
  }
});
  • 웹사이트에서 발생하는 모든 fetch 요청을 중간에 가로채서 분석하고 조작한다.
  • 들어오는 요청 중 /img/logo-flipped.png인 요청을 가로채서 → 다른 로고(/img/logo-flipped.png)를 fetch로 response를 생성하고 → 원래 request 이벤트에 응답한다.

3. Scope로 요청의 범위 조정하기

  • 서비스 워커는 서비스 워커 파일의 위치나 navigator.serviceWorker.register에서 지정해준 Scope 옵션에 따라 요청의 범위를 조정할 수 있다.
    • 서비스워커를 root 폴더에 저장했으므로 모든 요청을 다 거친다.
// js 폴더에서 발생한 요청만 전달 
navigator.serviceWorker.register("sw.js", {scope: "/js"}) 
// js 폴더 내부를 대상으로 하는 요청만 전달 
navigator.serviceWorker.register("/js/sw.js")
  • 만약 위 코드에서 js 폴더 내에 index.html 파일을 넣는다면
    • 서비스워커는 /js/index.html 에서 발생하는 요청만 전달받는다!

4. 오프라인 요청 감지하기

self.addEventListener("fetch", function (event) {
  event.respondWith(
    fetch(event.request).catch(function () {
      return new Response("hello world !\n");
    })
  );
});
  • 모든 fetch 요청에 대해, 예외가 발생하면 catch로 받아 새로운 응답을 보내준다.
    • 오프라인이면 당연히 fetch 요청에서 에러가 발생하므로!
  • 이를 이용해서, 오프라인이면 특정 html 파일을 응답하도록 만들 수도 있다.
self.addEventListener("fetch", function (event) {
    event.respondWith(
        fetch(event.request).catch(function () {
            return fetch('/offine.html');
        })
    );
});

 

 

CacheStorage API

오프라인 상태일 때 어떻게 상태를 알고 요청을 받아서 넘길 수 있을까?

  • 온라인일 때 해당 파일도 같이 받아와서 캐시해야 한다.
  • 그럼 파일을 캐시하는 과정을 한번 알아보자.

install Event에서 파일 캐싱하기

  • 서비스 워커의 install 이벤트는 첫번째 단계 “설치 중” 에서 단 한번 발생한다.
    • 서비스 워커가 가장 처음 등록된 직후
    • 이벤트가 활성화 되기 전
  • 서비스 워커가 페이지를 제어하고 fetch 이벤트 수신을 시작하기 전에, 오프라인화 가능한 모든 파일을 캐싱할 수 있다.
self.addEventListener("install", function (event) {
    event.waitUntil(
        caches.open("c").then(function (cache) {
            return cache.add("/offline.html");
        })
    );
});
  • install 이벤트는 설치 단계에서 새로운 서비스 워커가 등록된 직후 호출된다.
  • 파일을 가져와 캐시에 저장하는 일이 비동기적으로 일어나기 때문에, 해당 이벤트가 완료될 때까지 install 이벤트를 연기해야 한다.
    • waitUntil : 전달된 프로미스가 resolve될 때까지 이벤트의 수명을 연장한다.
    • 캐시가 정상적으로 되지 않을 때 install 자체를 취소해야 할 수도 있으므로, install에서 waitUntil을 해준다.

 

CacheStorage로부터 응답 받기

self.addEventListener("fetch", function (event) {
    event.respondWith(
        fetch(event.request).catch(function () {
            return caches.match("./offline.html");
        })
    );
});
  • 오프라인 감지 시 CacheStorage에서 caches.match를 사용하여 캐시된 데이터 중 원하는 파일을 반환받는다.

 

여러개 캐싱하고 원하는 응답 받기

const CACHE_NAME = "git-cache";
const CACHE_URLS = [
    "/offline.html",
    "/img/logo-header.png",
];

/** cache.addAll을 통해 여러 파일을 객체로 묶어 한번에 캐싱 */
self.addEventListener("install", function (event){
    event.waitUntil(
        caches.open(CACHE_NAME).then(function (cache) {
            return cache.addAll(CACHE_URLS);
        })
    );
});

/** fetch 응답 로직
* request fetch 과정에서 실패해서 catch로 넘어오면 
* -> 캐시에서 해당 request가 있는지 match해서 -> 있다면 응답 리턴
*/
self.addEventListener("fetch", function (event) {
    event.respondWith(
        fetch(event.request).catch(function() {
            return caches.match(event.request).then(function(response){
                if (response) {
                    return response;
                }
                else if (event.request.headers.get("accept").includes("text/html")){
                    return caches.match("/offline.html");
                }
            });
        })
    );
});
  • addAll 함수를 사용하여 원하는 파일들을 한번에 캐싱한다.
  • match 후 캐시된 파일을 찾을 때 then으로 예외를 처리하는 이유
    • cache.match는 찾지 못하면 undefined를 반환
    • 프로미스를 reject하기 위해 then으로 처리

 

 

서비스워커의 생명주기

https://www.oreilly.com/library/view/building-progressive-web/9781491961643/ch04.html

 

생명주기 5단계

  1. 설치 중 (Installing)
    • navigator.serviceWorker.register 를 사용하여 새로운 서비스 워커를 등록할 때, js 다운로드 및 파싱 후 installing에 들어간다.
    • 설치가 무사히 완료된다면(promise가 resolve되면) installed 단계로 넘어간다.
      • 설치 중 에러가 발생하면 페이지를 새로고침해서 서비스워커를 다시 등록한다.
      • 그래도 여전히 에러가 있으면 redundant(중복) 상태에 빠진다.
    • install 이벤트 콜백 내에서 waitUntil 함수를 사용해서 installing 상태를 연장할 수 있다.
  2. 설치 됨 / 대기 중(installed/waiting)
    • 서비스 워커가 성공적으로 설치되면 → installed
      • 현재 활성화된 다른 서비스워커가 앱을 제어하고 있는게 아니라면(단독이라면) → activating
      • 다른 서비스워커가 앱을 제어하고 있다면 → waiting
  3. 활성화 중(Activating)
    • 서비스 워커가 활성화되어 앱을 제어하기 전에 해당 이벤트가 발생한다.
    • waitUntil 을 사용하여 호출을 연장할 수 있다.
  4. 활성화 중(Activating)
    • 서비스워커가 활성화되면 페이지를 제어하고, fetch 이벤트와 같은 동작 이벤트를 받을 준비가 된다.
    • 서비스워커가 활성화되기 전에 로딩이 시작된 페이지는 제어할 수 없다.
      • 페이지 로딩이 시작되기 전에만 페이지 제어 권환을 가져올 수 있다.
  5. 중복(Redunant)
    • 서비스 워커가 설치 중 실패하거나 새로운 버전으로 교체되면 Redunant(중복 상태)가 된다.
    • 이 상태에서는 앱에 아무런 영향을 미칠 수 없다.

 

서비스워커의 수명

  • 사용자가 사이트를 방문하면 → 서비스 워커가 시작되고 → 페이지에서 이벤트 처리를 완료하면 → 종료된다.
  • 다른 이벤트가 나중에 들어온다면 다시 서비스워커는 시작되고 → 종료된다.
  • 때문에 이 이벤트 리스너 코드의 실행-종료가 중요하다. 서비스워커 작업 도중이어도 이벤트 리스터 코드의 실행이 종료되면 정상적으로 작동하지 않는다.
    • 이때 waitUntil 을 사용해서 브라우저가 서비스워커 작업이 완료될 때까지 기다리게 할 수 있다.
self.addEventListener("push", function (event) { 
	event.waitUntil( fetch("/updates").then(function () { 
    		return self.registration.showNotification("New Update"); 
        }) 
    ); 
});

 

 

서비스 워커 업데이트

  • 서비스 워커 내의 코드를 바꾸고 새로고침했을 때 업데이트된 내용이 반영되지 않을 수 있다.
  • 페이지를 새로고침 하면 브라우저는 서비스 워커 스크립트에 대한 업데이트가 있는지 확인하고, 업데이트가 있다면 새로운 서비스 워커를 설치(install)한다.
    • 하지만 바로 교체되지 않고 대기중(waiting) 상태에 남는다.
  • 현재 활성화된 서비스 워커가 제어하는 페이지가 더 이상 열려 있지 않아야 ⇒ 새 서비스 워커가 활성화된다.
    • 기존 탭과 윈도우 창이 종료되거나
    • 범위를 벗어난 새로운 페이지로 이동할 때까지 유지

 

 

서비스 워커 캐시 관리

  • 캐시명에 버전 넘버를 달고, 파일이 변경될 때마다 버전 숫자를 증가시키는 방법이 있다.
var CACHE_NAME = "gih-cache-v2";
var CACHED_URLS = [
  "/index-offline.html",
  "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css",
  "/css/gih-offline.css",
  "/img/jumbo-background-sm.jpg",
  "/img/logo-header.png",
];

self.addEventListener("install", function (event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function (cache) {
      return cache.addAll(CACHED_URLS);
    })
  );
});

self.addEventListener("fetch", function (event) {
  event.respondWith(
    fetch(event.request).catch(function () {
      return caches.match(event.request).then(function (response) {
        if (response) {
          return response;
        } else if (event.request.headers.get("accept").includes("text/html")) {
          return caches.match("/index-offline.html");
        }
      });
    })
  );
});
  • 혹은 workbox 같은 pwa 라이브러리를 이용해서 캐싱을 쉽게 할 수 있다.
    • https://developer.chrome.com/docs/workbox/what-is-workbox?hl=ko
    • 이번 서비스에서도 workbox의 옵션을 이용해서 캐싱을 관리했다.

 

 

 


 

출처

https://velog.io/@hancihu/%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9B%8C%EC%BB%A4%EC%9D%98-%EC%83%9D%EB%AA%85-%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%BA%90%EC%8B%9C-%EA%B4%80%EB%A6%AC

 

 

[PWA] 서비스 워커의 생명 주기와 캐시 관리

본 포스팅은 아래 링크의 만들면서 배우는 프로그레시브 웹 앱 책을 보며 공부한 내용을 스스로 정리한 것 입니다.만들면서 배우는 프로그레시브 웹 앱이번 포스팅부터 사용할 실습 코드는 포

velog.io

 

 

'PWA' 카테고리의 다른 글

PWA 푸시알림 기능 조사  (1) 2025.05.18
PWA란?  (3) 2025.05.04
'PWA' 카테고리의 다른 글
  • PWA 푸시알림 기능 조사
  • PWA란?
옹헤옹
옹헤옹
옹헤의 개발 + 잡담 티스토리입니다!
  • 옹헤옹
    옹토리
    옹헤옹
  • 전체
    오늘
    어제
    • 분류 전체보기 (11)
      • 스터디 (2)
      • JS (1)
      • React (4)
      • PWA (3)
      • 개발 과정 (0)
      • TIL (0)
      • 잡담 (1)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    useCallback
    서비스워커
    push api
    모던자바스크립트딥다이브
    모자딥
    CORS
    네이티브앱
    PWA란
    리액트 공식문서
    pwa
    preflighted request
    리액트
    모던자바스크립트
    useState
    react hook
    react
    프론트엔드 취업
    2025 다짐
    CORS 에러
    전략패턴
    simple request
    일급객체
    부트캠프 후기
    2024 회고
    useMemo
    딥다이브
    React.memo
    서비스워커 생명주기
    푸시알림
    성능최적화
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
옹헤옹
서비스워커란?
상단으로

티스토리툴바