먼저 제목이 너무 우스꽝스러워 죄송합니다.
Chrome 44에서는 Notfication.data 및 ServiceWorkerRegistration.getNotifications()가 추가되어 푸시 메시지로 알림을 처리할 때의 몇 가지 일반적인 사용 사례를 열어주고 단순화합니다.
알림 데이터
Notification.data를 사용하면 JavaScript 객체를 Notification과 연결할 수 있습니다.
기본적으로 푸시 메시지를 수신하면 일부 데이터가 포함된 알림을 만들 수 있으며, notificationclick 이벤트에서 클릭된 알림을 가져와 데이터를 가져올 수 있습니다.
예를 들어 데이터 객체를 만들고 다음과 같이 알림 옵션에 추가합니다.
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
var title = 'Yay a message.';
var body = 'We have received a push message.';
var icon = '/images/icon-192x192.png';
var tag = 'simple-push-demo-notification-tag';
var data = {
doge: {
wow: 'such amaze notification data'
}
};
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: data
})
);
});
즉, notificationclick 이벤트에서 정보를 가져올 수 있습니다.
self.addEventListener('notificationclick', function(event) {
var doge = event.notification.data.doge;
console.log(doge.wow);
});
이전에는 IndexDB에 데이터를 저장하거나 아이콘 URL 끝에 뭔가를 넣어야 했습니다.
ServiceWorkerRegistration.getNotifications()
푸시 알림을 사용하는 개발자가 자주 요청하는 기능 중 하나는 표시되는 알림을 더 효과적으로 제어하는 기능입니다.
사용자가 여러 메시지를 보내고 수신자가 여러 알림을 표시하는 채팅 애플리케이션을 예로 들 수 있습니다. 웹 앱이 조회되지 않은 알림이 여러 개 있는 것을 감지하고 이를 하나의 알림으로 축소하는 것이 이상적입니다.
getNotifications()가 없으면 이전 알림을 최신 메시지로 바꾸는 것이 가장 좋습니다. getNotifications()를 사용하면 알림이 이미 표시된 경우 알림을 '접을' 수 있으므로 사용자 환경이 훨씬 개선됩니다.
이를 위한 코드는 비교적 간단합니다. 푸시 이벤트 내에서 ServiceWorkerRegistration.getNotifications()를 호출하여 현재 알림 배열을 가져오고 여기에서 모든 알림을 접을지 아니면 Notification.tag를 사용할지 적절한 동작을 결정합니다.
function showNotification(title, body, icon, data) {
var notificationOptions = {
body: body,
icon: icon ? icon : 'images/touch/chrome-touch-icon-192x192.png',
tag: 'simple-push-demo-notification',
data: data
};
self.registration.showNotification(title, notificationOptions);
return;
}
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Since this is no payload data with the first version
// of Push notifications, here we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(API_ENDPOINT).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
// Throw an error so the promise is rejected and catch() is executed
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
var title = 'You have a new message';
var message = data.message;
var icon = 'images/notification-icon.png';
var notificationTag = 'chat-message';
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
.then(function(notifications) {
if (notifications && notifications.length > 0) {
// Start with one to account for the new notification
// we are adding
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data &&
existingNotification.data.notificationCount) {
notificationCount +=
existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
message = 'You have ' + notificationCount +
' weather updates.';
notificationData.notificationCount = notificationCount;
}
return showNotification(title, message, icon, notificationData);
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this ' +
'push message';
return showNotification(title, message);
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event);
if (Notification.prototype.hasOwnProperty('data')) {
console.log('Using Data');
var url = event.notification.data.url;
event.waitUntil(clients.openWindow(url));
} else {
event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME,
event.notification.tag).then(function(url) {
// At the moment you cannot open third party URL's, a simple trick
// is to redirect to the desired URL from a URL on your domain
var redirectUrl = '/redirect.html?redirect=' +
url;
return clients.openWindow(redirectUrl);
}));
}
});
이 코드 스니펫에서 가장 먼저 강조할 점은 getNotifications()에 필터 객체를 전달하여 알림을 필터링한다는 것입니다. 즉, 특정 태그 (이 예에서는 특정 대화)의 알림 목록을 가져올 수 있습니다.
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
그런 다음 표시되는 알림을 살펴보고 해당 알림과 연결된 알림 수가 있는지 확인하고 이를 기반으로 증분합니다. 이렇게 하면 사용자에게 읽지 않은 메시지가 2개 있다고 알리는 알림이 하나 있는 경우 새 푸시가 도착하면 읽지 않은 메시지가 3개 있다고 표시할 수 있습니다.
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data && existingNotification.data.notificationCount) {
notificationCount += existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
알림이 알림 목록에서 삭제되도록 하려면 알림에서 close()
를 호출해야 합니다. 동일한 태그가 사용되므로 각 알림이 다음 알림으로 대체되므로 이는 Chrome의 버그입니다. 현재 이 대체는 getNotifications()
에서 반환된 배열에 반영되지 않습니다.
이는 getNotifications()의 한 가지 예에 불과하며, 짐작할 수 있듯이 이 API를 사용하면 다양한 다른 사용 사례가 열립니다.
NotificationOptions.vibrate
Chrome 45부터 알림을 만들 때 진동 패턴을 지정할 수 있습니다. Vibration API를 지원하는 기기(현재 Android용 Chrome만 해당)에서는 알림이 표시될 때 사용될 진동 패턴을 맞춤설정할 수 있습니다.
진동 패턴은 숫자 배열이거나 단일 숫자일 수 있으며, 이 경우 단일 숫자 배열로 취급됩니다. 배열의 값은 밀리초 단위의 시간을 나타내며 짝수 색인 (0, 2, 4, ...)은 진동 시간, 홀수 색인은 다음 진동 전에 일시중지할 시간을 나타냅니다.
self.registration.showNotification('Buzz!', {
body: 'Bzzz bzzzz',
vibrate: [300, 100, 400] // Vibrate 300ms, pause 100ms, then vibrate 400ms
});
남은 일반적인 기능 요청
개발자의 남은 일반적인 기능 요청은 특정 시간 후에 알림을 닫는 기능 또는 표시된 알림을 닫기 위한 목적으로 푸시 알림을 보내는 기능입니다.
현재로서는 이를 실행할 방법이 없으며 이를 허용하는 사양도 없습니다. :( 하지만 Chrome 엔지니어링팀은 이 사용 사례를 인지하고 있습니다.
Android 알림
데스크톱에서는 다음 코드로 알림을 만들 수 있습니다.
new Notification('Hello', {body: 'Yay!'});
이는 플랫폼의 제한으로 인해 Android에서 지원되지 않았습니다. 특히 Chrome은 onclick과 같은 Notification 객체의 콜백을 지원할 수 없습니다. 하지만 데스크톱에서는 현재 열려 있을 수 있는 웹 앱의 알림을 표시하는 데 사용됩니다.
이 문제를 언급하는 유일한 이유는 원래 아래와 같은 간단한 기능 감지가 데스크톱을 지원하고 Android에서 오류를 일으키지 않는 데 도움이 되기 때문입니다.
if (!'Notification' in window) {
// Notifications aren't supported
return;
}
하지만 이제 Android용 Chrome에서 푸시 알림이 지원되므로 ServiceWorker에서는 알림을 만들 수 있지만 웹페이지에서는 만들 수 없습니다. 즉, 이 기능 감지는 더 이상 적절하지 않습니다. Android용 Chrome에서 알림을 만들려고 하면 다음과 같은 오류 메시지가 표시됩니다.
_Uncaught TypeError: Failed to construct 'Notification': Illegal constructor.
Use ServiceWorkerRegistration.showNotification() instead_
현재 Android 및 데스크톱에서 기능을 감지하는 가장 좋은 방법은 다음을 실행하는 것입니다.
function isNewNotificationSupported() {
if (!window.Notification || !Notification.requestPermission)
return false;
if (Notification.permission == 'granted')
throw new Error('You must only call this \*before\* calling
Notification.requestPermission(), otherwise this feature detect would bug the
user with an actual notification!');
try {
new Notification('');
} catch (e) {
if (e.name == 'TypeError')
return false;
}
return true;
}
다음과 같이 사용할 수 있습니다.
if (window.Notification && Notification.permission == 'granted') {
// We would only have prompted the user for permission if new
// Notification was supported (see below), so assume it is supported.
doStuffThatUsesNewNotification();
} else if (isNewNotificationSupported()) {
// new Notification is supported, so prompt the user for permission.
showOptInUIForNotifications();
}