Firebase Web 채팅앱 만들기 - 16. Cloud Messaging과 Functions을 이용한 푸시메세지 기능 - Service worker를 이용한 FCM수신

  • FCM 수신은 앱이 구동되고 있을 때 수신하는 포그라운드로 수신하는 방법과 앱이 구동되고있지 않을 때 백그라운드로 수신하는 방법이 있습니다. 접속하지 않은 유저들에게만 푸시를 보내려고 하기 때문에 백그라운드만 작성할 계획입니다.

1. 포그라운드 FCM 수신

  • 포그라운드로 수신하는 방법도 잠시 소개합니다. 아래는 포그라운드로 수신하는 코드입니다. Firebase Messaging을 설정할때 권한을 획득하면서 requestPermission 메소드를 실행했는데, 이 메소드 이후에 적당한 위치에서 실행하면 됩니다.
firebase.messaging().onMessage(function(payload) {
    var options = {
        body : payload.notification.body
        , icon : payload.notification.icon
    }
    var notification = new Notification(payload.notification.title, options);
    notification.onclick= function(event){
        event.preventDefault();
        ... (생략) 알림 클릭시 코드 작성
    }
});

2. 백그라운드 FCM 수신

  • 이제 백그라운드 수신하는 것을 작성하겠습니다. 먼저 프로젝트내에 public 폴더 아래에 manifest.json 파일을 하나 만듭니다. mainfiest.json 파일은 웹에 부가기능을 명시하는 파일입니다. 오프라인 웹앱을 만들 때 주로 사용됩니다. Firebase Messaging 수신을 위해 아래의 코드를 입력합니다.

2.1 manifest.json

  • ‘103953800507’ 이라는 숫자는 고정값입니다. 프로젝트마다 달라지거나 하는 숫자가 아니며 Firebase messaging서비스를 수신하기 위한 고정값입니다.
{ 
  "gcm_sender_id": "103953800507" 
}
  • manifest.json파일 완성 후 index.html 파일의 head 태그 안에 아래의 코드를 입력해주세요
<link rel="manifest" href="manifest.json"/>

2.2 Service Worker 작성

2.2.1 Service worker 란 무엇일까요?

  • Service woker는 브라우저가 백그라운드에서 실행하는 스크립트입니다.
  • 웹페이지와 상호작용하지 않는 기능들을 구현할 수 있습니다. 웹브라우저의 개발자 도구를 열어보겠습니다.
  • 아래의 화면은 크롬개발자 도구입니다.
  • 개발자도구에서 Application 탭에 Service Wokers 항목에 들어가 Show all에 체크를 해보았습니다. 저의 브라우저에는 여러가지 서비스워커가 이미 구동이 되고 있군요. 크롬 브라우저를 실행하게 되면 페이스북 푸시가 오는 경우가 있는데 이는 Service worker가 수행한 일입니다.

16-1

2.2.2 코드 작성

  • 프로젝트내 public폴더에 ‘firebase-messaging-sw.js’ 파일명으로 파일을 하나 만듭니다. 그리고 아래의 코드를 입력합니다.
importScripts('https://www.gstatic.com/firebasejs/4.6.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.6.1/firebase-messaging.js');

firebase.initializeApp({
    'messagingSenderId': '151504463082' //이곳은 자신의 프로젝트 설정 => 클라우드 메세징 => 발신자ID를 기입
});

const messaging = firebase.messaging();

self.addEventListener('push', function(event) {
    const payload = event.data.json();
    const title = payload.notification.title;
    const options = {
        body: payload.notification.body,
        icon: payload.notification.icon,
        data: payload.notification.click_action
    };

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

self.addEventListener('notificationclick', function(event) {
    console.log(event.notification);
    event.notification.close();
    event.waitUntil(
        clients.openWindow(event.notification.data)
    );
});
  • 코드 설명 :
    • importScripts는 woker파일에서 외부 라이브러리를 불러올 때 사용합니다.
    • initializeApp 메소드는 Functions코드와 같이 firebase를 초기화 합니다. 여기서는 Messaging을 초기화
      importScripts('https://www.gstatic.com/firebasejs/4.6.1/firebase-app.js');
      importScripts('https://www.gstatic.com/firebasejs/4.6.1/firebase-messaging.js');
        
      firebase.initializeApp({
          'messagingSenderId': '발신자ID' //이곳은 자신의 프로젝트 설정 => 클라우드 메세징 => 발신자ID를 기입
      });
        
      const messaging = firebase.messaging();
    
    • 발신자 ID를 입력해야하는데 Firebase console화면 프로젝트 설정 클라우드 메세징 탭에서 확인할 수 있습니다. 저와는 다를 것이므로 확인 후 입력해주세요.

    16-2

    16-3

    • 그 다음 코드는 푸시메세지를 수신했을 때 동작입니다. 데이터를 수신하고 알림 화면을 띄우게 됩니다.
      self.addEventListener('push', function(event) {
          const payload = event.data.json();
          const title = payload.notification.title;
          const options = {
              body: payload.notification.body,
              icon: payload.notification.icon,
              data: payload.notification.click_action
          };
        
          event.waitUntil(self.registration.showNotification(title, options));
      });
    
    
    • 다음 코드는 알림 화면을 클릭했을 때 입니다. 수신했을 때 options 객체에 할당한 data 값에 할당된 URL로 이동하게 됩니다. Functions코드에 payload click_action 데이터에 http://프로젝트호스팅주소=${roomId} 데이터를 입력하였는데 이 URL로 이동하게 될 것입니다. 그리고 URL 파라미터로 로그인 이후에 채팅방을 오픈하게 됩니다.
      self.addEventListener('notificationclick', function(event) {
          event.notification.close();
          event.waitUntil(
              clients.openWindow(event.notification.data)
          );
      });
    
    • Service worker 코드는 완성되었고, 기본적으로 mainfest.json 을 만들고, firebase-messaging-sw.js 명칭으로 Service worker파일을 만들면 웹앱이 구동될 때 브라우저에 Service worker가 설치가 됩니다. 하지만 개인적인 경험으로 수정된 코드가 반영이 잘되지 않아 명시적으로 화면이 로드될 때마다 코드를 입력
  • index.html파일을 열어 아래의 코드를 추가합니다.
/**
   * Dom 로딩 후 동작
   */
  document.addEventListener('DOMContentLoaded', function() {
      //...생략

      if(navigator.serviceWorker){
          navigator.serviceWorker.register('/firebase-messaging-sw.js')
              .then(function(reg){console.log('서비스워커 등록성공 :', reg)})
              .catch(function(error){console.log('서비스워커 등록실패 :', error)});
      }
  });
  • 수신 작업은 완료되었습니다. 한가지가 남았는데, 알림을 클릭하고 로그인 시에 채팅방으로 바로 진입하는 코드를 작성합니다.
  • 코드는 loadRoomList 메소드를 통해 채팅방 목록탭을 그리는 메소드인 getRoomList 메소드에 코드를 추가
          /**
           * loadRoomList 에서 데이터를 받아왔을 때
           */
          FirebaseChat.prototype.getRoomList = function(snapshot){
              var arrRoomListHtml = [];
              var cbDisplayRoomList = function(data){
              //...생략   

              // 채팅방을 로드 후 url에 roomId파라미터가 있으면 채팅방을 클릭한다.
              var paramData = FirebaseChat.getParam('roomId');
              if(paramData && paramData.length > 0){
                  document.getElementById('liRoom' + paramData).click();
                  history.replaceState('','', '/');
              }
            }
          }
          /**
           * 파라미터 값을 확인
           */
          FirebaseChat.getParam = function (name){
              var result = "";
              var queryString = window.location.search;
              var paramMap = {}
              if (queryString == "") result = undefined;
              if (typeof result != "undefined"){
                  var params = queryString.split("?")[1];

                  if (params == "") result = undefined;

                  if (typeof result != "undefined") {
                      var paramObj = params.split("&");
                      for (var i=0; i<paramObj.length; i++){
                          var datas = paramObj[i].split("=");
                          paramMap[datas[0]] = datas[1];
                      }
                      result = paramMap[name];
                  }
              }

              return result;
          }
  • getParam 메소드는 URL에 포함된 파라미터 값을 확인하는 메소드입니다. roomId를 확인하는 용도입니다.채팅방 로드가 완료된 후 URL에서 roomId값을 확인하고 채팅방목록에서 roomId 값을 가진 채팅방을 클릭하면서 채팅방을 띄우고, history.replaceState 메소드로 화면갱신없이 URL을 초기화 해줍니다.

  • 실제 푸시 메세지 보내고 받는 것을 해보기 위해서는 서버로 배포가 필요합니다. Functions 코드가 서버에서 구동되어져야하기 때문입니다. 로컬에서 호스팅을 실행하는 firebase serve 명령어의 옵션으로 Functions코드도 같이 구동할 수 있으나 이는 Functions중에서도 Hosting 트리거인 onRequest만 가능합니다. 그러므로 Firebase deploy명령어로 한 번 배포한 뒤 실행해보세요.

  • 챕터완성소스 : 16. Cloud Messaging과 Functions을 이용한 푸시메세지 기능 - Service worker를 이용한 FCM수신