Firebase_친구_기능_(3)_ _친구_요청_FCM - boostcampwm-2024/and04-Nature-Album GitHub Wiki

✅ 진행한 기능

  • Firebase Cloud Functions 초기 설정
    • Firebase 프로젝트에 Cloud Functions 활성화
    • Firebase CLI를 사용하여 Firebase Functions 프로젝트 초기화
    • Firebase Admin SDK 설정 및 초기화
  • Firebase Cloud Messaging(FCM) 설정
    • Firebase 프로젝트에 FCM 설정
    • Android 앱에서 FCM 토큰 받기 및 Firestore에 저장
    • FirebaseMessagingService를 사용하여 알림 수신 처리
  • FCM 토큰 저장
    • FirebaseMessaging.getInstance().getToken()을 사용하여 FCM 토큰 받기
    • Firebase Firestore에 사용자의 FCM 토큰 저장
  • Firestore 트리거 설정
    • Firestore 문서 생성 시 트리거가 작동하도록 설정
    • onDocumentCreated 트리거를 사용하여 친구 요청 문서가 생성될 때 알림 발송
  • 친구 요청 알림 구현
    • Firestore의 친구 요청 문서가 "RECEIVED" 상태일 때만 알림 발송
    • FCM 토큰을 이용하여 특정 사용자에게 푸시 알림 전송
    • 백그라운드, 포그라운드, 앱 종료 시 모두 푸시 알림 전송 확인 완료

💡문제 해결 과정 기록

https://firebase.google.com/docs/cloud-messaging/android/client?hl=ko&_gl=1*1wwcejw*_up*MQ

앱의 매니페스트에 다음을 추가합니다. FirebaseMessagingService를 확장하는 서비스를 추가합니다. 이 서비스는 백그라운드에서 앱의 알림을 수신하는 것 외에 다른 방식으로 메시지를 처리하려는 경우에 필요합니다. 포그라운드 앱의 알림 수신, 데이터 페이로드 수신, 업스트림 메시지 전송 등을 수행하려면 이 서비스를 확장해야 합니다.

<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>AndroidManifest.xml
  • 사용자에게 포그라운드에서도 알림 볼 수 있게 하기 위해서 추가하도록 하자.

Android 13 이상에서 런타임 알림 권한 요청?

Android 13에는 알림 표시를 위한 새로운 런타임 권한이 도입되었습니다. 이러한 새로운 런타임 권한은 FCM 알림을 사용하고 Android 13 이상에서 실행되는 모든 앱에 영향을 미칩니다. 기본적으로 FCM SDK(버전 23.0.6 이상)에는 매니페스트에 정의된 [POST_NOTIFICATIONS](https://developer.android.com/reference/android/Manifest.permission?hl=ko#POST_NOTIFICATIONS) 권한이 포함되어 있습니다. 그러나 앱에서 상수 android.permission.POST_NOTIFICATIONS를 통해 이 권한의 런타임 버전도 요청해야 합니다. 사용자가 이 권한을 부여할 때까지 앱에서 알림을 표시할 수 없습니다. 새 런타임 권한을 요청하려면 다음 안내를 따르세요. 일반적으로 앱에서 알림을 게시하도록 권한을 부여하면 사용 설정되는 기능을 사용자에게 설명하는 UI를 표시해야 합니다. 이 UI는 확인 및 아니요 버튼과 같이 사용자가 동의하거나 거부할 수 있는 옵션을 제공해야 합니다. 사용자가 확인을 선택하면 권한을 직접 요청합니다. 사용자가 아니요를 선택하면 알림 없이 계속 진행할 수 있도록 합니다. 앱이 사용자에게 POST_NOTIFICATIONS 권한을 요청해야 할 때 적용되는 권장사항을 자세히 알아보려면 [알림 런타임 권한](https://developer.android.com/about/versions/13/changes/notification-permission?hl=ko#new-apps)을 참조하세요. https://developer.android.com/develop/ui/views/notifications/notification-permission?hl=ko#new-apps

FCM으로 Push 알림 구현을 위해서는 Server가 필요하다!

https://maejing.tistory.com/52

image

https://zuminternet.github.io/FCM-PUSH/

https://hstory0208.tistory.com/entry/Spring-Firebase-Cloud-MessagingFCM%EC%97%90-%ED%91%B8%EC%8B%9C-%EC%95%8C%EB%A6%BC-%EC%A0%84%EC%86%A1-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84

  • 여러 자료들을 살펴보았을 때 FCM 기본 동작을 위해서는 Server가 필요하다는 것을 알게 되었다.

❓ FCM을 위해서 서버가 필요하다는데 어떻게 해결하지?

  • FCM으로 알림을 보내려면 백엔드 서버가 필요하다고 한다. 그러나 우리 팀은 Android 개발을 위해 달려오고 Android 개발을 하는 사람들로 모인 팀이다. 즉, 백엔드 개발자가 없고, 그렇다고 직접 백엔드를 구축하는 데에 시간을 쓰는 것은 무리라고 판단하여 Firebase를 통해 데이터를 관리하고 있었다.
  • FCM에서 서버 역할이 필요해지다니! 전에 Firebase Cloud Functions를 언급한 적이 있었다. 친구 기능을 만들게 되면서 여러 필터링 등을 서버 쪽에서 진행하면 좋을 것 같아서 찾아봤었던 것이었다. Android 개발을 메인으로 하고 있었기에 일단은 클라이언트 쪽에서 처리하는 방향으로 진행했었다. 그런데 이제 피할 수 없게 된 것이다.

💡Firebase Cloud Functions로 구현 결정!

https://firebase.google.com/docs/functions/use-cases?hl=ko

image

  • 위와 같은 형식으로 진행할 수 있다. 위의 문서를 따라서 Cloud Functions를 구축했다.

Node.js 설치 및 Firebase CLI Login으로 준비하기

https://firebase.google.com/docs/functions/get-started?hl=ko&gen=2nd

https://nodejs.org/en

image

  • 위와 같은 과정을 통해서 Firebase CLI Login을 완료했다.

❓ Firebase Cloud Functions 개발 언어는 무엇으로?

  • Firebase Cloud Functions 개발 언어는 Python, JavaScript(Node.js), TypeScript를 지원한다.
  • 이 중 경험이 많은 것은 Python이라서 Python으로 진행할까 하다가 지금 node.js를 설치하기도 했고 기본적으로 JavaScript가 메인인 것 같아서 잠시 살펴봤다.

💡 JavaScript로 진행

  • 아니나 다를까 Python을 사용하여 Firebase Cloud Functions를 개발할 수 있지만, 추가 설정이 필요하다고 한다.
  1. Python SDK 설치:
  2. Firebase CLI 설정:
    • Firebase CLI는 기본적으로 Node.js 환경에서 동작하므로, Python 환경에서 사용하려면 추가 설정이 필요할 수도 있음
    • Python용 Cloud Functions를 배포하려면 Firebase CLI를 최신 버전으로 업데이트하고, Python SDK와의 호환성을 확인해야 함
  3. 환경 구성:
    • Python 환경에서의 종속성 관리, 가상 환경 설정 등 추가적인 환경 구성이 필요
    • 이는 JavaScript 환경보다 복잡할 수 있으므로, 개발 및 배포 과정에서 주의가 필요
  • 위와 같은 이유로 Python이 저 중 가장 익숙하지만 Firebase Cloud Functions를 효율적으로 사용하려면 JavaScript(Node.js)를 사용하는 것이 더 간편하고 안정적일 수 있다고 판단했다. 특히, Firebase의 공식 문서와 커뮤니티 지원이 JavaScript에 집중되어 있어, 학습 자료와 예제를 쉽게 찾을 수 있으므로 백엔드에 많은 시간을 쏟을 수 없는 나에게는 더 적합했다.

💡 FCM 토큰 저장 과정

❓친구 요청을 받은 사람에게 알림을 보내려면 어떻게?

FCM을 통해 알림을 보내기 위해서는 각 사용자에 대한 고유한 FCM 토큰이 필요하다. Firebase Cloud Messaging은 각 장치에 고유한 토큰을 할당하고, 이 토큰을 사용하여 메시지를 해당 장치로 전송한다.

❓ FCM 토큰을 어디에서 받아올까?

FCM 토큰은 사용자가 앱에 로그인할 때, Firebase에서 자동으로 발급한다. 이를 위해 FirebaseMessaging.getInstance().getToken() 메서드를 호출하여 토큰을 가져올 수 있다.

💡 Firebase Auth 로그인 시 FCM 토큰 생성 및 저장

  1. FCM 토큰 받기:
    • 앱에 로그인한 후 FirebaseMessaging.getInstance().getToken() 메서드를 호출하여 사용자의 고유 FCM 토큰을 받아온다.
    • 이 토큰은 앱을 설치한 장치에 따라 다르며, 토큰은 장치가 바뀌거나, 앱을 재설치하거나, 주기적으로 갱신될 수 있다.
  2. Firestore에 FCM 토큰 저장:
    • 받아온 FCM 토큰을 Firebase Firestore에 저장하여, 후속 요청에서 해당 토큰을 사용할 수 있도록 한다. Firebase에서는 이를 통해 알림을 받을 장치를 구분할 수 있다.

💥 Firebase에서 토큰을 저장하는 방법

private fun updateFcmToken(uid: String) {
        FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val token = task.result
                if (token != null) {
                    CoroutineScope(Dispatchers.IO).launch {
                        val success = fireBaseRepository.saveFcmToken(uid, token)
                        if (success) {
                            Log.d("FCM", "FCM token updated successfully via Repository")
                        } else {
                            Log.e("FCM", "Failed to update FCM token via Repository")
                        }
                    }
                } else {
                    Log.e("FCM", "Failed to fetch FCM token: ${task.exception?.message}")
                }
            }
        }
    }
  • 위와 같이 updateFcmToken을 만들어 login이 success될 때 호출이 되도록 했다.

💡 이렇게 구현한 이유

  • FCM 토큰 저장: 사용자의 기기에 고유한 FCM 토큰을 저장하여, 이후 특정 사용자에게 푸시 알림을 보낼 수 있도록 한다.

  • 자동 갱신 처리: FCM 토큰은 주기적으로 갱신되기 때문에, 토큰을 업데이트할 때마다 Firestore에 새로운 토큰을 저장하도록 처리한다.

  • 자세한 내용은 아래 링크 참고

    https://firebase.google.com/docs/cloud-messaging/manage-tokens?hl=ko&utm_source=chatgpt.com

    비활성 토큰이 270일 동안 활동이 없으면 FCM에서 만료된 토큰으로 간주합니다. 토큰이 만료되면 FCM는 토큰을 유효하지 않은 것으로 표시하고 토큰으로의 전송을 거부합니다. 하지만 FCM은 기기가 다시 연결되고 앱이 열리는 흔치 않은 경우에 앱 인스턴스의 새 토큰을 발급합니다.

  • 제대로 삽입되는 것 확인 ▼

image


  • 배포 관련하여 정말 많은 일이 있었는데 이에 관한 사항은 아래의 문서를 참고 바란다.

💡 Firebase Cloud Functions 초기 설정 및 FCM 테스트

❓ 문제 정의

Firebase Cloud Functions를 처음 설정할 때, **FCM(Firebase Cloud Messaging)**을 통해 푸시 알림을 정상적으로 발송할 수 있는지 테스트하기 위해, Firebase Cloud Functions의 트리거가 잘 작동하는지 확인해야 했다. 이를 위해 Firebase Topic 구독을 사용하여 푸시 알림을 발송할 수 있는지를 테스트했다.

💡 해결 과정

  1. Firebase Cloud Functions 초기 설정:
    • Firebase CLI를 통해 Firebase Cloud Functions 프로젝트를 설정하고, FirestoreFirebase Messaging을 연동하여 푸시 알림을 보낼 준비를 했다.
    • Firebase 프로젝트에서 Firebase Functions가 제대로 동작하는지 확인하기 위해, 간단한 Hello World 예제를 먼저 배포하여 기본적인 설정이 잘 되었는지 점검했다.
  2. Topic 구독을 이용한 FCM 테스트:
    • Firebase Cloud Functions의 트리거를 설정하여 Firestore의 문서가 생성될 때마다 특정 동작을 수행하도록 했다.
    • Firestore Document 생성 시 트리거가 작동하도록 설정한 후, Firebase Messaging Topic을 사용하여 FCM 메시지를 발송했다.
    • 이를 통해 Firebase Cloud Functions가 특정 토픽을 구독한 장치에 알림을 정상적으로 발송하는지 확인했다.
  3. Firebase Topic 구독 및 발행:
    • Firebase에서 Topic을 생성하여 구독한 후, Cloud Functions에서 해당 토픽으로 푸시 알림을 발송하도록 설정했다.
    • FirebaseMessaging.getInstance().subscribeToTopic("test-topic")을 사용하여 Android 앱이 특정 토픽을 구독하게 했고, 그 후 firebase.functions().send() 등을 사용하여 알림을 발송했다.
  4. FCM 푸시 알림 발송 확인:
    • Firebase Functions의 트리거가 정상적으로 작동하면, 구독된 모든 장치에 푸시 알림을 발송할 수 있다.
    • 알림 제목본문을 포함한 메시지를 발송하여 Android 앱에서 푸시 알림이 제대로 수신되는지 확인했다.

💡 예시 코드 (FCM Topic 구독 및 메시지 발송)

const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const admin = require("firebase-admin");
admin.initializeApp();

// Firestore 트리거: 새 문서 생성 이벤트
exports.notifyOnDocumentCreate = onDocumentCreated("TEST/{documentId}", async (event) => {
    const snapshot = event.data;
    
    // 문서 데이터 가져오기
    if (!snapshot) {
      console.log("No data found in the document.");
      return;
    }
 
    const data = snapshot.data();
    console.log("New document created in TEST collection:", data);
  
    const message = {
        notification: {
            title: "Firestore 새 문서",
            body: `새 데이터: ${data.message || "내용 없음"}`,
        },
        data: {
            additionalInfo: "추가 데이터",
        },
        topic: "test-topic",
    };
    
    try {
      const response = await admin.messaging().send(message);
      console.log("FCM 메시지 전송 성공:", response);
    } catch (error) {
      console.error("FCM 메시지 전송 실패:", error);
    }
  });

✅ 결과 확인

image

  • Firebase Functions가 제대로 설정된 후, Topic 구독을 통해 푸시 알림이 성공적으로 발송되는 것을 확인했다.
  • FCM을 통해 친구 요청 알림이 정상적으로 푸시 알림으로 수신되는지 확인하며, Cloud Functions가 Firestore 트리거를 통해 알림을 보내는 로직이 올바르게 작동함을 확인했다.

💡 결론

  • Firebase Cloud FunctionsFCM을 연동하여 푸시 알림을 구현할 수 있었다.
  • Topic 구독을 사용하여 특정 사용자 그룹에 대해 알림을 발송하는 방법을 사용했으며, Firestore 트리거를 통해 알림을 자동으로 발송하는 방식으로 구현했다.

참고: 백그라운드 알림 확인

  • 백그라운드에서의 알림 동작은 로그를 통해 확인했으며, 앱이 종료된 상태에서 알림이 잘 수신되었는지 확인하였다.
  • 로그로 확인된 푸시 알림 이미지 및 동작은 정상적으로 이루어졌음을 확인한 상태다.

  • 아래의 내용을 확인할 수 있으므로 참고 바란다.

💡 FCM 알림에서 Notification과 Data의 차이

💡 Topic 구독 vs FCM 토큰을 사용한 알림 발송


💡 친구 요청 알림을 위한 Firebase Cloud Functions 설정

❓ 문제 정의

친구 요청이 Firestore에 추가되었을 때, 해당 요청을 받은 사용자가 FCM을 통해 알림을 받을 수 있도록 하려는 기능이다. 이 기능은 Cloud Functions를 사용하여 Firestore에서 친구 요청 문서가 생성되었을 때, 해당 사용자의 FCM 토큰을 통해 푸시 알림을 보내는 방식으로 구현된다.

💡 해결 과정

  1. Firestore 트리거 설정:
    • onDocumentCreated 트리거를 사용하여 USER/{uid}/FRIEND_REQUESTS/{requestUid} 경로에 새로운 문서가 생성되면 함수가 실행되도록 설정했다.
    • 친구 요청 문서가 생성되면 이를 처리하는 함수가 실행되며, 요청 상태가 "RECEIVED"일 때만 알림을 보내도록 조건을 추가했다.
  2. FCM 토큰을 통해 푸시 알림 전송:
    • 친구 요청을 받은 사용자의 FCM 토큰을 Firestore에서 조회하여, 해당 사용자에게 푸시 알림을 보내도록 설정했다.
    • FirebaseMessaging.send()를 사용하여 푸시 알림을 발송했다.

💡 코드 구현

const functions = require("firebase-functions");
const admin = require("firebase-admin");

// Firebase Admin 초기화
admin.initializeApp();

// 친구 요청 문서 생성 시 트리거
exports.onFriendRequestCreated = functions.firestore
  .document("USER/{uid}/FRIEND_REQUESTS/{requestUid}")
  .onCreate(async (snapshot, context) => {
    const newRequest = snapshot.data(); // 새로 생성된 문서 데이터
    const uid = context.params.uid; // 요청을 받은 사용자 ID
    const requestUid = context.params.requestUid; // 요청 문서 ID

    // 데이터 확인
    if (!newRequest) {
      console.log("No snapshot available.");
      return;
    }

    // 요청 상태가 "RECEIVED"인 경우만 처리
    if (newRequest?.status === "RECEIVED") {
      console.log(`Friend request RECEIVED by user: ${uid}`);

      try {
        // Firestore에서 요청 받은 사용자의 FCM 토큰 가져오기
        const userDoc = await admin.firestore().collection("USER").doc(uid).get();
        const userData = userDoc.data();
        const fcmToken = userData?.fcmToken;

        if (!fcmToken) {
          console.log(`No FCM token found for user: ${uid}`);
          return;
        }

        // 요청을 보낸 사용자 정보 가져오기
        const sender = newRequest.user; // 요청 보낸 사용자 정보
        const senderDisplayName = sender?.displayName || "알 수 없는 사용자";

        // FCM 알림 메시지 생성
        const message = {
          token: fcmToken,
          notification: {
            title: "새로운 친구 요청",
            body: `${senderDisplayName}님으로부터 친구 요청이 도착했습니다.`,
          },
        };

        // FCM 메시지 전송
        await admin.messaging().send(message);
        console.log(`Notification sent to user: ${uid}`);
      } catch (error) {
        console.error("Error sending notification:", error);
      }
    } else {
      console.log("Friend request not in RECEIVED state, skipping notification.");
    }
  });

💡 핵심 동작

  1. Firestore Document 생성 시 트리거:
    • onCreate()를 사용하여 USER/{uid}/FRIEND_REQUESTS/{requestUid} 경로에서 문서가 생성될 때마다 트리거가 실행된다.
  2. FCM 토큰 확인 및 알림 전송:
    • 요청을 받은 사용자(uid)의 FCM 토큰을 Firestore에서 가져와 푸시 알림을 전송한다.
    • 요청을 보낸 사용자의 정보를 notification 메시지에 포함시켜 알림을 구성한다.
  3. 상태 체크:
    • 친구 요청 상태가 "RECEIVED"일 때만 알림을 보내고, 그 외의 상태에서는 알림을 생략한다.

✅ 결과 확인

  • Firestore에 친구 요청이 추가되면, 해당 요청을 받은 사용자에게 FCM 푸시 알림이 정상적으로 발송된다.
  • FCM 토큰이 없거나 잘못된 경우 알림은 발송되지 않으며, 이를 로그로 확인할 수 있다.

💡 결론

  • Firebase Cloud Functions와 Firebase Cloud Messaging(FCM)을 연동하여, 친구 요청이 발생할 때 자동으로 푸시 알림을 보내는 기능을 구현할 수 있었다.
  • Firestore 트리거와 FCM을 활용하여, 친구 요청을 받은 사용자에게 자동으로 알림을 전송하는 방식으로 구현했다.
  • RECEIVED 상태의 친구 요청만 알림을 보내도록 조건을 추가하여, 불필요한 알림을 방지할 수 있었다.

더미 데이터 확인 영상

FCM.mp4

포그라운드 백그라운드

FCM.mp4

종료 후에도 알림 확인 영상

FCM.mp4
⚠️ **GitHub.com Fallback** ⚠️