31. Firebase Functions 프로젝트 PWA myBlog 개발 - FCM 보내기와 받기
FCM 보내기와 받기
myBlog 개발에 대한 마지막 글입니다.
FCM은 서버에서 보냅니다.
우리는 서버가 없습니다.
그래서 firebase의 functions를 사용하여 FCM 전송 기능을 구현합니다.
Firebase Functions란?
Firebase Functions는 서버 없이 백엔드 로직을 실행할 수 있는 클라우드 함수 서비스입니다.
- 서버 없이 Firebase와 다른 서비스들을 연결
- 이벤트 기반 실행 (Firestore, Auth, FCM, HTTP 요청 등)
- 자동 확장 (트래픽 증가 시 자동으로 처리)
Firebase Functions의 주요 기능
1. HTTP 요청을 처리하는 API 만들기
- REST API를 만들어 Vue, React 같은 프론트엔드에서 호출 가능
const functions = require("firebase-functions");
exports.helloWorld = functions.https.onRequest((req, res) => {
res.send("Hello from Firebase Functions!");
});
- 배포 후 실행:
https://us-central1-프로젝트ID.cloudfunctions.net/helloWorld
2. Firestore 이벤트 감지 (DB 트리거)
- Firestore에 문서가 추가/수정/삭제되면 자동 실행
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.newUser = functions.firestore
.document("users/{userId}")
.onCreate((snap, context) => {
const newUser = snap.data();
console.log("새로운 사용자:", newUser);
return null;
});
- Firestore에 users 컬렉션에 문서가 추가되면 자동 실행
3. Firebase Authentication 트리거
- 사용자가 회원가입하면 자동 실행
exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => {
console.log("새 사용자 가입:", user.email);
return null;
});
4. FCM 푸시 알림 보내기
- Firestore에 새 글이 등록되면 자동으로 푸시 알림 전송
exports.sendNotification = functions.firestore
.document("posts/{postId}")
.onCreate(async (snap, context) => {
const post = snap.data();
const message = {
notification: {
title: "새로운 글이 등록되었습니다!",
body: post.title,
},
topic: "allUsers",
};
await admin.messaging().send(message);
console.log("푸시 알림 전송 완료");
return null;
});
- Firestore에 posts 컬렉션에 문서가 추가되면 모든 사용자에게 푸시 알림
Firebase Functions 사용 방법
1. Firebase CLI 설치
npm install -g firebase-tools
2. Firebase 로그인
firebase login
3. 프로젝트 설정
firebase init functions
4. Firebase Functions 배포
firebase deploy --only functions
- 배포 후 Firebase 콘솔 → Functions에서 확인
myBlog 푸시 알림 보내기
Firebase functions
Firebase용 Cloud Functions라고도 알려진 Firebase Functions는
개발자가 서버를 관리하지 않고도 백엔드 코드를 작성하고 배포할 수 있도록 해주는
Firebase에서 제공하는 서버리스 백엔드 서비스입니다.
Firebase 함수는 이벤트 기반 입니다.
Firestore, Firebase 인증, Firebase 실시간 데이터베이스, FCM(Firebase 클라우드 메시징)과 같은 다양한 Firebase 서비스의 이벤트에 대한 응답으로 또는 HTTP 요청을 통해 함수가 트리거될 수 있습니다.
Firestore 데이터베이스의 변경사항(예: 문서 생성, 업데이트, 삭제)에 응답하여 기능을 트리거할 수 있습니다.
myBlog FCM 전송
myBlog는 새글이 등록되면 구독자에게 알림을 보냅니다.
이를 위하여 firebase functions에서 fcm을 전송하는 과정은 다음과 같습니다.
- firestore의 posts 컬렉션에 문서가 저장될 때 이벤트가 발생하면
- firebase functions에서 이 이벤트를 받아
- 저장한 문서의 저자에 대해 subscriptions 컬렉션에서 독자 목록을 가져옵니다.
- 독자 목록에 대해 fcmTokens에 저장된 토큰으로 FCM을 전송합니다
firebse functions
// functions/index.js
/**
* Import function triggers from their respective submodules:
*
* const {onCall} = require("firebase-functions/v2/https");
* const {onDocumentWritten} = require("firebase-functions/v2/firestore");
*
* See a full list of supported triggers at https://firebase.google.com/docs/functions
*/
const {onRequest} = require("firebase-functions/v2/https");
const logger = require("firebase-functions/logger");
// Firestore에 문서가 추가되면 자동 실행
const { onDocumentCreated } = require('firebase-functions/v2/firestore');
// Create and deploy your first functions
// https://firebase.google.com/docs/functions/get-started
// exports.helloWorld = onRequest((request, response) => {
// logger.info("Hello logs!", {structuredData: true});
// response.send("Hello from Firebase!");
// });
const admin = require('firebase-admin');
// Initialize Firebase Admin
admin.initializeApp();
// Reference to Firestore
const db = admin.firestore();
// 글을 저장하면 구독자에게 알림을 전송한다.
exports.sendNewPostNotification = onDocumentCreated('/posts/{postId}', async (event) => {
const snapshot = event.data;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// access a particular field as you would any JS property
const authorId = data.userId;
const title = data.title;
const content = data.content;
try {
// userId는 저자이다. 저자의 독자들을 가져와야 한다.
// subscriptions 컬렉션의 구조: userId: userId, authorId: authorId, createdAt: new Date(),
const readersSnapshot = await db.collection('subscriptions')
.where('authorId', '==', authorId) // Query for posts where 'authorId' matches
.get();
const readerIds = [];
readersSnapshot.forEach((doc) => {
readerIds.push(doc.data().userId);
});
// readerIds 에 userId가 있는 모든 독자에게 알림 전송
for (const userId of readerIds) {
// 각 회원의 토큰을 가져온다.
const tokenSnapshot = await admin.firestore().collection('fcmTokens').doc(userId).get();
// 토근이 있으면 알림을 전송한다.
if (tokenSnapshot.exists) {
// 한 회원이 여러 토큰을 가진다. PC, 모바일 등
const tokens = tokenSnapshot.data().tokens;
console.log('tokens: ', tokens);
// Token을 배열에 넣는다.
const fcmTokens = [];
tokens.forEach((token) => {
// doc.data() is never undefined for query doc snapshots
fcmTokens.push(token.token);
});
try {
const response = await admin.messaging().sendEachForMulticast({
tokens: fcmTokens,
notification: {
title: title,
body: content,
}
});
// Check the results of the notifications
response.responses.forEach((response, idx) => {
if (response.success) {
console.log(`Message sent successfully to token: ${fcmTokens[idx]}`);
} else {
console.error(`Failed to send message to token: ${fcmTokens[idx]}`, response.error);
}
});
//return res.status(200).send('Notification sent successfully');
} catch (error) {
console.error('Error sending multicast notifications:', error);
}
}
}
} catch (error) {
console.error('Error getting readers:', error);
//res.status(500).send('Error getting readers');
}
});
FCM 받기
구독 요청을 하고, 알림 요청까지 했다면 클라우드에서 보내는 FCM을 받을 수 있습니다.
앱이 백그라운드에 있을 때 메시지를 받는 것은 서버스 워커입니다.
앱이 포그라운드(foreground) 상태에 있을 때 Firebase Cloud Messaging(FCM)을 수신하려면 클라이언트 앱에서 메시지 리스너를 구현해야 합니다.
myBlog 개발은 여기까지 입니다.
다음 프로젝트로 vue와 firebase를 사용하여 실시간 채팅 앱을 개발해 보려고 합니다.
앱의 이름을 'VSignal'로 할까 합니다. ㅎㅎ
ChatGPT에게 물어가면서 Vue3로 개발합니다.
어떤 결과가 나올지 나도 기대가 됩니다.
고맙습니다.