지난 글에 이어 서비스 워커를 이용한 웹 푸시 서비스를 구현했습니다. 서비스 워커의 권한이 더 많은 탓인지 구현 과정에서 웹 워커에 비해 제한 사항이 많았습니다.
클라이언트(브라우저)에서 푸시 서비스를 구독하고 어플리케이션 서버에서는 메시지 데이터를 푸시 서비스로 전달하면 푸시 서비스에서 사용자 정보를 식별한 후 클라이언트로 전달합니다.
크롬, 파폭 등 브라우저마다 자체적인 푸시 서비스를 위한 서버를 제공합니다. 클라이언트는 각 브라우저의 푸시 서비스를 구독하는 방식이기에 직접 서버를 구축하거나 유지보수하는 부담이 없습니다.
파이어베이스 에 접속해 프로젝트를 생성합니다.
프로젝트 생성 후 웹 앱을 추가합니다.
프로젝트 설정 -> 클라우드 메시징 탭에서 인증서를 생성 합니다.
지난 웹 워커 글에서는 JavaScript Blob 파일을 동적으로 생성해 워커를 등록했지만 서비스 워커의 경우에는 불가능합니다. 같은 호스트 주소의 정적 파일만 등록 가능합니다.
서비스 워커 등록, 토큰 발급, 포그라운드 메시지 수신을 구현하는 코드입니다. 토큰을 얻기 위해서는 vapid가 필요한데, 이 값은 프로젝트설정 > 클라우드메시징 > 웹 구성의 웹푸시인증서 발급 에서 발급받을 수 있습니다.
import { initializeApp } from "//www.gstatic.com/firebasejs/9.15.0/firebase-app.js";
import { getAnalytics } from "//www.gstatic.com/firebasejs/9.15.0/firebase-analytics.js";
import { getMessaging, getToken, onMessage } from "//www.gstatic.com/firebasejs/9.15.0/firebase-messaging.js";
// 파이어베이스 프로젝트 생성후 받은 설정 정보를 붙여넣습니다.
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
...
};
// 서비스워커 등록
if ('serviceWorker' in window.navigator) {
window.navigator.serviceWorker.register('../firebase-messaging-sw.js', {type : 'module'})
.then(function(registration) {
console.log('Registration successful, scope is:', registration.scope);
initFirebase();
}).catch(function(err) {
console.log('Service worker registration failed, error:', err);
});
}
function initFirebase() {
const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);
const analytics = getAnalytics(app);
getToken(messaging, {
vapidKey: "",
})
.then((currentToken) => {
if (currentToken) {
console.log(currentToken);
} else {
console.log("No registration token available. Request permission to generate one.");
}
})
.catch((err) => {
console.log("An error occurred while retrieving token. ", err);
});
onMessage(messaging, (payload) => {
console.log("Message received. ", payload);
});
}
importScripts('https://www.gstatic.com/firebasejs/9.15.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.15.0/firebase-messaging-compat.js');
const firebaseConfig = {
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log(
"[firebase-messaging-sw.js] Received background message ",
payload
);
// Customize notification here
const notificationTitle = "Background Message Title";
const notificationOptions = {
body: payload,
icon: "/firebase-logo.png",
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
라이브러리 추가
implementation 'com.google.firebase:firebase-admin:7.1.1'
파이어베이스 프로젝트 생성 후 발급받은 인증서 파일을 프로젝트 내에 저장합니다.
spring:
cloud:
gcp:
fcm : gcp-webpush-key.json
파이어베이스 앱 설정
@Component
public class FCMInitializer {
@Value("${spring.cloud.gcp.fcm}")
private String googleApplicationCredentials;
@PostConstruct
public void initialize() throws IOException {
ClassPathResource resource = new ClassPathResource(googleApplicationCredentials);
try (InputStream is = resource.getInputStream()) {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(is))
.build();
if (FirebaseApp.getApps().isEmpty()) {
FirebaseApp.initializeApp(options);
}
}
}
}
@Service
public class FCMService {
public void sendCompletedMessage(String email) {
Message message = Message.builder()
.putData("title", "title")
.putData("content", "content")
.setToken("")
.build();
FirebaseMessaging.getInstance().sendAsync(message);
}
}
서버에서 푸시 알림을 보내면 아래와 같이 메시지를 받을 수 있습니다.
포어그라운드에서 수신시에는 로그를 찍는 것 외에는 별도의 작업을 구현하지 않았습니다.
백그라운드에서 수신시에는 아래와 같이 알림창이 나타납니다.
현재 상태가 백그라운드인지 아닌지를 판단하는 기준은 페이지가 보이는지 아닌지로 생각하시면 됩니다. 페이지가 열려있어도 다른 탭을 띄어놨다면 백그라운드 상태입니다. 하단의 코드와 함께 테스트해보면 쉽게 이해되실 겁니다.
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
console.log('포어그라운드 상태');
return;
}
if (document.visibilityState === 'hidden') {
console.log('백그라운드 상태');
}
});
추가로 데스크톱 기기에서 브라우저가 실행되고 있지 않으면 푸시 알림을 받을 수 없습니다. 웹 푸시 알림은 구독자가 온라인 상태이고 브라우저가 실행 중인 경우에만 전달할 수 있습니다.
웹사이트 방문자가 푸시 알림을 구독하면 고유한 ID가 할당됩니다. 그리고 그의 메타데이터(운영 체제, 브라우저, 국가, 도시, 언어)는 웹사이트 서버에 저장됩니다. 이 데이터는 대상 캠페인에 대한 구독자를 분류하는 데 사용됩니다.
프로젝트 설정 -> 통합 -> BigQuery 탭에서 Cloud Messagign 체크를 누르면 됩니다.
주의할 점은 기본 요금제로 사용했을 시 메시지가 쌓이지 않습니다. 반드시 Blaze 요금제를 사용해야 BigQuery와 연동되고 데이터가 쌓입니다. 유료 요금제를 선택해도 무료 요금제에서 제공하는 사용량만큼은 공제 된다고 하니 테스트용으로는 부담 없습니다.
정상적으로 연동이 되었다면 데이터셋과 테이블이 자동적으로 추가됩니다.
가끔 웹 사이트 내에서 실시간 알림(인기글, 상품정보)을 봤을때 어떻게 구현했는지 궁금했는데 찾아보니 대부분 웹푸시가 아니라 웹소켓으로 이었습니다. 유저가 따로 알림 설정을 켜야 하는 문제 때문이지 않을까 싶네요. 웹푸시는 유저가 사이트를 닫아도 알림을 받을 수 있다는 장점이 있지만 아마 대부분의 유저들은 알림을 귀찮아 하지 않을까 싶습니다. 광고 서비스에 적용하려면 먼저 적절한 타겟팅 설정이 필수로 보입니다.
https://nsinc.tistory.com/218
https://anywaydevlog.tistory.com/93#public%-Ffirebase-messaging-sw-js
https://developer.mozilla.org/ko/docs/Web/API/Service_Worker_API/Using_Service_Workers
https://stackoverflow.com/questions/40340076/firebase-notification-records-log-api
https://geundung.dev/114
https://velog.io/@idojustdo_it/Web-push%EB%9E%80
https://okky.kr/articles/648517