Service Worker Cache

박상수
8 min readOct 24, 2018

--

OFFLINE 상태에서도 홈페이지가 돌아간다면?
서비스 워커 캐시를 이용하여 가능
서비스 워커 캐시를 알아보기 전 service worker를 간단히 알아보자.

service worker란?

  • 브라우저 백그라운드에서 실행되는 worker를 말한다.

주요동작

  • 주기적인 백그라운드 동기화
  • 푸쉬 알림
  • client와 network 사이의 proxy서버 역할을 할 수 있으며 이를 이용하여offline에서도 페이지를 동작하도록 cache를 할 수 있다.(Network proxy)
  • Dom Thread와 별개의 Thread로 동작, Dom 및 Global Scope에 접근 불가
  • page단위로 동작하지 않기때문에 register 후 새탭을 열어도 동일한 worker가 처리

사전 요구사항

  • 브라우저 지원
    https://developer.mozilla.org/ko/docs/Web/API/Service_Worker_API#Browser_compatibility
  • HTTPS 필요
    - localhost에서 사용 가능
    - chrome unsafely 옵션을 이용하여 사용 가능
    chrome.exe 우클릭 -> 속성 -> 대상 “C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” — user-data-dir=/tmp/foo — ignore-certificate-errors — unsafely-treat-insecure-origin-as-secure=https://domain.co.kr 추가
  • same-origin 기반

Service Worker LifeCycle

  • register -> install -> activate-> fetch
  • register : 서비스워커를 설치 하려면 서비스워커를 등록해야 된다.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function() {
console.log('ServiceWorker registration successful');
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
  • install : 해당페이지를 처음 방문할 때 install 이벤트 발생, 페이지 자산을 캐시하는 곳
    1. cache open
    2. resource cache
    3. resource cache confirm
self.addEventListener('install', event => {
console.log('cacheName1 installing..');
event.waitUntil(
caches.open('cacheName1').then(cache => cache.addAll(['/aa.do','/common/css/bb.css','/common/js/cc.js']))
)
});
  • activate : 설치된 서비스 워커가 제어권을 갖은 상태, push 및 sync와 같은 함수가 처리할 준비가 됨.
self.addEventListener('activate', function(e) {
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(
keyList.map(function(key) {
if ("cacheName1".indexOf(key) == -1) {
return caches.delete(key);
}
})
);
})
);
});
  • fetch : 서비스워커를 설치 완료 후 캐시된 응답을 반환받음.
    - 해당 소스는 네트웍 데이터가 있을경우 cache를 update
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('cacheName1').then(function(cache) {
if (event.request.clone().method == "GET") {
return cache.match(event.request).then(function(response) {
var fetchPromise =
fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
}
})
);
});

구현

  • index.html
  if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(reg) {
console.log('ServiceWorker registration successful');
reg.update();
}).catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
  • sw.js
const expectedCaches = ['static-v1'],
resources = [
'/index.html',
'/do.html'
...
];
self.addEventListener('install', event => {
console.log('installing..');
event.waitUntil(
caches.open(expectedCaches[0]).then(cache => cache.addAll(resources))
)
});self.addEventListener('foreignfetch', event => {
event.respondWith(fetch(event.request).then(response => {
return {
response: response,
origin: event.origin,
headers: ['Content-Type']
}
}));
});
self.addEventListener('activate', function(e) {
var expectedCacheNames = expectedCaches[0];
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(
keyList.map(function(key) {
if (expectedCacheNames.indexOf(key) == -1) {
return caches.delete(key);
}
})
);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open(expectedCaches[0]).then(function(cache) {
if (event.request.clone().method == "GET") {
return cache.match(event.request).then(function(response) {
var fetchPromise = fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
}
})
);
});
  • 해당 페이지를 network 결과 데이터가 있을경우 cache를 하고 없으면 cache를 불러온다. (offline 대비)
  • 기본적인 cycle에 대한 이해를 갖고 있으면, cache에 대한 많은 전략을 펼칠 수 있을 것으로 생각된다.

참고

--

--

No responses yet