Progressive Web Apps
Get your site out of the tab!
송정기 책임
Samsung Internet 개발자
웹 표준 에디터
Chromium contributor
Email: jungkee.song@samsung.com
GitHub: @jungkees
Web App Manifest
Service Worker
Push notification
Web App Manifest
Jinho Bang (romandev@nate.com)
웹(Web)은 죽었다.
앱(App)의 시대가 올것이다.
http://stories.flipkart.com/introducing-flipkart-lite/
Progressive Web App!
Progressive Web Apps
Background Sync
ServiceWorker
Push Notification
Permissions
Bluetooth&USB
Web App Manifest
Web App Manifest?
http://www.w3.org/TR/appmanifest
<link rel = ‘manifest’ href= ‘manifest.webmanifest’>
Installable Web App
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
click
Installable Web App
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
click
Installable Web App
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
installed
Installable Web App - Demo
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
Splash Screen
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
background_color
icons name
* Chrome implemented it using name/icon/background_color
Splash Screen - Demo
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
Task Manager
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
Display Mode (standalone)
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
status
bar
#3F51B5
Display Mode (fullscreen)
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "fullscreen",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
no status bar
Start URL
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "portrait",

"start_url": "/simple-demo/?home=true"

}
../simple_demo?home=true
../simple_demo
Orientation (landscape)
{

"name": "Simple web app demo",

"short_name": "Demo",

"icons": [

{

"src": "icon-medium.png",

"sizes": "96x96"

}, {

"src": "icon-large.png",

"sizes": "192x192"

}

],

"theme_color": "#3F51B5",

"background_color": "#F5F5F5",

"display": "standalone",

"orientation": "landscape",

"start_url": "/simple-demo/?home=true"

}
Manifest로 Web App을
first-class citizen으로 만들자!
Service Worker
Offline-first & Background processing
오프라인 우선
백그라운드 이벤트 처리
어디가 오프라인? 비행기 안에서만?
가능한 한 언제나!
브라우저에 진짜 없던 것
브라우저 꺼도 web push 옵니다!
그러면서 웹 그대로!
웹 서버 업데이트가 곧 앱 업데이트! URL! 웹의 장점은 바로 거기에
Lie-Fi 시! 데이터 사용할 때도!
push, sync, geofenceenter, ..
표준 현황
개발 현황
2015년 6월 네 번째 WD 그리고..
2016년 Service Worker 1 CR 계획
4.0 40+ 44 32+
Edge? Safari?
동작의 기초가 되는 개념 들
Same origin 에서 동작
Registration 기반으로 동작 ­ key는 URL scope!
Navigation 시 매칭되는 SW가 있으면 document가 control 됨
버전 관리를 위해 waiting worker, active worker 존재
fetch 이벤트
Navigation/Resource request
onfetch
Page
SW
Cache
캐시 match 시도
Match된 Response
클라이언트에 respond
Page Page
이벤트 기반 워커
Navigation/Resource request
Page
Network fetch
Response
온라인 이신가요?
충분히 온라인 이신가요?
Navigation/Resource request
Page
Network fetch
5XX
DNS 실패
Lie-Fi
너무 심한 RTT
오프라인 with Service Worker!
fetch 이벤트
Navigation/Resource request
onfetch
Page
SW
Cache 캐시 match 시도
Match된 Response
클라이언트에 respond
IDB
new
Response({
status: 200,
body: { … }
})
오프라인 우선!
fetch 이벤트
Navigation/Resource request
onfetch
Page
SW
Cache
Fetch request
캐시 match 시도
Match 실패
클라이언트에 respond
인스톨하기
var navigator.serviceWorker;
sw.register(scriptURL, { scope: scopeURL })
리소스 Pre-caching 하기
oninstall = e => { /* 여기서 캐시 */ };
fetch event 처리 하기
onfetch = e => { /* e.request에 대해 개발자 맘대로! */ };
Cache 관리
onactivate = e => { /* 안 쓰는 cache delete도 개발자의 몫 */ };
업데이트 w/ 24시간 룰
매 navigation 시 registration.update() functional event
“/assets/v1” /assets/v1/serviceworker.js
[ Registration map ]
Scope Script URL
Register
Page에서
// scope defaults to ‘/‘
var sw = navigator.serviceWorker;
sw.register(‘/assets/v1/serviceworker.js’)
.then(reg => {
console.log(‘success!’);
reg.installing.postMessage(‘Howdy from your installing page.’);
})
.catch(e => {
console.error(‘Installing the worker failed!:’, e);
});
“/bar” /bar/sw1.js
Var sw = navigator.serviceWorker;
① sw.register(‘/bar/sw1.js’, { scope: ‘/bar’ });
② sw.register(‘/foo/sw.js’, { scope: ‘/foo’ });
③ sw.register(‘/bar/sw2.js’, { scope: ‘/bar’ } );
[ Registration map ]
Scope Script URL
“/foo” /foo/sw.js
Register ­ 복수개의 registration 생성 가능
Page에서
“/bar” /bar/sw2.js ③ Replace sw1
①
②
onactivate
SW
oninstall
onfetch
Register가 trigger하는 인스톨 과정
브라우저 internal
oninstall = e => {
// pre-cache, etc.
};
onactivate = e => {
// upgrade Cache, etc.
};
onfetch = e => {
// Not yet ready
};
Fetched sw script
Fetch sw script
Update①
Install②
Activate③
onfetch
onfetch = e => {
// Not yet ready
};
Pre-cache in oninstall
// sw.js – 인스톨 과정에서 pre-caching
self.addEventListener(‘install’, function(e) {
// 명시된 모든 리소스를 성공적으로 받아 캐시 해야만 인스톨 성공
e.waitUntil(
self.caches.open(‘shellResources’).then(function(cache) {
return cache.addAll([
‘/app.html’,
‘/assets/v1/base.css’,
‘/assets/v1/app.js’,
‘/assets/v1/logo.png’,
‘/assets/v1/intro_video.webm’
]);
});
});
Delete unused caches in inactivate
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});
https://jakearchibald.com/2014/offline-cookbook/#on-activate
Do proxy in onfetch
self.addEventListener(‘fetch’, function(e) {
// 성공적으로 인스톨 되기 전까지는 fetch 이벤트 발생 안 함
// 모든 Cache 함수는 aync 하게 동작 함. 따라서 인터페이스가 promise로 디자인 됨
e.respondWith(
self.caches.match(e.request).catch(function() {
return fetch(e.request);
}).catch(function() {
return self.caches.match(‘/fallback.html’);
})
);
});
onfetch
sw.js
Cache
캐시 match 시도
Match된 response
클라이언트에 respond
“/” /sw.js
[ Registration map ]
Scope Script URL
“/foo” /foo/sw.js
Page Navigate https://example.com/index.html
fetch 이벤트
Scope matching
Run SW
클라이언트 Match (navigation)
onfetch
sw.js
Cache 캐시 match 시도
Match된 response
클라이언트에 respond
“/” /sw.js
[ Registration map ]
Scope Script URL
“/img” /img/sw.js
Page
Fetch “https://example.com/img/flower.png
fetch 이벤트
Control
Run SW
Subresource request
Emojoy
Offline
Wikipedia
https://wiki-offline.jakearchibald.com/
was shown
Web Push

Notification
Sanghyun Park
- sh919.park@samsung.com
(dynastpsh@gmail.com)
PUSH NOTIFICATION?
PUSH NOTIFICATION 효과
72% 26%
Push Notification 에 의한
사용자 방문 시간 증가율
Push Notification 에 의해
방문한 사용자의 지출 증가율
※ Chrome dev summit 2015 Keynote
WEB PUSH!
Background Service
WEB PUSH NOTIFICATION 필수 요소
Notification UI
Push 등록/해지/이벤트
Service Worker
WEB PUSH NOTIFICATION 구성
Web Notification API
Push API
WEB PUSH
= 구독 + 받기
4.0 Chrome 42+
지원 브라우저..
Opera44
어떻게!
Service Worker
Client side proxy
Written in Javascript
Web Server
Push Service
SUBSCRIPTION
①
③
④
②
WHAT!
SUBSCRIPTION
navigator.serviceWorker.ready.then(function(registration) {

registration.pushManager.subscribe({userVisibleOnly: true})
.then(function(subscription) {

// The push subscription details needed by the
// application. server are now available, and can be
// sent to it using, for example, an XMLHttpRequest.
sendSubscriptionToServer(subscription);

}), function(error) {
// Subscription failed.

}

);

});
WHAT!
SUBSCRIPTION
navigator.serviceWorker.ready.then(function(registration) {

registration.pushManager.subscribe({userVisibleOnly: true})
.then(function(subscription) {

// The push subscription details needed by the application

// server are now available, and can be sent to it using,

// for example, an XMLHttpRequest.
sendSubscriptionToServer(subscription);

}), function(error) {
// During development it often helps to log errors to the
// console.

}

);

});
{
"name": "Simple Push Demo",
"short_name": "Push Demo",
"start_url": "/index.html?homescreen=1",
"display": "standalone",
"gcm_sender_id": "653317226796"
}
Define GCM Sender ID in Manifest.json
Service Worker
Background Service
Web Server
Push Service
①
②
③
④
RECEIVING A PUSH MESSAGE
self.addEventListener('push', function(event) {
event.waitUntil(
fetch('/notification.json').then(function(response) {
return response.json();
}).then(function(data) {
return self.registration.showNotification(data.title, {
body: data.body,
icon: data.iconUrl,
tag: data.tag,
data: { url : data.url }
});
});
);
});
REFOCUS EXISTING WINDOWS
self.addEventListener('notificationclick', function(event) {
clients.matchAll({
type: 'window'
})
.then(function(clients) {
if (clients.length > 0) {
clients[0].postMessage({ navigateTo: event.notification.data.url });
return clients[0].focus();
} else {
return clients.openWindow(event.notification.data.url);
}
})
});
Demo
발전방향..
Service Worker
Client side proxy
Written in Javascript
Web Server
Push Service
PAYLOAD
DATA
DATA
DATA
Q & A
참고자료
개념 이해
•Introduction to Service Worker
•Service Worker - first draft published
표준 리소스
•Nightly
API 사용 패턴
•The offline cookbook
•플랫폼 구현자 참고 문서
•GitHub
•Platinum Service Worker Elements
•Service Worker API MDN

Progressive Web Apps

  • 1.
    Progressive Web Apps Getyour site out of the tab!
  • 2.
    송정기 책임 Samsung Internet개발자 웹 표준 에디터 Chromium contributor Email: jungkee.song@samsung.com GitHub: @jungkees
  • 3.
    Web App Manifest ServiceWorker Push notification
  • 4.
    Web App Manifest JinhoBang (romandev@nate.com)
  • 5.
  • 8.
  • 9.
    Progressive Web Apps BackgroundSync ServiceWorker Push Notification Permissions Bluetooth&USB Web App Manifest
  • 10.
  • 11.
    <link rel =‘manifest’ href= ‘manifest.webmanifest’>
  • 12.
    Installable Web App {
 "name":"Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } click
  • 13.
    Installable Web App {
 "name":"Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } click
  • 14.
    Installable Web App {
 "name":"Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } installed
  • 15.
    Installable Web App- Demo {
 "name": "Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 }
  • 16.
    Splash Screen {
 "name": "Simpleweb app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } background_color icons name * Chrome implemented it using name/icon/background_color
  • 17.
    Splash Screen -Demo {
 "name": "Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 }
  • 18.
    Task Manager {
 "name": "Simpleweb app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 }
  • 19.
    Display Mode (standalone) {
 "name":"Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } status bar #3F51B5
  • 20.
    Display Mode (fullscreen) {
 "name":"Simple web app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "fullscreen",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } no status bar
  • 21.
    Start URL {
 "name": "Simpleweb app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "portrait",
 "start_url": "/simple-demo/?home=true"
 } ../simple_demo?home=true ../simple_demo
  • 22.
    Orientation (landscape) {
 "name": "Simpleweb app demo",
 "short_name": "Demo",
 "icons": [
 {
 "src": "icon-medium.png",
 "sizes": "96x96"
 }, {
 "src": "icon-large.png",
 "sizes": "192x192"
 }
 ],
 "theme_color": "#3F51B5",
 "background_color": "#F5F5F5",
 "display": "standalone",
 "orientation": "landscape",
 "start_url": "/simple-demo/?home=true"
 }
  • 23.
    Manifest로 Web App을 first-classcitizen으로 만들자!
  • 24.
    Service Worker Offline-first &Background processing
  • 25.
    오프라인 우선 백그라운드 이벤트처리 어디가 오프라인? 비행기 안에서만? 가능한 한 언제나! 브라우저에 진짜 없던 것 브라우저 꺼도 web push 옵니다! 그러면서 웹 그대로! 웹 서버 업데이트가 곧 앱 업데이트! URL! 웹의 장점은 바로 거기에 Lie-Fi 시! 데이터 사용할 때도! push, sync, geofenceenter, ..
  • 26.
    표준 현황 개발 현황 2015년6월 네 번째 WD 그리고.. 2016년 Service Worker 1 CR 계획 4.0 40+ 44 32+ Edge? Safari?
  • 27.
    동작의 기초가 되는개념 들 Same origin 에서 동작 Registration 기반으로 동작 ­ key는 URL scope! Navigation 시 매칭되는 SW가 있으면 document가 control 됨 버전 관리를 위해 waiting worker, active worker 존재
  • 28.
    fetch 이벤트 Navigation/Resource request onfetch Page SW Cache 캐시match 시도 Match된 Response 클라이언트에 respond Page Page 이벤트 기반 워커
  • 29.
  • 30.
    충분히 온라인 이신가요? Navigation/Resourcerequest Page Network fetch 5XX DNS 실패 Lie-Fi 너무 심한 RTT
  • 31.
    오프라인 with ServiceWorker! fetch 이벤트 Navigation/Resource request onfetch Page SW Cache 캐시 match 시도 Match된 Response 클라이언트에 respond IDB new Response({ status: 200, body: { … } })
  • 32.
    오프라인 우선! fetch 이벤트 Navigation/Resourcerequest onfetch Page SW Cache Fetch request 캐시 match 시도 Match 실패 클라이언트에 respond
  • 33.
    인스톨하기 var navigator.serviceWorker; sw.register(scriptURL, {scope: scopeURL }) 리소스 Pre-caching 하기 oninstall = e => { /* 여기서 캐시 */ }; fetch event 처리 하기 onfetch = e => { /* e.request에 대해 개발자 맘대로! */ }; Cache 관리 onactivate = e => { /* 안 쓰는 cache delete도 개발자의 몫 */ }; 업데이트 w/ 24시간 룰 매 navigation 시 registration.update() functional event
  • 34.
    “/assets/v1” /assets/v1/serviceworker.js [ Registrationmap ] Scope Script URL Register Page에서 // scope defaults to ‘/‘ var sw = navigator.serviceWorker; sw.register(‘/assets/v1/serviceworker.js’) .then(reg => { console.log(‘success!’); reg.installing.postMessage(‘Howdy from your installing page.’); }) .catch(e => { console.error(‘Installing the worker failed!:’, e); });
  • 35.
    “/bar” /bar/sw1.js Var sw= navigator.serviceWorker; ① sw.register(‘/bar/sw1.js’, { scope: ‘/bar’ }); ② sw.register(‘/foo/sw.js’, { scope: ‘/foo’ }); ③ sw.register(‘/bar/sw2.js’, { scope: ‘/bar’ } ); [ Registration map ] Scope Script URL “/foo” /foo/sw.js Register ­ 복수개의 registration 생성 가능 Page에서 “/bar” /bar/sw2.js ③ Replace sw1 ① ②
  • 36.
    onactivate SW oninstall onfetch Register가 trigger하는 인스톨과정 브라우저 internal oninstall = e => { // pre-cache, etc. }; onactivate = e => { // upgrade Cache, etc. }; onfetch = e => { // Not yet ready }; Fetched sw script Fetch sw script Update① Install② Activate③ onfetch onfetch = e => { // Not yet ready };
  • 37.
    Pre-cache in oninstall //sw.js – 인스톨 과정에서 pre-caching self.addEventListener(‘install’, function(e) { // 명시된 모든 리소스를 성공적으로 받아 캐시 해야만 인스톨 성공 e.waitUntil( self.caches.open(‘shellResources’).then(function(cache) { return cache.addAll([ ‘/app.html’, ‘/assets/v1/base.css’, ‘/assets/v1/app.js’, ‘/assets/v1/logo.png’, ‘/assets/v1/intro_video.webm’ ]); }); });
  • 38.
    Delete unused cachesin inactivate self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.filter(function(cacheName) { // Return true if you want to remove this cache, // but remember that caches are shared across // the whole origin }).map(function(cacheName) { return caches.delete(cacheName); }) ); }) ); }); https://jakearchibald.com/2014/offline-cookbook/#on-activate
  • 39.
    Do proxy inonfetch self.addEventListener(‘fetch’, function(e) { // 성공적으로 인스톨 되기 전까지는 fetch 이벤트 발생 안 함 // 모든 Cache 함수는 aync 하게 동작 함. 따라서 인터페이스가 promise로 디자인 됨 e.respondWith( self.caches.match(e.request).catch(function() { return fetch(e.request); }).catch(function() { return self.caches.match(‘/fallback.html’); }) ); });
  • 40.
    onfetch sw.js Cache 캐시 match 시도 Match된response 클라이언트에 respond “/” /sw.js [ Registration map ] Scope Script URL “/foo” /foo/sw.js Page Navigate https://example.com/index.html fetch 이벤트 Scope matching Run SW 클라이언트 Match (navigation)
  • 41.
    onfetch sw.js Cache 캐시 match시도 Match된 response 클라이언트에 respond “/” /sw.js [ Registration map ] Scope Script URL “/img” /img/sw.js Page Fetch “https://example.com/img/flower.png fetch 이벤트 Control Run SW Subresource request
  • 42.
  • 43.
  • 44.
    Web Push
 Notification Sanghyun Park -sh919.park@samsung.com (dynastpsh@gmail.com)
  • 45.
  • 47.
    PUSH NOTIFICATION 효과 72%26% Push Notification 에 의한 사용자 방문 시간 증가율 Push Notification 에 의해 방문한 사용자의 지출 증가율 ※ Chrome dev summit 2015 Keynote
  • 48.
  • 50.
    Background Service WEB PUSHNOTIFICATION 필수 요소 Notification UI Push 등록/해지/이벤트
  • 51.
    Service Worker WEB PUSHNOTIFICATION 구성 Web Notification API Push API
  • 52.
  • 53.
    4.0 Chrome 42+ 지원브라우저.. Opera44
  • 54.
  • 55.
    Service Worker Client sideproxy Written in Javascript Web Server Push Service SUBSCRIPTION ① ③ ④ ②
  • 56.
    WHAT! SUBSCRIPTION navigator.serviceWorker.ready.then(function(registration) {
 registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription){
 // The push subscription details needed by the // application. server are now available, and can be // sent to it using, for example, an XMLHttpRequest. sendSubscriptionToServer(subscription);
 }), function(error) { // Subscription failed.
 }
 );
 });
  • 57.
    WHAT! SUBSCRIPTION navigator.serviceWorker.ready.then(function(registration) {
 registration.pushManager.subscribe({userVisibleOnly: true}) .then(function(subscription){
 // The push subscription details needed by the application
 // server are now available, and can be sent to it using,
 // for example, an XMLHttpRequest. sendSubscriptionToServer(subscription);
 }), function(error) { // During development it often helps to log errors to the // console.
 }
 );
 }); { "name": "Simple Push Demo", "short_name": "Push Demo", "start_url": "/index.html?homescreen=1", "display": "standalone", "gcm_sender_id": "653317226796" } Define GCM Sender ID in Manifest.json
  • 58.
    Service Worker Background Service WebServer Push Service ① ② ③ ④
  • 59.
    RECEIVING A PUSHMESSAGE self.addEventListener('push', function(event) { event.waitUntil( fetch('/notification.json').then(function(response) { return response.json(); }).then(function(data) { return self.registration.showNotification(data.title, { body: data.body, icon: data.iconUrl, tag: data.tag, data: { url : data.url } }); }); ); });
  • 60.
    REFOCUS EXISTING WINDOWS self.addEventListener('notificationclick',function(event) { clients.matchAll({ type: 'window' }) .then(function(clients) { if (clients.length > 0) { clients[0].postMessage({ navigateTo: event.notification.data.url }); return clients[0].focus(); } else { return clients.openWindow(event.notification.data.url); } }) });
  • 61.
  • 63.
  • 64.
    Service Worker Client sideproxy Written in Javascript Web Server Push Service PAYLOAD DATA DATA DATA
  • 65.
  • 66.
    참고자료 개념 이해 •Introduction toService Worker •Service Worker - first draft published 표준 리소스 •Nightly API 사용 패턴 •The offline cookbook •플랫폼 구현자 참고 문서 •GitHub •Platinum Service Worker Elements •Service Worker API MDN