웹 앱에 푸시 알림 추가

푸시 메시지는 사용자와 다시 소통할 수 있는 간단하고 효과적인 방법을 제공합니다. 이 Codelab에서는 웹 앱에 푸시 알림을 추가하는 방법을 알아봅니다.

학습할 내용

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

필요한 항목

  • Chrome 52 이상
  • Chrome용 웹 서버 또는 원하는 웹 서버
  • 텍스트 편집기
  • HTML, CSS, JavaScript, 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. 푸시 Codelab 탭으로 이동합니다.
  2. 개발자 도구를 엽니다. Windows 및 Linux에서는 Ctrl-Shift-I, macOS에서는 Cmd-Option-I를 누릅니다.
  3. 애플리케이션 패널을 선택하고 서비스 워커 탭을 클릭한 후 새로고침 시 업데이트 체크박스를 선택합니다. 이 체크박스를 사용 설정하면 페이지가 새로고침될 때마다 서비스 워커가 강제로 업데이트됩니다.

완료된 코드

app 디렉터리에 sw.js이라는 빈 파일이 있습니다. 이 파일이 서비스 워커가 됩니다. 지금은 비워 두어도 됩니다. 나중에 여기에 코드를 추가할 예정입니다.

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

app/index.html 페이지가 로드됩니다scripts/main.js. 이 JavaScript 파일에 서비스 워커를 등록합니다.

다음 코드를 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: 사용자가 구독 중인지 여부에 따라 버튼을 사용 설정하고 텍스트를 변경합니다.

다음과 같이 main.jsinitializeUI 함수를 추가합니다.

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()를 호출하여 버튼을 업데이트할 수 있습니다.

main.jsupdateBtn() 함수를 추가합니다.

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();
})

사용해 보기

푸시 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();
  });
}

이 코드가 하는 일과 푸시 메시지를 위해 사용자를 구독하는 방법을 단계별로 살펴보겠습니다.

먼저 Base64 URL 보안 인코딩된 애플리케이션 서버의 공개 키를 가져와 UInt8Array로 변환합니다. 이는 subscribe() 호출의 예상 입력이기 때문입니다. 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');
  }
}

사용해 보기

푸시 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', ... );

(Web Worker를 사용해 본 적이 없다면 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 버튼을 클릭합니다.

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

참고: 이 단계가 작동하지 않으면 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)를 사용하면 됩니다. 페이지 하단에 구독을 붙여넣습니다.

그런 다음 이 코드를 전송할 구독 텍스트 영역의 동반자 사이트에 붙여넣습니다.

전송할 텍스트에 푸시 메시지와 함께 전송할 문자열을 추가합니다.

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

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

이를 통해 데이터를 보내고 받는 것을 테스트하고 그 결과로 알림을 조작할 수 있습니다.

동반 앱은 web-push 라이브러리를 사용하여 메시지를 전송하는 노드 서버일 뿐입니다. 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가 있으면 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을 사용하는 이전 브라우저 또는 표준 미준수 브라우저에 대한 지원을 추가하는 것이 좋습니다. 자세히 알아보기

추가 자료

관련 블로그 게시물