개발

GCP Cloud Function으로 FCM 메시지 보내기 + Pub/Sub

mycloudy 2021. 9. 29. 18:02

GCP Cloud Function과 Pub/Sub을 연동해서 FCM (Firebase Cloud Messaging) 사용하는 방법을 기록으로 남깁니다

 

대용량 FCM 메시지 보내기 위해 PubSub을 GCP로 구현한 두번째 내용입니다

이전 글에서는 GCP Pub/Sub 만드는 내용을 다루었고 이번 내용은 GCP Cloud Function을 구현하는 내용입니다

 

모든 코드는 github에서 확인할 수 있습니다

github에 테스트 코드도 함께 작성해놓았습니다 본문에는 따로 기재를 하지 않았지만 함께 확인하시면 좋습니다

1. Pub/Sub과 연동된 Cloud Function 생성

  1. 함수 이름과 GCP Pub/Sub의 topic을 선택하고 NEXT를 선택합니다

2. Cloud Function 언어 선택

  1. .NET, Go, Java, Node, Python, PHP, Ruby 가 가능합니다
  2. 저는 개인적으로 가볍고 이식성이 좋은 언어로 AWS Lambda나 GCP Cloud Function을 짜는 것을 좋아합니다 그래서 Python, Node를 선호하고 이번에는 Python으로 선택했습니다

3. 파이썬 프로젝트를 생성하고 firebase admin sdk를 설치합니다

# initialize project
pyenv local 3.9.6   # python 3.9.6 버전으로 작업
python3 -m venv venv  # virtual environment 생성
source venv/bin/activate  # virtual environment 활성화
# install firebase admin package
pip install firebase-admin

4. firebase service account를 다운로드 받습니다

  1. GCP에서 인증하는 방식은 여러가지 입니다
  2. 저는 그 중에서 service account 키 파일을 이용해서 인증을 했습니다
  3. Firebase Console > 프로젝트 설정 > 서비스 계정 > 새 비공개 키 생성
  4. code snippet 입니다
# main.py
# code snippet

import firebase_admin
from firebase_admin import credentials

...
cred = credentials.Certificate("./pointberry-292606-firebase-adminsdk.json")
firebase_admin.initialize_app(cred)
....

5. main.py 코드 작성

  1. entrypoint 함수의 첫번째 파라미터 event의 data 필드에는 base64로 인코딩된 PubSub Message가 있습니다
  2. event를 디코딩한 뒤에 메시지 내용을 추출합니다
# main.py

import base64
import json
import logging
from enum import Enum
from typing import Optional

import firebase_admin
from firebase_admin import credentials
from firebase_admin import messaging


# https://cloud.google.com/functions/docs/calling/pubsub
def entrypoint(event: dict, context) -> str:
    FIREBASE_ACCOUNT_SERVICE = "./sample-project-5d15z-firebase-adminsdk-gabge-4fa79ee667.json"
    try:
        cred = credentials.Certificate(FIREBASE_ACCOUNT_SERVICE)
        firebase_admin.initialize_app(cred)
    except ValueError:  # The default Firebase app already exists. This means you called initialize_app() more than once
        pass

    logging.info("event: ", event)
    print("event:", event)
    data = json.loads(base64.b64decode(event['data']).decode('utf-8'))

    push_type: str = data['pushType']
    if push_type != PushType.SINGLE_MESSAGE.name:
        print("Not supported message type", push_type)

    device_token = data['deviceToken']
    title: str = data['title']
    body: str = data['body']
    metadata: dict = data.get('data', None)

    return send_one_data(device_token, title, body, metadata)


def send_one_data(device_token: str, title: str, body: str, data: Optional[dict]) -> str:
    message = messaging.Message(
        notification=messaging.Notification(title=title, body=body),
        data=data,
        token=device_token,
    )

    try:
        response = messaging.send(message)
        print("successfully Sent message:", response)
        return f"successfully Sent message: {response}"
    except firebase_admin._messaging_utils.UnregisteredError:
        print(f"No such deviceToken: {device_token}")
        return f"No such deviceToken: {device_token}"
    except firebase_admin.exceptions.InvalidArgumentError:
        return f"The registration token is not a valid FCM registration token: ${device_token}"


class PushType(Enum):
    SINGLE_MESSAGE = 1
    MULTIPLE_MESSAGE = 2

6. requirements.txt 작성

pip freeze > requirements.txt

생성된 requirements.txt 파일입니다

# requirements.txt

CacheControl==0.12.6
cachetools==4.2.2
certifi==2021.5.30
cffi==1.14.6
charset-normalizer==2.0.4
firebase-admin==5.0.1
google-api-core==1.31.1
google-api-python-client==2.15.0
google-auth==1.34.0
google-auth-httplib2==0.1.0
google-cloud-core==1.7.2
google-cloud-firestore==2.2.0
google-cloud-storage==1.41.1
google-crc32c==1.1.2
google-resumable-media==1.3.3
googleapis-common-protos==1.53.0
grpcio==1.39.0
httplib2==0.19.1
idna==3.2
msgpack==1.0.2
packaging==21.0
proto-plus==1.19.0
protobuf==3.17.3
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
pyparsing==2.4.7
pytz==2021.1
requests==2.26.0
rsa==4.7.2
six==1.16.0
uritemplate==3.0.1
urllib3==1.26.6

7. Cloud Function을 설정합니다

  1. Entry point는 entrypoint 로 함수로 설정합니다
  2. main.py는 위에서 작성한 main.py 를 복붙합니다
  3. requirements.txt도 requirements.txt 파일을 복붙합니다
  4. sample-project-5d15z-firebase-adminsdk-gabge-4fa79ee667.json 을 생성합니다
  • [과정4]에서 다운로드 받은 service account key 파일을 root 경로에 복사합니다
  • main.py 코드에서 동일한 디렉토리에 key 파일을 검색하기 때문에 root 경로에 파일을 만들어줍니다

8. Deploy 버튼을 눌러서 배포합니다

Refer