웹 앱에 푸시 알림 추가

푸시 메시지를 사용하면 간편하고 효과적으로 사용자의 재참여를 유도할 수 있습니다. 이 Codelab에서는 웹 앱에 푸시 알림을 추가하는 방법을 알아봅니다.

학습할 내용

  • 푸시 메시지에 대한 사용자의 구독 및 구독 취소 방법
  • 수신 푸시 메시지를 처리하는 방법
  • 알림 표시 방법
  • 알림 클릭에 응답하는 방법

필요한 항목

  • Chrome 52 이상
  • Chrome용 웹 서버 또는 원하는 웹 서버
  • 텍스트 편집기
  • HTML, CSS, 자바스크립트, Chrome DevTools 관련 기본 지식
  • 샘플 코드 (설정 참조)

샘플 코드 다운로드

이 Codelab에서 샘플 코드를 가져오는 방법에는 두 가지가 있습니다.

  • Git 저장소를 복제합니다.
git clone https://github.com/GoogleChrome/push-notifications.git
  • ZIP 파일을 다운로드합니다.

소스 코드 다운로드

소스를 ZIP 파일로 다운로드한 경우 압축을 풀면 루트 폴더 push-notifications-master이 제공됩니다.

웹 서버 설치 및 확인하기

자체 웹 서버를 사용해도 되지만 이 Codelab은 Chrome용 웹 서버에서 잘 작동하도록 설계되어 있습니다. 이 앱이 아직 설치되어 있지 않다면 Chrome 웹 스토어에서 다운로드할 수 있습니다.

Chrome용 웹 서버 설치하기

Chrome용 웹 서버를 설치한 후 북마크바에서 단축키를 클릭합니다.

앱 창에서 웹 서버 아이콘을 클릭합니다.

다음으로 로컬 웹 서버를 구성할 수 있는 대화상자가 표시됩니다.

폴더 선택 버튼을 클릭하고 다운로드한 push-notifications 폴더에서 app 폴더를 선택합니다. 이렇게 하면 대화상자의 웹 서버 URL 섹션에 표시되는 URL을 통해 진행 중인 작업을 제공할 수 있습니다.

옵션에서 아래와 같이 index.html 자동 표시 옆의 체크박스를 선택합니다.

그런 다음 웹 서버: 시작 전환 버튼을 왼쪽으로 슬라이드한 다음 오른쪽으로 다시 전환하여 서버를 중지했다가 다시 시작합니다.

웹 서버 URL을 클릭하여 웹브라우저에서 사이트를 방문합니다. 다음과 같은 페이지가 표시됩니다. 단, 버전에 127.0.0.1:8887이 주소로 표시될 수도 있습니다.

00-push-codelab.png

항상 서비스 워커 업데이트

개발 중에는 서비스 워커를 항상 최신 상태로 유지하고 변경사항을 적용하는 것이 좋습니다.

Chrome에서 설정하는 방법은 다음과 같습니다.

  1. Push Codelab 탭으로 이동합니다.
  2. DevTools를 엽니다. Windows 및 Linux에서는 Ctrl-Shift-I, macOS에서는 Cmd-Option-I를 누릅니다.
  3. Application 패널을 선택하고 Service Workers(서비스 워커) 탭을 클릭하고 Update on Reload(새로고침 시 업데이트) 체크박스를 선택합니다. 이 체크박스를 선택하면 페이지가 새로고침될 때마다 서비스 워커가 강제로 업데이트됩니다.

완료된 코드

app 디렉터리에 이름이 sw.js인 빈 파일이 있습니다. 이 파일은 서비스 워커가 됩니다. 지금은 비워 둘 수 있습니다. 나중에 여기에 코드를 추가할 것입니다.

먼저 이 파일을 서비스 워커로 등록해야 합니다.

app/index.html 페이지에서 scripts/main.js을(를) 로드합니다. 이 자바스크립트 파일에 서비스 워커를 등록합니다.

다음 코드를 scripts/main.js에 추가합니다.

if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push are supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}

이 코드는 브라우저에서 서비스 워커와 푸시 메시지를 지원하는지 확인합니다. 지원되는 경우 코드에서 sw.js 파일을 등록합니다.

사용해 보기

브라우저에서 Push Codelab 탭을 새로고침하여 변경사항을 확인합니다.

Chrome DevTools의 콘솔에서 다음과 같이 Service Worker is registered message를 확인합니다.

애플리케이션 서버 키 가져오기

이 Codelab을 사용하려면 애플리케이션 서버 키를 생성해야 합니다. 컴패니언 사이트(web-push-codelab.glitch.me)에서 확인할 수 있습니다.

여기에서 공개 키와 비공개 키 쌍을 생성할 수 있습니다.

push-codelab-04-companion.png

공개 키를 scripts/main.js에 복사하여 <Your Public Key> 값을 바꿉니다.

const applicationServerPublicKey = '<Your Public Key>';

중요: 웹 앱에 비공개 키를 절대 포함해서는 안 됩니다.

완료된 코드

현재 웹 앱의 사용 설정 버튼이 사용 중지되어 있으므로 클릭할 수 없습니다. 이는 기본적으로 푸시 버튼을 사용 중지하고 브라우저에서 푸시 메시지가 지원되며 사용자가 현재 메시지를 구독했는지 여부를 알 수 있을 때 이를 사용 설정하는 것이 좋습니다.

scripts/main.js에서 다음 두 함수를 만들어야 합니다.

  • initializeUI: 사용자가 현재 구독 중인지 확인합니다.
  • updateBtn: 버튼을 사용 설정하고 사용자의 구독 여부에 따라 텍스트를 변경합니다.

다음과 같이 initializeUI 함수를 main.js에 추가합니다.

function initializeUI() {
  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

새 메서드는 이전 단계의 swRegistration를 사용하고 여기에서 pushManager 속성을 가져와서 여기서 getSubscription()를 호출합니다.

pushManager. getSubscription()는 현재 구독이 있는 경우 확인되는 프라미스를 반환합니다. 성공적으로 처리되지 못한 경우 null를 반환합니다. 이를 통해 사용자가 이미 구독했는지 확인하고 isSubscribed 값을 설정한 다음 updateBtn()를 호출하여 버튼을 업데이트할 수 있습니다.

updateBtn() 함수를 main.js에 추가합니다.

function updateBtn() {
  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

이 함수는 버튼을 사용 설정하고 사용자의 구독 여부에 따라 버튼 텍스트를 변경합니다.

마지막으로 할 일은 서비스 워커가 main.js에 등록되어 있을 때 initializeUI()를 호출하는 것입니다.

navigator.serviceWorker.register('sw.js')
.then(function(swReg) {
  console.log('Service Worker is registered', swReg);

  swRegistration = swReg;
  initializeUI();
})

사용해 보기

Push Codelab 탭을 새로고침합니다. 푸시 메시지 사용 설정 버튼이 사용 설정되었으며 (클릭할 수 있음) 콘솔에 User is NOT subscribed가 표시됩니다.

이 Codelab의 나머지 부분을 진행하면서 구독하거나 구독 취소할 때마다 버튼 텍스트가 변경되는 것을 확인할 수 있습니다.

완료된 코드

현재 푸시 메시지 사용 버튼은 별로 많은 작업을 하지 않습니다. 이 문제를 해결해보겠습니다.

initializeUI() 함수에서 버튼의 클릭 리스너를 추가합니다.

function initializeUI() {
  pushButton.addEventListener('click', function() {
    pushButton.disabled = true;
    if (isSubscribed) {
      // TODO: Unsubscribe user
    } else {
      subscribeUser();
    }
  });

  // Set the initial subscription value
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    isSubscribed = !(subscription === null);

    updateSubscriptionOnServer(subscription);

    if (isSubscribed) {
      console.log('User IS subscribed.');
    } else {
      console.log('User is NOT subscribed.');
    }

    updateBtn();
  });
}

사용자가 푸시 버튼을 클릭하면 버튼을 다시 사용 설정할 수 있도록 버튼을 사용 중지합니다. 푸시 메시지를 구독하는 데 시간이 걸릴 수 있기 때문입니다.

그런 다음 사용자가 현재 구독하지 않는 경우 subscribeUser()를 호출합니다. 이를 위해 다음 코드를 scripts/main.js에 붙여넣어야 합니다.

function subscribeUser() {
  const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
  swRegistration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: applicationServerKey
  })
  .then(function(subscription) {
    console.log('User is subscribed.');

    updateSubscriptionOnServer(subscription);

    isSubscribed = true;

    updateBtn();
  })
  .catch(function(error) {
    console.error('Failed to subscribe the user: ', error);
    updateBtn();
  });
}

이 코드의 역할에 관한 단계별 안내와 푸시 메시지를 수신하도록 사용자를 구독하는 방법을 알아보겠습니다.

먼저 subscribe()서버의 예상 입력이므로 애플리케이션 서버의 공개 키를 Base64 URL 안전 인코딩으로 UInt8Array로 변환합니다. urlB64ToUint8Array() 함수는 scripts/main.js의 상단에 있습니다.

값을 변환한 후 서비스 워커 pushManager에서 subscribe() 메서드를 호출하여 애플리케이션 서버의 공개 키와 값 userVisibleOnly: true을 전달합니다.

const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);
swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})

userVisibleOnly 매개변수는 푸시 메시지가 전송될 때마다 알림을 표시합니다. 현재 이 값은 필수 항목이며 true여야 합니다.

subscribe()를 호출하면 다음 단계 후 확인되는 프라미스가 반환됩니다.

  1. 사용자가 알림을 표시할 권한을 부여했습니다.
  2. 브라우저가 PushSubscription을 생성하는 데 필요한 데이터를 가져오기 위해 푸시 서비스에 네트워크 요청을 보냈습니다.

이 단계가 성공하면 subscribe() 프라미스가 PushSubscription로 확인됩니다. 사용자가 권한을 부여하지 않거나 사용자를 구독하는 데 문제가 있는 경우 프로미스가 오류와 함께 거부됩니다. 그러면 Codelab에서 다음과 같은 프라미스 체인이 표시됩니다.

swRegistration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
})
.then(function(subscription) {
  console.log('User is subscribed.');

  updateSubscriptionOnServer(subscription);

  isSubscribed = true;

  updateBtn();

})
.catch(function(err) {
  console.log('Failed to subscribe the user: ', err);
  updateBtn();
});

이렇게 하면 구독을 받아서 사용자를 구독한 것으로 취급하거나 오류를 포착하여 콘솔에 로깅할 수 있습니다. 두 경우 모두 updateBtn()를 호출하여 버튼이 다시 사용 설정되고 적절한 텍스트가 있는지 확인합니다.

실제 애플리케이션에서 updateSubscriptionOnServer() 함수는 백엔드로 구독 데이터를 보내지만 Codelab에서는 UI에 구독을 표시하기만 합니다. 다음 함수를 scripts/main.js에 추가합니다.

function updateSubscriptionOnServer(subscription) {
  // TODO: Send subscription to application server

  const subscriptionJson = document.querySelector('.js-subscription-json');
  const subscriptionDetails =
    document.querySelector('.js-subscription-details');

  if (subscription) {
    subscriptionJson.textContent = JSON.stringify(subscription);
    subscriptionDetails.classList.remove('is-invisible');
  } else {
    subscriptionDetails.classList.add('is-invisible');
  }
}

사용해 보기

Push Codelab 탭으로 이동하여 페이지를 새로고침한 후 버튼을 클릭합니다. 다음과 같은 권한 메시지가 표시됩니다.

권한을 부여하면 콘솔에 User is subscribed가 로깅됩니다. 버튼의 텍스트가 푸시 메시지 사용 안함으로 변경되고 페이지 하단에서 구독을 JSON 데이터로 볼 수 있습니다.

완료된 코드

아직 처리되지 않은 부분은 사용자가 권한 요청을 차단하는 경우입니다. 이 경우 사용자가 권한을 차단하면 웹 앱에서 권한 메시지를 다시 표시할 수 없고 사용자를 구독할 수 없기 때문에 고유한 고려사항이 필요합니다. 적어도 사용자가 버튼을 사용할 수 없음을 알 수 있도록 푸시 버튼을 사용 중지해야 합니다.

이러한 상황을 처리할 수 있는 명확한 위치는 updateBtn() 함수입니다. 다음과 같이 Notification.permission 값을 확인하기만 하면 됩니다.

function updateBtn() {
  if (Notification.permission === 'denied') {
    pushButton.textContent = 'Push Messaging Blocked';
    pushButton.disabled = true;
    updateSubscriptionOnServer(null);
    return;
  }

  if (isSubscribed) {
    pushButton.textContent = 'Disable Push Messaging';
  } else {
    pushButton.textContent = 'Enable Push Messaging';
  }

  pushButton.disabled = false;
}

권한이 denied인 경우 사용자가 구독할 수 없으며 더 이상 취할 수 있는 작업이 없으므로 버튼을 영구적으로 사용 중지하는 것이 가장 좋습니다.

사용해 보기

이전 단계에서 이미 웹 앱에 권한을 부여했으므로 URL 표시줄의 원에 있는 i를 클릭하고 알림 권한을 전역 기본값 (질문하기)으로 변경해야 합니다.

이 설정을 변경한 후 페이지를 새로고침하고 푸시 메시지 사용 설정 버튼을 클릭한 후 권한 대화상자에서 차단을 선택합니다. 버튼이 사용 중지되고 푸시 메시지가 차단됨으로 표시됩니다.

이번 변경을 통해 가능한 권한 시나리오를 고려하여 사용자를 구독할 수 있습니다.

완료된 코드

백엔드에서 푸시 메시지를 보내는 방법을 알아보기 전에, 구독한 사용자가 푸시 메시지를 수신할 때 실제로 어떤 일이 일어날지 고려해야 합니다.

푸시 메시지를 트리거하면 브라우저에서 푸시 메시지를 수신하고 푸시의 서비스 워커를 파악한 후 서비스 워커의 절전 모드를 해제하고 푸시 이벤트를 발송합니다. 이 이벤트를 수신하고 그 결과로 알림을 표시해야 합니다.

다음 코드를 sw.js 파일에 추가하세요.

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: 'Yay it works.',
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

이 코드를 단계별로 실행하세요. 이벤트 리스너를 추가하여 서비스 워커에서 push 이벤트를 수신 대기합니다.

self.addEventListener('push', ... );

웹 작업자와 함께해 본 적이 없다면 self은(는) 처음 하게 될 것입니다. 서비스 워커 파일에서 self는 서비스 워커 자체를 참조합니다.)

푸시 메시지가 수신되면 이벤트 리스너가 호출되고 서비스 워커의 registration 속성에서 showNotification()를 호출하여 알림을 생성합니다. showNotification()에는 title이 필요합니다. 또한 본문 객체, 아이콘 및 배지를 설정하는 options 객체를 제공할 수도 있습니다. 배지는 작성 시점에 Android에서만 사용됩니다.

const title = 'Push Codelab';
const options = {
  body: 'Yay it works.',
  icon: 'images/icon.png',
  badge: 'images/badge.png'
};
self.registration.showNotification(title, options);

push 이벤트 처리에서 마지막으로 다룹니다. event.waitUntil() 이 메서드는 프라미스를 사용하여 서비스 워커가 활성 상태로 유지되고 전달된 프라미스가 확인될 때까지 계속 실행되도록 합니다.

위의 코드를 좀 더 쉽게 이해할 수 있도록 다음과 같이 재작성할 수 있습니다.

const notificationPromise = self.registration.showNotification(title, options);
event.waitUntil(notificationPromise);

푸시 이벤트를 단계별로 실행했으므로 이제 푸시 이벤트를 테스트해 보겠습니다.

사용해 보기

서비스 워커에서 푸시 이벤트 처리를 사용하면 가짜 푸시 이벤트를 트리거하여 메시지가 수신될 때 발생하는 작업을 테스트할 수 있습니다.

웹 앱에서 푸시 메시지를 구독하고 콘솔에 User IS subscribed가 표시되는지 확인합니다. DevTools의 Application 패널에 있는 Service Workers 탭에서 Push 버튼을 클릭합니다.

Push(푸시)를 클릭하면 다음과 같은 알림이 표시됩니다.

참고: 이 단계를 따라도 문제가 해결되지 않으면 DevTools 애플리케이션 패널에서 등록 취소 링크를 사용하여 서비스 워커를 등록 취소하고 서비스 워커가 중지될 때까지 기다린 후 페이지를 새로고침하세요.

완료된 코드

이러한 알림 중 하나를 클릭하면 아무 일도 일어나지 않습니다. 서비스 워커에서 notificationclick 이벤트를 수신 대기하여 알림 클릭을 처리할 수 있습니다.

먼저 sw.jsnotificationclick 리스너를 추가합니다.

self.addEventListener('notificationclick', function(event) {
  console.log('[Service Worker] Notification click received.');

  event.notification.close();

  event.waitUntil(
    clients.openWindow('https://developers.google.com/web')
  );
});

사용자가 알림을 클릭하면 notificationclick 이벤트 리스너가 호출됩니다.

이 코드는 먼저 클릭된 알림을 닫습니다.

event.notification.close();

그러면 URL https://developers.google.com/web이 로드되는 새 창 또는 탭이 열립니다. 자유롭게 변경하세요.

event.waitUntil(
    clients.openWindow('https://developers.google.com/web/')
  );

event.waitUntil()는 새 창이나 탭이 표시되기 전에 브라우저가 서비스 워커를 종료하지 않도록 합니다.

사용해 보기

DevTools에서 푸시 메시지를 다시 트리거하고 알림을 클릭합니다. 이제 알림이 닫히고 새 탭이 열립니다.

웹 앱에서 DevTools를 사용하여 알림을 표시할 수 있으며, 클릭 한 번으로 알림을 닫는 방법을 살펴보았습니다. 다음 단계는 실제 푸시 메시지를 보내는 것입니다.

일반적으로 웹페이지에서 백엔드로 구독을 전송해야 합니다. 그러면 백엔드에서 구독의 엔드포인트에 API를 호출하여 푸시 메시지를 트리거합니다.

이러한 내용은 이 Codelab에서 다루지 않지만, 컴패니언 사이트 (web-push-codelab.glitch.me)를 사용하여 실제 푸시 메시지를 트리거할 수 있습니다. 페이지 하단에 구독을 붙여넣습니다.

그런 다음 보내기 대상 텍스트 영역의 컴패니언 사이트에 붙여넣습니다.

전송할 텍스트에 푸시 메시지로 보내려는 문자열을 추가합니다.

푸시 메시지 보내기 버튼을 클릭합니다.

그러면 푸시 메시지가 수신됩니다. 사용한 텍스트는 콘솔에 로깅됩니다.

이렇게 하면 데이터 송수신을 테스트하고 그 결과로 알림을 조작할 수 있습니다.

호환 앱은 웹-푸시 라이브러리를 사용하여 메시지를 보내는 노드 서버일 뿐입니다. GitHub의 web-push-libs org를 검토하여 푸시 메시지를 전송하는 데 사용할 수 있는 라이브러리를 알아보세요. 푸시 메시지를 트리거하기 위한 많은 세부정보가 처리됩니다.

여기에 모든 컴패니언 사이트 코드가 나와 있습니다.

완료된 코드

한 가지 누락된 부분이 있다면 사용자가 푸시 알림을 수신 거부하는 기능이었습니다. 이렇게 하려면 PushSubscription에서 unsubscribe()를 호출해야 합니다.

다시 scripts/main.js 파일로 돌아가 initializeUI()pushButton 클릭 리스너를 다음과 같이 변경합니다.

pushButton.addEventListener('click', function() {
  pushButton.disabled = true;
  if (isSubscribed) {
    unsubscribeUser();
  } else {
    subscribeUser();
  }
});

이제 새 함수 unsubscribeUser()를 호출합니다. 이 함수에서 현재 구독을 가져오고 여기서 unsubscribe()를 호출합니다. scripts/main.js에 다음 코드를 추가합니다.

function unsubscribeUser() {
  swRegistration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription) {
      return subscription.unsubscribe();
    }
  })
  .catch(function(error) {
    console.log('Error unsubscribing', error);
  })
  .then(function() {
    updateSubscriptionOnServer(null);

    console.log('User is unsubscribed.');
    isSubscribed = false;

    updateBtn();
  });
}

이 함수를 단계별로 진행해 보세요.

먼저 getSubscription()를 호출하여 현재 구독을 가져옵니다.

swRegistration.pushManager.getSubscription()

그러면 PushSubscription로 확인되는 프라미스가 있고(있는 경우) 그렇지 않으면 null가 반환됩니다. 정기 결제가 있으면 unsubscribe()를 호출하여 PushSubscription가 무효화됩니다.

swRegistration.pushManager.getSubscription()
.then(function(subscription) {
  if (subscription) {
    // TODO: Tell application server to delete subscription
    return subscription.unsubscribe();
  }
})
.catch(function(error) {
  console.log('Error unsubscribing', error);
})

unsubscribe()를 호출하면 완료하는 데 다소 시간이 걸릴 수 있으므로 프라미스를 반환합니다. 프라미스를 반환하여 체인의 다음 then()unsubscribe()이 완료되기를 기다립니다. unsubscribe()를 호출하면 오류가 발생하는 경우 catch 핸들러도 추가합니다. 그런 다음 UI를 업데이트할 수 있습니다.

.then(function() {
  updateSubscriptionOnServer(null);

  console.log('User is unsubscribed.');
  isSubscribed = false;

  updateBtn();
})

사용해 보기

웹 앱에서 푸시 메시지 사용 설정 또는 푸시 메시지 사용 중지를 누를 수 있어야 하며, 이 로그에는 사용자의 구독 및 구독 취소가 표시됩니다.

축하합니다. 이 Codelab을 완료했습니다.

이 Codelab에서는 웹 앱에 푸시 알림을 추가하여 시작하는 방법을 살펴보았습니다. 웹 알림의 기능을 자세히 알아보려면 이 문서를 확인하세요.

사이트에 푸시 알림을 배포하려는 경우 이전 브라우저 또는 GCM을 사용하는 비표준 호환 브라우저에 대한 지원을 추가하는 것이 좋습니다. 자세히 알아보기

추가 자료

관련 블로그 게시물