05
19

์ง„ํ–‰ ์ค‘์ธ ํ† ์ด ํ”„๋กœ์ ํŠธ์—์„œ ํ‘ธ์‹œ์•Œ๋ฆผ์„ ์‚ฌ์šฉํ•ด์•ผ๋๋‹ค. ์ด์ „์— Firebase ์‹ค์‹œ๊ฐ„ DB์— ํ† ํฐ์„ ์ €์žฅํ•ด์„œ ๊ทธ๊ฑธ ์ฝ์–ด๋“ค์—ฌ ํ‘ธ์‹œ์•Œ๋ฆผ์„ ๊ฐœ๊ฐœ์ธ์—๊ฒŒ ๋ณด๋‚ด๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ topic์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ๋๋‹ค.

https://firebase.google.com/docs/cloud-messaging/android/topic-messaging?hl=ko&authuser=0&_gl=1*1wkaaex*_up*MQ..*_ga*MTU1MjEzMDIyNi4xNzEzNTk0NjM3*_ga_CW55HF8NVT*MTcxNjEwNDkxOS4xNS4xLjE3MTYxMDcwMTguNTUuMC4w

 

Android์—์„œ ์ฃผ์ œ ๋ฉ”์‹œ์ง•  |  Firebase ํด๋ผ์šฐ๋“œ ๋ฉ”์‹œ์ง•

Google I/O 2023์—์„œ Firebase์˜ ์ฃผ์š” ์†Œ์‹์„ ํ™•์ธํ•˜์„ธ์š”. ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ ์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ Android์—์„œ ์ฃผ์ œ ๋ฉ”์‹œ์ง• ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. FCM

firebase.google.com


firebase cli ์„ธํŒ…ํ•˜๋Š” ๋ถ€๋ถ„์€ ๊ณต์‹๋ฌธ์„œ์— ์ž˜ ๋‚˜์™€์žˆ์œผ๋‹ˆ ๋„˜์–ด๊ฐ€๊ฒ ๋‹ค.

topic์„ ๊ธฐ์ค€์œผ๋กœ ๋ฉ”์‹œ์ง•ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•œ ์ด์œ ๋Š” ํšจ์œจ์„ฑ ๋•Œ๋ฌธ์ด๋‹ค. ๋ชจ์ž„๊ด€๋ฆฌ ์•ฑ์„ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ์ค‘์ด๊ณ , ์–ด๋–ค ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ์ž„์— ์ฐธ์—ฌํ•˜๊ฒŒ ๋˜๋ฉด ์ด๋ฏธ ๋ชจ์ž„์— ์ฐธ์—ฌ์ค‘์ด๋˜ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ƒˆ๋กœ์šด ์ฐธ์—ฌ์ž๊ฐ€ ์ƒ๊ฒผ๋‹ค๊ณ  ํ‘ธ์‹œ์•Œ๋ฆผ์œผ๋กœ ๋ณด๋‚ด๋„๋ก ํ•˜๋ คํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ฒ˜์Œ์—๋Š” ๋ชจ์ž„์— ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์˜ ํ† ํฐ์„ ๊ด€๋ฆฌํ•˜๋Š” collection์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ๊ทธ๊ฑธ๋กœ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ ํ† ํฐ์— fcm์„ ๋ณด๋‚ด๋ ค๊ณ  ํ•˜๋‹ค๊ฐ€ topic์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ํšจ์œจ์ ์ธ ๋ฉ”์‹œ์ง•์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์„ ์•Œ๊ฒŒ ๋๋‹ค. ๊ณต์‹๋ฌธ์„œ์— ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์™€์žˆ๋Š”๋ฐ,

ํ”ผ๋“œ๋ฐฑ์ด ๋นจ๋ผ์•ผํ•˜๋Š” ํ‘ธ์‹œ์•Œ๋ฆผ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฑธ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

function์€ v2๊ฐ€ ์•„๋‹ˆ๋ผ v1์„ ์‚ฌ์šฉํ–ˆ๋‹ค.(๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฒฝํ—˜๋„ ํ•ด๋ณด๊ธฐ ์œ„ํ•ด์„œ)

const functions = require('firebase-functions/v1');
const admin = require("firebase-admin");

admin.initializeApp();

exports.detectMeetingJoinWithFCM = functions.firestore
  .document("/DETAILMEETING/{documentId}")
  .onUpdate((change, context) => {
    const documentId = context.params.documentId;

    // ๋ฌธ์„œ๊ฐ€ ์‚ญ์ œ๋œ ๊ฒฝ์šฐ๋Š” ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ
    if (!change.after.exists) {
      functions.logger.log("Document deleted", documentId);
      return null;
    }

    const newValue = change.after.data();
    const previousValue = change.before.data();

    // ๋ฌธ์„œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ๊ฒฝ์šฐ
    if (newValue.participants !== previousValue?.participants) {
      const participants = newValue.participants;
      const meetingContent = newValue.meeting;
      functions.logger.log("Detected write", documentId, participants);
      
      const topic = documentId;
      const message = {
        notification: {
          title: `${meetingContent}`,
          body: `์ด์ œ ๋ชจ์ž„์— ์ฐธ๊ฐ€์ž๊ฐ€ ${participants.length}๋ช…์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค`
        },
        topic: topic
      };
      
      return admin.messaging().send(message)
      .then(response => {
        console.log('Successfully sent message:', response);
        return null;
      })
      .catch(error => {
        console.error('Error sending message:', error);
        return null;
      });
    }

	return null;
  });

 

์ „์ฒด ์ฝ”๋“œ๋‹ค. trigger ๋ถ€๋ถ„๊ณผ fcm ๋ถ€๋ถ„ ์ด๋ ‡๊ฒŒ ๋‘ ๊ฐœ๋กœ ๋‚˜๋ˆ ์„œ ์‚ดํŽด๋ณด์ž.

# Trigger

exports.detectMeetingJoinWithFCM = functions.firestore
  .document("/DETAILMEETING/{documentId}")
  .onUpdate((change, context) => {
    const documentId = context.params.documentId;

    // ๋ฌธ์„œ๊ฐ€ ์‚ญ์ œ๋œ ๊ฒฝ์šฐ๋Š” ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ
    if (!change.after.exists) {
      functions.logger.log("Document deleted", documentId);
      return null;
    }

    const newValue = change.after.data();
    const previousValue = change.before.data();

    // ๋ฌธ์„œ๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ ๊ฒฝ์šฐ
    if (newValue.participants !== previousValue?.participants) {
      const participants = newValue.participants;
      functions.logger.log("Detected write", documentId, participants);
    }

	return null;
  });

cloudfunction์€ ๋‚ด์žฅํ•จ์ˆ˜์—์„œ firestore๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธธ ๋•Œ http๋กœ ๋งŒ๋“ค์–ด์ง„ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ง์ ‘ firestore๋ฅผ ๋ฐ”๋ผ๋ณด๊ฒŒ ํ•ด์„œ ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค. ์ด๋•Œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ๊ฒŒ collection ์ด๋ฆ„์— ์˜คํƒ€๋ฅผ ๋‚ด์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค... ๋‚ด ๊ฒฝ์šฐ์—๋Š” _ ํ•˜๋‚˜๋ฅผ ์ž˜๋ชป ๋„ฃ์–ด์„œ 30๋ถ„ ๋™์•ˆ ์—๋Ÿฌ ์›์ธ์„ ์ฐพ๊ณ  ์žˆ์—ˆ๋‹ค.

firestore์•ˆ์— ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธด document๋ฅผ ์•Œ์•„์„œ ํƒ์ง€ํ•œ๋‹ค. ๊ทธ documentId๋ฅผ ํ† ํ”ฝ์œผ๋กœ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€์ˆ˜์— ์ €์žฅํ•ด์ฃผ๊ณ , ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธด ๋ถ€๋ถ„์€ change๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์„ฑํ•  ๋•Œ๋Š” ํ‘ธ์‹œ์•Œ๋ฆผ์„ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†์–ด์„œ onUpdate๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. onDelete๋Š” ํ˜„์žฌ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š๋Š”๋ฐ, ๋‚˜์ค‘์— ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

# FCM

const participants = newValue.participants;
const meetingContent = newValue.meeting;
const topic = documentId;
const message = {
notification: {
      title: `${meetingContent}`,
      body: `์ด์ œ ๋ชจ์ž„์— ์ฐธ๊ฐ€์ž๊ฐ€ ${participants.length}๋ช…์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค`
    },
    topic: topic
};

return admin.messaging().send(message)
    .then(response => {
    	console.log('Successfully sent message:', response);
    	return null;
	})
	.catch(error => {
		console.error('Error sending message:', error);
		return null;
	});

fcm์„ ๊ตฌ์„ฑํ•ด์„œ ์ „์†กํ•˜๋Š” ์ฝ”๋“œ๋‹ค. topic์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด trigger์—์„œ ๋ฐ›์•„์˜จ document์˜ id์ด๊ณ , notification์œผ๋กœ JSON ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์•ผํ•œ๋‹ค.

 

์ด์ œ ๋งŒ๋“ค์–ด์ง„ ํ•จ์ˆ˜๋ฅผ ๊ฐ–๊ณ  emulators:start๋กœ ํ…Œ์ŠคํŠธํ•œ๋‹ค์Œ deployํ•ด์ฃผ๋ฉด ๋๋‚œ๋‹ค...

 

๋‚œ ์ฒ˜์Œ์— cloudfunction์œผ๋กœ ๋‚ ๋ฆฐ fcm์€ data ๋ฉ”์‹œ์ง€๊ฐ€ ์•„๋‹ˆ๋ผ notification ์ผ์ค„ ์•Œ์•˜๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ›์•„๋ณด๋‹ˆ data ๋ฉ”์‹œ์ง€๋กœ ๋“ค์–ด์˜ค๊ณ  ์žˆ์–ด์„œ pending intent๋ฅผ ์‚ฌ์šฉํ•œ ์ œ์–ด๋„ ๊ฐ€๋Šฅํ–ˆ๋‹ค.

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!
๋ฐ˜์‘ํ˜•
COMMENT