일반적인 알림 패턴

웹 푸시의 몇 가지 일반적인 구현 패턴을 살펴보겠습니다.

이를 위해서는 서비스 워커에서 사용할 수 있는 몇 가지 API를 사용해야 합니다.

알림 닫기 이벤트

이전 섹션에서는 notificationclick 이벤트를 수신 대기하는 방법을 알아봤습니다.

사용자가 다음 중 하나를 닫으면 호출되는 notificationclose 이벤트도 있습니다. 알림을 클릭하는 대신 X 표시를 클릭하거나 표시되지 않음).

이 이벤트는 일반적으로 알림을 통한 사용자 참여를 추적하는 분석에 사용됩니다.

self.addEventListener('notificationclose', function (event) {
  const dismissedNotification = event.notification;

  const promiseChain = notificationCloseAnalytics();
  event.waitUntil(promiseChain);
});

알림에 데이터 추가

푸시 메시지가 수신되면 사용자가 알림을 클릭한 경우에 유용합니다. 예를 들어 URL 열려야 합니다.

푸시 이벤트에서 데이터를 가져와 전달된 옵션 객체에 data 매개변수를 추가하는 것입니다. 다음과 같이 showNotification()로 변환합니다.

const options = {
  body:
    'This notification has data attached to it that is printed ' +
    "to the console when it's clicked.",
  tag: 'data-notification',
  data: {
    time: new Date(Date.now()).toString(),
    message: 'Hello, World!',
  },
};
registration.showNotification('Notification with Data', options);

클릭 핸들러 내에서 event.notification.data를 사용하여 데이터에 액세스할 수 있습니다.

const notificationData = event.notification.data;
console.log('');
console.log('The notification data has the following parameters:');
Object.keys(notificationData).forEach((key) => {
  console.log(`  ${key}: ${notificationData[key]}`);
});
console.log('');

창 열기

알림에 대한 가장 일반적인 응답 중 하나는 특정 URL로 이동할 수 있습니다. 이 작업은 clients.openWindow() 드림 API에 액세스할 수 있습니다.

notificationclick 이벤트에서 다음과 같은 코드를 실행합니다.

const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);

다음 섹션에서는 사용자를 연결하려는 페이지가 확인할 수 있습니다. 이렇게 하면 새 창을 여는 대신 열려 있는 탭에 탭

기존 창에 포커스

가능하면 사용자가 매번 새 창을 열지 않고 창에 포커스를 맞춰야 합니다. 알림을 클릭합니다.

이 목표를 달성하는 방법을 살펴보기 전에 이 기능이 원본의 페이지에서만 가능합니다. 이는 우리가 Google 사이트에 속한 페이지만 볼 수 있습니다. 이렇게 하면 개발자가 사용자가 보고 있는 모든 사이트를 볼 수 없도록 합니다.

이전 예에서 코드가 올바르게 작동하는지 /demos/notification-examples/example-page.html이(가) 이미 열려 있습니다.

const urlToOpen = new URL(examplePage, self.location.origin).href;

const promiseChain = clients
  .matchAll({
    type: 'window',
    includeUncontrolled: true,
  })
  .then((windowClients) => {
    let matchingClient = null;

    for (let i = 0; i < windowClients.length; i++) {
      const windowClient = windowClients[i];
      if (windowClient.url === urlToOpen) {
        matchingClient = windowClient;
        break;
      }
    }

    if (matchingClient) {
      return matchingClient.focus();
    } else {
      return clients.openWindow(urlToOpen);
    }
  });

event.waitUntil(promiseChain);

코드를 단계별로 진행해 보겠습니다.

먼저 URL API를 사용하여 예시 페이지를 파싱합니다. 제프가 알려준 좋은 팁입니다. Posnick과 함께 모델을 학습시킬 수 있습니다 location 객체로 new URL()를 호출하면 전달된 문자열이 상대적인 경우 (즉, /https://example.com/).

나중에 창 URL과 일치시킬 수 있도록 URL을 절대값으로 설정합니다.

const urlToOpen = new URL(examplePage, self.location.origin).href;

그런 다음 WindowClient 객체 목록을 가져옵니다. 이 목록은 현재 열려 있는 탭과 창입니다. 이 탭은 원본 전용 탭입니다.

const promiseChain = clients.matchAll({
  type: 'window',
  includeUncontrolled: true,
});

matchAll에 전달된 옵션은 'window'를 검색 입력 클라이언트 (예: 탭과 창 웹 작업자 제외) includeUncontrolled를 사용하면 현재 서비스에서 제어하지 않는 원본의 모든 탭 작업자, 즉 이 코드를 실행하는 서비스 워커입니다. 일반적으로 matchAll()를 호출할 때 항상 includeUncontrolled가 true가 되기를 바랍니다.

반환된 프로미스를 promiseChain로 캡처하여 event.waitUntil() 후 서비스 워커를 활성 상태로 유지합니다.

matchAll() 프로미스가 확인되면 반환된 윈도우 클라이언트를 반복하고 열려는 URL과 비교할 수 있습니다. 일치하는 항목을 찾으면 사용자의 관심을 끌 수 있습니다. 포커스는 matchingClient.focus() 호출

일치하는 고객을 찾을 수 없는 경우 이전 섹션과 마찬가지로 새 창이 열립니다.

.then((windowClients) => {
  let matchingClient = null;

  for (let i = 0; i < windowClients.length; i++) {
    const windowClient = windowClients[i];
    if (windowClient.url === urlToOpen) {
      matchingClient = windowClient;
      break;
    }
  }

  if (matchingClient) {
    return matchingClient.focus();
  } else {
    return clients.openWindow(urlToOpen);
  }
});
드림

알림 병합

알림에 태그를 추가하면 같은 태그가 있는 기존 알림이 대체됩니다.

하지만 알림 API 개발자가 새로운 알림을 받고 싶을 수 있는 채팅 앱을 생각해 보세요. "영희가 보낸 메시지가 두 개 있습니다."와 유사한 메시지를 표시합니다. 최신 버전을 표시하는 것이 아니라 메시지가 표시됩니다.

이렇게 하거나 registration.getNotifications() API를 사용하면 웹 앱에 현재 표시되는 모든 알림에 액세스할 수 있습니다.

이 API를 사용하여 채팅 예시를 구현하는 방법을 살펴보겠습니다.

채팅 앱에서 각 알림에 사용자 이름이 포함된 데이터가 있다고 가정해 보겠습니다.

가장 먼저 해야 할 일은 특정 상태가 있는 사용자에 대해 열려 있는 알림을 찾는 것입니다. 사용자 이름을 입력하세요. registration.getNotifications()를 가져와서 루프를 실행하고 특정 사용자 이름: notification.data

const promiseChain = registration.getNotifications().then((notifications) => {
  let currentNotification;

  for (let i = 0; i < notifications.length; i++) {
    if (notifications[i].data && notifications[i].data.userName === userName) {
      currentNotification = notifications[i];
    }
  }

  return currentNotification;
});

다음 단계는 이 알림을 새 알림으로 대체하는 것입니다.

이 가짜 메시지 앱에서는 새 메시지의 개수를 추적하여 알림의 데이터를 업데이트하고 새 알림이 있을 때마다 이를 증가시킵니다.

.then((currentNotification) => {
  let notificationTitle;
  const options = {
    icon: userIcon,
  }

  if (currentNotification) {
    // We have an open notification, let's do something with it.
    const messageCount = currentNotification.data.newMessageCount + 1;

    options.body = `You have ${messageCount} new messages from ${userName}.`;
    options.data = {
      userName: userName,
      newMessageCount: messageCount
    };
    notificationTitle = `New Messages from ${userName}`;

    // Remember to close the old notification.
    currentNotification.close();
  } else {
    options.body = `"${userMessage}"`;
    options.data = {
      userName: userName,
      newMessageCount: 1
    };
    notificationTitle = `New Message from ${userName}`;
  }

  return registration.showNotification(
    notificationTitle,
    options
  );
});

현재 표시된 알림이 있으면 메시지 수를 증가시키고 알림 제목과 본문 메시지를 적절히 수정합니다. 만약 알림이 없으면 newMessageCount이 1인 새 알림을 만듭니다.

첫 번째 메시지는 다음과 같습니다.

병합하지 않은 첫 번째 알림입니다.

두 번째 알림은 알림을 다음과 같이 축소합니다.

병합이 포함된 두 번째 알림입니다.

이 접근 방식의 장점은 사용자가 여러 개의 알림이 하나씩 표시되어 더욱 일관성 있게 꾸밀 수 있습니다. 최신 메시지로 알림을 바꾸는 것보다 더 효과적입니다.

규칙의 예외

푸시를 받을 때 알림을 반드시 표시해야 한다고 말씀드렸는데, 이는 대부분 참입니다. 알림을 표시할 필요가 없는 시나리오는 사용자가 사이트를 열어놓고 집중하고 있는 것입니다

푸시 이벤트 내에서 윈도우 클라이언트를 살펴보고 포커스가 맞춰진 창을 찾고 있습니다

모든 창을 가져오고 포커스가 맞춰진 창을 찾는 코드는 다음과 같습니다.

function isClientFocused() {
  return clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true,
    })
    .then((windowClients) => {
      let clientIsFocused = false;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.focused) {
          clientIsFocused = true;
          break;
        }
      }

      return clientIsFocused;
    });
}

clients.matchAll()를 사용합니다. 를 사용하여 모든 창 클라이언트를 가져온 다음 focused 매개변수를 확인하여 루프를 돌립니다.

푸시 이벤트 내에서 이 함수를 사용하여 알림을 표시해야 하는지 결정합니다.

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    console.log("Don't need to show a notification.");
    return;
  }

  // Client isn't focused, we need to show a notification.
  return self.registration.showNotification('Had to show a notification.');
});

event.waitUntil(promiseChain);

푸시 이벤트로부터 페이지 메시지 보내기

사용자가 현재 사이트를 보고 있는 경우 알림 표시를 건너뛸 수 있는 것으로 확인되었습니다. 하지만 사용자에게 이벤트가 발생했다고 알려주고 싶지만 알림이 너무 무거운 손이요?

한 가지 접근 방식은 서비스 워커에서 페이지로 메시지를 보내는 것으로, 이런 식으로 웹 페이지는 사용자에게 알림이나 업데이트를 표시하여 이벤트를 알릴 수 있습니다. 이는 페이지의 미묘한 알림이 사용자에게 더 낫고 친근한 상황입니다.

푸시를 수신하고 웹 앱에 현재 포커스가 있는지 확인했다고 가정해 보겠습니다. 그런 다음 '메시지를 게시'하고 로 연결합니다.

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    windowClients.forEach((windowClient) => {
      windowClient.postMessage({
        message: 'Received a push message.',
        time: new Date().toString(),
      });
    });
  } else {
    return self.registration.showNotification('No focused windows', {
      body: 'Had to show a notification instead of messaging each page.',
    });
  }
});

event.waitUntil(promiseChain);

각 페이지에서 메시지 이벤트를 추가하여 메시지를 수신합니다. 리스너:

navigator.serviceWorker.addEventListener('message', function (event) {
  console.log('Received a message from service worker: ', event.data);
});

이 메시지 리스너에서는 원하는 모든 작업을 할 수 있습니다. 메시지를 완전히 무시할 수 있습니다.

또한 웹페이지에서 메시지 리스너를 정의하지 않으면 메시지가 도착하면 아무 일도 일어나지 않습니다.

페이지를 캐시하고 창 열기

이 가이드의 범위에 포함되지는 않지만 살펴볼 만한 시나리오는 사용자가 방문할 것으로 예상되는 웹페이지를 캐시하여 웹 앱의 전반적인 UX 개선 알림을 클릭합니다.

그러려면 fetch 이벤트를 처리하도록 서비스 워커를 설정해야 합니다. fetch 이벤트 리스너를 구현하는 경우 페이지와 애셋을 캐시하여 push 이벤트에서 활용 확인해야 합니다.

브라우저 호환성

notificationclose 이벤트

브라우저 지원

  • Chrome: 50. <ph type="x-smartling-placeholder">
  • Edge: 17. <ph type="x-smartling-placeholder">
  • Firefox: 44. <ph type="x-smartling-placeholder">
  • Safari: 16. <ph type="x-smartling-placeholder">

소스

Clients.openWindow()

브라우저 지원

  • Chrome: 40. <ph type="x-smartling-placeholder">
  • Edge: 17. <ph type="x-smartling-placeholder">
  • Firefox: 44. <ph type="x-smartling-placeholder">
  • Safari: 11.1. <ph type="x-smartling-placeholder">

소스

ServiceWorkerRegistration.getNotifications()

브라우저 지원

  • Chrome: 40. <ph type="x-smartling-placeholder">
  • Edge: 17. <ph type="x-smartling-placeholder">
  • Firefox: 44. <ph type="x-smartling-placeholder">
  • Safari: 16. <ph type="x-smartling-placeholder">

소스

clients.matchAll()

브라우저 지원

  • Chrome: 42. <ph type="x-smartling-placeholder">
  • Edge: 17. <ph type="x-smartling-placeholder">
  • Firefox: 54. <ph type="x-smartling-placeholder">
  • Safari: 11.1. <ph type="x-smartling-placeholder">

소스

자세한 내용은 이 서비스 워커 소개 게시물을 참고하세요.

다음에 수행할 작업

Codelab