FCM 2: Functions 설정 그리고 오류

2025. 12. 31. 12:37
반응형

이전에 어플의 출석 기능을 위해서 랜덤 토큰 생성을 Functions로 하기 위해서 도전을 해봤었다. 그런데 internal 오류가 계속 날아와서 이를 해결하지 못하고 functions가 아닌 다른 방식을 선택해서 구현했었다.

그런데 이번에할 자동 앱을 이용한 FCM을 하기 위해서는 필연적으로 Functions를 이용해야만 일반 사용자가 FCM을 이용해 공지나 알림을 띄울 수 있다. 그래서 이번에는 포기하지 않고 다시 도전해 보기로 한다.

 

먼저 Functions가 뭔지 간단하게 알아보자.

Cloud Functions for Firebase

 

Cloud Functions for Firebase

Firebase용 Cloud Functions는 Firebase 기능과 HTTPS 요청에 의해 트리거되는 이벤트에 응답하여 백엔드 코드를 자동으로 실행할 수 있는 서버리스 프레임워크입니다.

firebase.google.com

공식 사이트의 설명을 보면 HTTPS 요청을 통해서 백엔드 코드를 자동으로 실행할 수 있는 서버리스 프레임 워크라고 나와있습니다. 간단하게 말하면 백엔드에서의 함수를 실행하게 해주는 서비스입니다.

서버 즉 백엔드에서도 함수를 이용하는 경우가 무척 많습니다. 백엔드에 해야만 보안, 서비스 등의 면에서 더욱 신뢰성이 올라가는 기능들이 있는데 서버의 기능을 대체하는 Firebase에서 백엔드 코드를 대체할 수 있는데 Functions인 겁니다.

 


functions 설정은 다른 곳에서도 찾아보기 어렵지 않기 때문에 여기서는 다른 분의 블로그 첨부만 해놓겠습니다.

Firebase Functions Setting

[Flutter] Firebase Cloud Functions 사용해 보기

 

[Flutter] Firebase Cloud Functions 사용해 보기

Firebase Cloud Functions 사용해 보기 Firebase 세팅하기 Firebase Authentication 사용해보기 Firebase Storage 사용해보기 Firebase Firestore Database 이번 글에서는 Firebase의 기능인

velog.io

 

 

오류

Functions로 앱에서 Notifications를 전송하기 위해서 아래의 코드를 짜보았습니다.

exports.sendMassNotification = functions.https.onCall(async (data, context) => {
    const allowedRoles = ["admin", "teacher"];
    const userRole = context.auth.token.role;
    if (!userRole || !allowedRoles.includes(userRole)) {
        throw new functions.https.HttpsError("permission-denied", "관리자 또는 교사만 대량 알림을 보낼 수 있습니다.", );
    }
    const title = data.title;
    const body = data.body;
    if (!title || !body) {
        throw new functions.https.HttpsError("Error', '제목과 내용을 모두 입력해야 합니다.");
    }
    try {
        const message = {
            notification: {
                title: title,
                body: body,
            },
            topic: "allUsers",
        };
        const response = await admin.messaging().send(message);
        return {
            success: true,
            message: 총 $ {
                response.successCount
            }
            개의 알림이 성공적으로 전송되었습니다.,
        };
    } catch (error) {
        console.error("대량 알림 전송 실패:", error);
        throw new functions.https.HttpsError("internal", "대량 알림 전송 중 오류 발생", );
    }
});

 

함수는 아래의 조건을 포함하고 있습니다.

  • 보내는 사람의 계정이 교사와 관리자 계정이 여야 한다.
  • 제목과 내용 둘 다 비어있으면 안 된다.
  • 알림을 받을 사람(topic)은 모든 유저이다.

위의 내용으로 함수를 만들고  "firebase deploy --only functions" 명령어로 배포를 했습니다. 그리고 실행을 해보니 아래의 오류가 발생했습니다.

[firebase_functions/internal] INTERNAL

그래서 Google Cloud Console로 들어가서 정확한 log를 확인해 봤습니다.

Unhandled error TypeError: Cannot read properties of undefined (reading 'token')

token이 읽히지 않는다고 뜨고 있었습니다. 저 요청에서의 token은 firebase에서 관리하는 유저의 로그인 상태 토큰입니다. 로그인 상태라면 토큰이 있고 firebase가 자동으로 유저 토큰을 넘길 것이고 그러면 인식이 되어야 하는데 안 되고 있는 것이었습니다.

예전에 functions를 포기했을 때는 internal 오류가 발생한 것은 같았지만 firebase cloud console에서 말한 오류를 해결하지 못했었기 때문이었습니다. 하지만 이번에는 확실하게 token이 인식이 안된다를 알게 되었습니다.

  1. Token이 인식이 안되고 있기 때문에 가장 먼저 의심해 본 것은 로그인은 했지만 firebase가 인식을 못하고 token을 안 주고 있는 것인가? 였습니다. 하지만 토큰을 찍어서 확인해 본 결과 로그인 토큰은 잘 넘어오고 있는 것을 보아 토큰을 넘기긴 하고 있는 것 같았습니다.
  2. undefine token에서 정보를 잘 못 넘기고 있나? 이것도 token 속에 유저의 role이 있어야 하는 건데 알맞은 위치에 저장되어 있었고 그러면 cloud가 뭔가 잘 못 인식하고 있다가 가장 의심되었습니다.
  3. version의 차이
    1. 좀 더 찾아보자 functions의 version이 있었습니다 Cloud Functions 버전 비교  |  Cloud Functions for Firebase
    2. 버전은 신경 쓰지 않았었는데 functions문 저를 보니 버전 1의 문법으로 작성하고 있었고 여러 가지 기능적인 이점이 있고 오류 해결을 기대하면서 version2로 바꿔봤습니다.
 

Cloud Functions 버전 비교  |  Cloud Functions for Firebase

Cloud Functions의 두 버전을 비교하여 2세대의 새로운 기능을 강조합니다.

firebase.google.com

 

const {onCall, HttpsError} = require("firebase-functions/v2/https");
const functions = require("firebase-functions");
const admin = require("firebase-admin");

exports.sendMassNotification = onCall(async (request) => {
  const data = request.data;
  const auth = request.auth;

  // 인증 체크
  if (!auth || !auth.token) {
    throw new HttpsError("unauthenticated", "로그인이 필요합니다.");
  }

  // 역할 확인
  const allowedRoles = ["admin", "teacher"];
  const userRole = auth.token.role;

  if (!userRole || !allowedRoles.includes(userRole)) {
    throw new HttpsError("permission-denied", "권한이 없습니다.");
  }

  const title = data.title;
  const body = data.body;

  if (!title || !body) {
    throw new HttpsError("invalid-argument", "제목과 내용을 입력하세요.");
  }

  const msg = {
    notification: {title, body},
    topic: "allUsers",
  };

  await admin.messaging().send(msg);

  return {success: true};
});

보시면 oncall 함수를 v2 https 함수라고 지정을 했습니다. 그러면서 sendMassNotifications 함수가 달라지게 됩니다. 먼저 onCall함수를 바로 사용하면서 사용하는 인자가 data에서 request로 바뀌게 됩니다. 이전에는 data와 context 두 가지를 구분해서 전달해야 했지만 request하나가 되면서 firebase가 token을 바로 인식할 수 있게 되었습니다.

단순하게 지금 functions 함수의 문제가 해결되는 것뿐만 아니라 v2를 이용하면서 HTTP 요청, Firestore 이벤트, Storage 이벤트 등 다양한 트리거에 대한 인터페이스를 개선할 수 있습니다.

v1으로 하더라도 auth.token은 넘어가서 인식이 되어야하는데 왜 안되는지에 대해서는 아직도 잘 모르겠습니다만 그래도 v2로 바꿔서 해결을 했고 어차피 v2로 다른 함수도 사용할 수 있기 때문에 앞으로는 v2로 처음부터 사용하는게 좋을거 같습니다.

반응형

'Flutter > 성당 앱' 카테고리의 다른 글

FCM 1 : firebase_messaging setting  (0) 2025.12.18

BELATED ARTICLES

more