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