알림 변경사항 알림

맷 곤트

먼저, 제목이 끔찍한 점에 대해 사과드리며, 그렇게 하지 못했습니다.

Chrome 44에서는 Notfication.dataServiceWorkerRegistration.getNotifications()를 추가하고 푸시 메시지로 알림을 처리할 때 몇 가지 일반적인 사용 사례를 열고 단순화합니다.

알림 데이터

Notification.data를 사용하면 자바스크립트 객체를 알림과 연결할 수 있습니다.

기본적으로 이 방법은 푸시 메시지를 수신할 때 일부 데이터로 알림을 만든 다음 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
    })
    );
});

알림 클릭 이벤트에서 정보를 가져올 수 있습니다.

self.addEventListener('notificationclick', function(event) {
    var doge = event.notification.data.doge;
    console.log(doge.wow);
});

이전에는 IndexDB에 데이터를 보관하거나 아이콘 URL 끝에 무언가를 추가해야 했습니다.

ServiceWorkerRegistration.getNotifications()

푸시 알림을 개발하는 개발자들이 일반적으로 요청하는 사항 중 하나는 표시되는 알림을 더 효과적으로 관리하고 싶다는 것입니다.

예를 들어 사용자가 여러 메시지를 보내고 수신자가 여러 알림을 표시하는 채팅 애플리케이션이 있습니다. 이상적으로는 웹 앱에서는 확인하지 않은 여러 알림이 있는 것을 알아차려 하나의 알림으로 축소할 수 있습니다.

getNotification()이 없으면 이전 알림을 최신 메시지로 바꾸는 것이 가장 좋습니다. 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)

그런 다음 표시되는 알림을 살펴보고 해당 알림과 관련된 알림 수가 있는지 확인하고 이에 따라 증가합니다. 이렇게 하면 사용자에게 읽지 않은 메시지가 두 개 있음을 알리는 알림이 하나 있는 경우 새 푸시가 도착하면 읽지 않은 메시지가 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()에서 반환된 배열에 반영되지 않습니다.

이는 getNotification()의 한 가지 예일 뿐이며 예상할 수 있듯이 이 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은 알림 객체의 콜백(예: 온)을 지원할 수 없습니다. 그러나 데스크톱에서는 현재 열어 놓은 웹 앱의 알림을 표시하는 데 사용됩니다.

처음에 말씀드린 이유는 아래와 같은 간단한 기능 감지를 통해 데스크톱을 지원하고 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();
    }