Firebase로 Serverless Push 알림 발송 구현하기

2020. 3. 30. 00:15Web

 

목차

  • 서론
  • Firebase 비공개 키 획득하기
  • Firebase-tools 설치하기
  • Firebase에 비공개 키 설정하기
  • Firebase 프로젝트 초기화 하기
  • 의존성 설치
  • 코드 작성
  • Firebase 함수에 환경변수 설정하기
  • Firebase 로컬에서 테스트하기
  • Firebase 배포하기
  • 부록

 

 

서론

이 게시글을 읽기 전에 이전 섹션 내용을 모두 숙지하셨거나 기본 베이스가 되어 있으셔야 합니다.

https://gmyankee.tistory.com/288

 

Nuxt + PWA + Firebase Cloud messaging 이젠 눈감고도 설치하죠?

목차 서론 Firebase Project 생성하기 Firebase 설정 값 획득하기 PWA 라이브러리 설치/설정 하기 Service Worker 작성하기 Token으로 알림 발송해보기 다중 Token으로 다수에게 알림 발송해보기 서론 Nuxt.js 를..

gmyankee.tistory.com

대충 Python3 코드로 requests를 이용해서 push 알림을 발송하는 코드를 클라이언트 측에서 구현을 했었는데,

이번에는 Production에서도 사용할 수 있고, 로컬 테스트 환경도 제공해주는 킹 갓 제네럴 엠페러스

Firebase Cloud Functions를 이용하여 발송해보도록 하죠.

 

 

Firebase Cloud functions는 AWS Lambda와 유사(동일?)한 역할을 합니다.

완전 관리형(구글 입장에서의 완전 관리) 서버로서 유저에게 서버 관리 입지와 입장을 없애서,

사용한 만큼(함수 호출수, 실행시간, CPU, Memory, 스케일링)등 사용한 비례만큼만 지불하면 됩니다.

 

 

기존 Google이 Lambda보다 먼저 출시하여 오랜 시간 서비스해온 Google App Engine이 있는데,

이 GAE 서비스는 프로젝트를 통째로 서비스하는 것이기에 Monolithic(모놀리식) 서비스에 적합하고

AWS Lambda나 Google Cloud Functions는 Micro Service에 더 적합합니다.

 

이러한 추세에서 더 증가된 게 요즘 많이들 사용하는 도커를 이용한 컨테이너 기반의 마이크로 서비스를 많이들 이용하며, 구글에서 자사 개발하여, 사용하고 있고 오픈소스로 공개한 쿠버 네티스가 각광받으면서, 오케스트레이션의 정점을 찍었는데, 사실 개인적으로는 진짜 막 일 방문자 10만 명도 안 되는 서비스에 도입하기엔 

Docker Swarm이 러닝 커브도 엄청 적고 컨테이너 배포 실행 측면 속도나 관리가 더 편한 것 같습니다.

 

 

이러한 실정을 고려한 각 클라우드 서비스 제공사들은

AWS의 ECS(Elastic Container Service), EKS(Elastic Container of Kubernets of Service)

Google의 Cloud run 등을 제공하고 있는데,

 

역시나 제 개인적인 견해로는 소중 규모 서비스에서 도입하려는 생각과 행위 자체가 팀원에게 부담이면서...

비용적인 측면을 고려하지 않은 것과... 오버 엔지니어링(Over Engineering)이라고 표현하고 싶습니다.

 

 

뭐 이러한 Container 기반의 마이크로 서비스들이 무조건 나쁘다는 건 아닙니다.

Lambda와 Cloud Functions, Google App Engine, Google Pub/Sub, AWS SNS 뭐 이러한 서비스들을 쪼개서 사용하는 게 훨씬 비용적으로 저렴하다는 측면입니다.

 

관리가 편하다면 쿠버 네티스를 무조건 사용하는 것도 나쁘지 않습니다.

다만.. AWS만 사용하셨다면 그게 불편하지만 GCP(Google Cloud Platform)을 사용해오셨다면 프로젝트 단위로 구분되기 때문에 아마 큰 불편함이 없었을 겁니다. 아마도요... 적어도 파이어 베이스에선..?

 

 

 

 

 

 

Firebase 비공개 키 획득하기

Firebase Console로 이동하셔서 프로젝트 개요 -> 톱니바퀴 -> 프로젝트 설정으로 이동합니다.

 

 

프로젝트 설정에서 서비스 계정 탭으로 이동하시면

 

 

 

새 비공개 키 생성이라는 게 보입니다. 저걸 눌러주시면!

 

모달 창이 하나 뜨는데  키 생성을 클릭하여 다운로드하여줍니다.

이제 이건 firebase 프로젝트 초기화 작업 후 필요로 합니다.

 

 

 

 

Firebase-tools 설치하기

npm install -g firebase-tools

firebase-tools를 설치하셨다면, 프로젝트를 생성합니다.

 

 

 

 

 

Firebase에 비공개 키 설정하기

아까 다운로드한 비공개 키를 아무 데나 박고 해당 위치와 파일명을 기억해주세요.

 

export GOOGLE_APPLICATION_CREDENTIALS="path/to/key.json"

이렇게 Linux에서 GOOGLE_APPLICATION_CREDENTIALS라는 환경변수에 비공개 키 파일명의 위치를 알려주면

알아서 인식합니다.

 

Window는

set GOOGLE_APPLICATION_CREDENTIALS=path\to\key.json

 

참고자료

 

로컬에서 함수 실행  |  Firebase

Firebase CLI에는 다음 함수 유형을 에뮬레이션할 수 있는 Cloud Functions 에뮬레이터가 포함되어 있습니다. HTTPS 함수 호출 가능 함수 Cloud Firestore 함수 함수를 프로덕션 단계로 배포하기 전에 로컬에서 테스트 실행이 가능합니다. 참고: 활성 세션 중에 변경한 코드는 에뮬레이터에서 자동으로 다시 로드됩니다. 코드를 트랜스파일(TypeScript, React)해야 하는 경우 에뮬레이터를 실행하기 전에 해야 합니다. Fir

firebase.google.com

 

 

 

 

 

Firebase 프로젝트 초기화 하기

firebase init

firebase 명령을 사용할 수 있게 되며, init 명령을 사용하여 프로젝트를 생성합니다.

firebase-cli

여기서 우리는 Functions를 사용할 것 이기에 방향키로 조작을 하여 Functions를 스페이스바로 선택 후

엔터로 진행합니다.

 

 

Functions를 선택하고 나면, project를 기존껄 쓸 건지 새로 불러올 건지 등등을 말하는데,

우린 이미 Firebase 프로젝트가 존재하기 때문에 Use an existing project에서 엔터를 입력합니다.

 

 

 

 

그다음에는 프로젝트가 출력되는데 또 엔터...

잘 못 고르셔도 어차피 firebase use 명령을 사용해서 다른 프로젝트로 변경할 수 있으니 걱정 마세요!

프로젝트명이 저처럼 노출되어도 걱정 마세요! 어차피 구글이 제공해주는 무료 DNS 사용하면

어차피 Firebase나 GAE 심지어는 DB도 다 프로젝트명이 노출되니까요! ㅋ__ㅋ

 

 

 

 

전 타입 스크립트의 매력을 못 느껴서 JS로 선택

 

 

 

 

ESLint를 사용해서 버그와 스타일 모두 잡을 거냐고 하는데 그냥 ESLint 쓸 거냐 그런 느낌이니 y

 

 

의존성을 지금 설치할 거니?! Y

 

 

 

이렇게 되면 프로젝트 초기화 작업이 완료됩니다.

 

프로젝트 구조: https://firebase.google.com/docs/functions/get-started?hl=ko#node.js-%EB%B0%8F-firebase-cli-%EC%84%A4%EC%A0%95

 

 

의존성 설치

푸시를 요청하기 위해 필요한 것은 토큰과, 요청 모듈입니다.

그런데 단일 유저에게 발송하거나 그냥 POST 데이터나, GET 쿼리로 던져도 되지만,

저는 DB를 통해 토큰을 적재하도록 미리 만들어두었는데요.

저처럼 하시는 분들은 mysqlrequest 모듈을 설치하도록 합시다. (파이썬은 requests라서 헷갈리네)

npm i mysql request

 

 

 

코드 작성

functions 폴더로 이동하면 index.js라는 파일이 존재합니다.

functions/index.js

const functions = require('firebase-functions');
const requests = require('request');
const mysql = require('mysql');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//

const headers = {
	'Content-Type': 'application/json; UTF-8',
	'Authorization': `key=${functions.config().server.key}`,
}
const url = 'https://fcm.googleapis.com/fcm/send'
const icon = 'https://lh3.googleusercontent.com/vr4fECPpvS6yzPzoJ_cKib8GKISzn2podJTenF5wJI-oPNx7ZOgQkezQJaZ-g_T8afiMWUx25FmxO1m3RuKlCGrrf9lrTn71Tc1dO5VMgCM8KIMbksVOLzXc9ULUEZkfUY0RkXd1iasrRNbmc2oye1L2RFZDBHsFB77TL5Satqnqir7vcxt1DCUpNuuAeskbWpkVQ9ZWQG_BmapPtP4LZnXm6q2GPZ85OFhM8i_WeYtoZGVjJ6cA0Lts9HeDeuOzISqn5b4K1o3hWYRNnrw1LqD5h032R2Xe6yc0OZTWh0wssKcQPhP13sYPDqVW-snSIucVdZAvq1ba8081itcb2foZ9AUaM5RAsw0N9DMpzgtQCbOeWlqf3AIa7iH8JQitoWs0p_pim919wpBo9dHTbNlT83GmitZ_ZvW0ef7N6bpeQgUseggT1LQyTFWot80SEKhMzi3MvXY7GGthr1alh7xPP8vFJuH_aQepYoCiau43MJg35vhxcfY89onQ6tYyBxPY0X4ipgpMG6sUN21WnkbS_iHVLV2Wp0-ZOwUOkeplBKXVQ6p7B5P0jYqGAcNvB6cKbDAKqhVpHlRlvGrmSPbhZRIywZqMpp_2ZTRbHbcU7GIx9kkLIDDAivwt1LVSs4aKxrj35v7trnsvscfftxj_JGHrzpS2bx8z_DNU5VbfFYK5MEt65ehvM9_z=s755-no'
const connection = mysql.createConnection({
 	host: functions.config().db.host || '172.17.0.1',
 	user: functions.config().db.user || 'root',
 	port: functions.config().db.port || 3306,
 	password: functions.config().db.pass || 'regexp12!',
 	database: functions.config().db.name || 'overmap',
 	insecureAuth: true
})
connection.connect();

exports.allPush = functions.region('asia-northeast1').https.onRequest((request, response) => {
    const query = `ㅋ__ㅋ 쿼리는 알아서..`
	connection.query(query, function(error, results, fields){
		if (error){console.error(error)}
		const params = {
			'registration_ids': results.map((x) => x.token),
			'notification': {
				'title': 'OVERMAP - 신규 워크샵 등록 알림',
				'body': `${request.query.title}`,
				'icon': icon,
				'click_action': request.query.action || 'https://overmap.me/workshop',
			}
		}
		requests.post({
			headers: headers,
			url: url,
			body: params,
			json: true
		}, function(error, response, body){
			console.log(body)
		})
	})
    response.send("allPush Successed!");
});

 네 대충 이런 식으로 썼는데 잘 동작하는군요 대충 설명하자면...

 

변하지 않는 고정값들은 전역으로 headers, 푸시 url, 아이콘, DB 커넥션 등을 선언하였고

exports.allPush 여기서 allPush가 Cloud Functions에서 관리하게 되는 함 수명입니다.

 

https://firebase.google.com/docs/functions/http-events?hl=ko

request 인자의 메서드들 참고자료

 

 

 

functions.region('asia-northeast1') 지역을 설정하지 않으면 기본값이 us-east1로 잡힙니다.

https://firebase.google.com/docs/functions/locations?hl=ko

2020.03에 서울 리전이 등장하였지만 아직 Cloud Functions에서는 도입하지 못하고 제 기억으론 Cloud SQL 정도는 가능한 걸로 알고 있습니다. App Engine도 아직(2020.03.29 기준) 안됩니다.

 

여기 가시면 위 이미지 내용과 region(지역) 설정 방법 등에 대해 자세히 설명되어 있습니다.

Cloud functions는 일회성 단발 함수 이기에 굳이 List에 담는 캐싱을 하지 않고, 쿼리 결과로 바로 때려뿌었습니다

 

오히려 코드는 지난 게시글이 더 도움이 될 것 같습니다.

 

 

 

 

Firebase 함수에 환경변수 설정하기

firebase functions:config:set db.host="localhost" db.port=3306

터미널에서 위처럼 firebase 명령을 이용하여 함수에 환경변수를 설정할 수 있습니다.

참고자료

 

환경 구성  |  Firebase

경우에 따라 함수에 타사 API 키 또는 조정 가능한 설정과 같은 추가적인 구성이 필요할 수 있습니다. Cloud 함수용 Firebase SDK는 이러한 프로젝트 관련 데이터 유형을 손쉽게 저장하고 검색할 수 있는 환경 구성을 기본적으로 제공합니다. 프로젝트의 환경 구성 설정 환경 데이터를 저장하려면 Firebase CLI에서 firebase functions:config:set 명령어를 사용합니다. 각 키를 마침표로 네임스페이스화하여 서로 관련된 구성을

firebase.google.com

functions.config().db.host

사용은 코드 작성 순서에서 본 것처럼 위와 같이 사용할 수 있습니다.

 

 

 

 

Firebase 로컬에서 테스트하기

Firebase는 AWS와 다르게 십색기가 아니어서 로컬에서 테스트하는 것을 매우 쉽게 지원해주고 로컬에서 실행되기에

비용조차 나가지 않아 테스트하기 정말 깔끔합니다.

 

https://gmyankee.tistory.com/289

 

AWS가 십색기인이유

AWS가 십색기인이유... 상황1. 서버가 EC2 대시보드에서 켜져있다고 하는데도 불구하고, 서버가 다운됨, SSH 웹 모두 갑자기 안됨(SELinux) 문제도 아님 이유를 알 수 없으니 아래 돌아온 답변을 확인해보세요.....

gmyankee.tistory.com

 

firebase emulators:start --only functions

마찬가지로 터미널에서 firebase명령을 사용하여 오직 함수만 실행! 을 인자로 주면

함수가 실행됩니다.

 

참고자료

 

실행 명령 화면

 

 

잘 보시면 functions [allPush]: http function initialized (http://localhost:5001/프로젝트명/지역명/함수명)

이게 로컬 실행의 해당 함수 URL입니다.

로컬테스트 실행화면

 

 

Firebase 배포하기

드디어 막바지에 도달했습니다.

이제 함수를 배포하면 되는데 사실 부연설명이나, 캡처 등 이거 저거 잡소리가 많아서 스크롤이 길어진 것 같은데,

막상 몇 번 해보시면 코드 작성하는 부분 빼면 5분도 안 걸립니다..

 

배포도 정말 쉽습니다!

로컬 테스트를 실행할 때처럼 하시면 되는데 emulator 대신에 이제는 deploy를 입력하세요!

$ firebase deploy --only functions

 

 

 

 

이제 Firebase에서 Functions 메뉴에서 대시보드를 보시면

 

함수가 생성된 것을 볼 수 있습니다. 로그도 볼 수 있고, 상태 사용량 등을 모두 체크할 수 있으며,

로그는 구글의 로깅 할당량 기준을 따릅니다.

https://cloud.google.com/logging/quota-policy?authuser=0

 

 

 

 

부록 - 참고자료(문헌?)