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에 대한 많은 전략을 펼칠 수 있을 것으로 생각된다.