본문 바로가기
Programming/Backend

[Laravel] FCM을 이용해서 웹푸시 발송하기

by BitSense 2020. 6. 15.
반응형

https://picory.com/entry/Laravel-Web-Push-%EB%B0%9C%EC%86%A1%EA%B8%B0

얼마 전에 테스트 중이던 웹 푸시 소스가 사용자를 일정 수준으로 넘어서면서 인지, 아니면 원래 그런 건지 모르겠지만, 메시지는 전송했다는데, 테스트 하는 사람들은 받지 못했다고 하는 상황이 자주 연출이 되었다. 전송했다는데? 왜 안오지? 나는 잘되던데... 하다가 나도 받지 못하는 상황이 생기니... 이거 너무 불안정한데?? 싶어서 FCM을 사용하기로 하고 다시 정리를 해본다.

설치

composer require brozot/laravel-fcm

config/app.php 설정

'providers' => [
	// ...

	LaravelFCM\FCMServiceProvider::class,
]

'aliases' => [
	...
	'FCM'      => LaravelFCM\Facades\FCM::class,
	'FCMGroup' => LaravelFCM\Facades\FCMGroup::class, // Optional
]
php artisan vendor:publish --provider="LaravelFCM\FCMServiceProvider"

.env 설정

FCM_SERVER_KEY=my_secret_server_key
FCM_SENDER_ID=my_secret_sender_id

Firebase Console[링크] 에서 프로젝트를 생성 후 [프로젝트 개요] 옆의 톱니바퀴(Settings) > 클라우드 메시징 페이지에 서버 키와 발신자 ID 값을 복사해서 추가

컨트롤러 샘플 코드

use LaravelFCM\Message\OptionsBuilder;
use LaravelFCM\Message\PayloadDataBuilder;
use LaravelFCM\Message\PayloadNotificationBuilder;
use FCM;

$optionBuilder = new OptionsBuilder();
$optionBuilder->setTimeToLive(60*20);

$notificationBuilder = new PayloadNotificationBuilder('my title');
$notificationBuilder->setBody('Hello world')
				    ->setSound('default');

$dataBuilder = new PayloadDataBuilder();
$dataBuilder->addData(['a_data' => 'my_data']);

$option = $optionBuilder->build();
$notification = $notificationBuilder->build();
$data = $dataBuilder->build();

// You must change it to get your tokens
$tokens = MYDATABASE::pluck('fcm_token')->toArray();

$downstreamResponse = FCM::sendTo($tokens, $option, $notification, $data);

$downstreamResponse->numberSuccess();
$downstreamResponse->numberFailure();
$downstreamResponse->numberModification();

// return Array - you must remove all this tokens in your database
$downstreamResponse->tokensToDelete();

// return Array (key : oldToken, value : new token - you must change the token in your database)
$downstreamResponse->tokensToModify();

// return Array - you should try to resend the message to the tokens in the array
$downstreamResponse->tokensToRetry();

// return Array (key:token, value:error) - in production you should remove from your database the tokens present in this array
$downstreamResponse->tokensWithError();

웹 푸시용 디바이스 토큰 수집 방법

console.firebase > settings > 일반 > 내 앱 섹션 > 앱 추가 > 웹앱 추가 시 웹 js 샘플 코드(snippet) 확인

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.15.1/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.15.1/firebase-analytics.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "[APIKEY]",
    authDomain: "[AUTHDOMAIN]",
    databaseURL: "[DATABASEURL]",
    projectId: "[PRODUCTID]",
    storageBucket: "[STORAGEBUCKET]",
    messagingSenderId: "[SENDERID]",
    appId: "[APPID]",
    measurementId: "[MEASUREID]"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
  firebase.analytics();
</script>

설정 후에 사용자 브라우저(디바이스)에 메시지를 전송하기 위해 토큰을 수집하는 작업을 하기 위해 ServiceWorker를 등록하는 작업을 해야 한다. 그리고 토큰 수집을 위해서 VAPID 키가 필요한데, 해당 키는 console.firebase > Settings > 클라우드 메시징 > 웹 구성 > 웹 푸시 인증서 에서 키를 생성해서 획득한다.

// 메시지 객체 획득
const messaging = firebase.messaging();

// 웹 푸시 인증 (VAPID 키)
messaging.usePublicVapidKey("BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu");

// 현재 등록 토큰 검색
messaging.getToken().then((currentToken) => {
  if (currentToken) { // 토큰이 있으면 서버로 전송
    sendTokenToServer(currentToken);
    updateUIForPushEnabled(currentToken);
  } else {
    // Show permission request.
    console.log('No Instance ID token available. Request permission to generate one.');
    // Show permission UI.
    updateUIForPushPermissionRequired();
    setTokenSentToServer(false);
  }
}).catch((err) => {
  console.log('An error occurred while retrieving token. ', err);
  showToken('Error retrieving Instance ID token. ', err);
  setTokenSentToServer(false);
});


// 토큰 갱신 모니터링
messaging.onTokenRefresh(() => {
  messaging.getToken().then((refreshedToken) => {
    console.log('Token refreshed.');
    // Indicate that the new Instance ID token has not yet been sent to the
    // app server.
    setTokenSentToServer(false);
    // Send Instance ID token to app server.
    sendTokenToServer(refreshedToken);
    // ...
  }).catch((err) => {
    console.log('Unable to retrieve refreshed token ', err);
    showToken('Unable to retrieve refreshed token ', err);
  });
});

위 내용을 기준으로 메시지 전송을 위해서 Service Worker 용 소스를 정리한다.

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('sw.js')
      .then((registration) => {
          // 서버스 워커를 사용한다고 알림
          messaging.useServiceWorker(registration);

          // 이미 웹푸시를 승인한 회원의 경우 토큰이 바뀌었는지 확인
          messaging.onTokenRefresh(() => {
              getToken();
          });

          requestPermission();
      });
}

// 웹푸시 권한 요청
const requestPermission = () => {
    Notification.requestPermission()
      .then((permission) => {
          if (permission === 'granted') {
              // 웹푸시를 허용한 경우
              getToken();
          } else if (permission === 'denied') {
              // 웹푸시를 거절한 경우
          } else {
              console.log('push default');
          }
      });
};

const getToken = () => {
  // 토큰 수집
  messaging.getToken().then((currentToken) => {
    if (currentToken) {
      // 토큰이 있으면 추가 액션
    } else {
      // 없으면???
    }
  }).catch((err) => {
    console.log('An error occurred while retrieving token. ', err);
  });
}

참조 URL

https://developers.google.com/web/fundamentals/codelabs/push-notifications
https://firebase.google.com/docs/cloud-messaging/js/client?hl=ko

 

반응형