본문 바로가기

4.개발 및 운영 환경

구글 FCM 사용하기

들어가기

FCM은 Firebase Cloud Messaging으로 구글에서 제공하는 메시지를 안정적으로 무료로 전송할 수 있는 메시징 솔루션입니다. 메시지는 모바일 푸시와 웹 알림으로 전송할 수 있습니다. FCM을 사용해보자.

작성자: ospace114@empal.com, http://ospace.tistory.com/

동작 매커니즘

간단하게 메시지를 보내는 동작 방식을 살펴보겠습니다. 크게 2단계로 구분할 수 있는데, 토큰획득과 메시지 전송입니다.

토큰 획득

메시지 전송할 사용자 토큰 획득하는 과정

  1. 사용자는 Firebase로 토큰 요청
  2. 사용자는 획득한 토큰을 서비스 서버로 전송
  3. 서비스 서버는 토큰을 정적 저장소에 저장

여기서 서비스 서버는 서비스를 제공하는 서버로 백엔드 서버에 해당한다.

메시지 전송

획득한 토큰을 사용해서 사용자에게 메시지 전송

  1. 저장된 토큰으로 서비스 서버는 Firebase에 메시지 전송
  2. Firebase은 받은 메시지를 사용자에게 전송
  3. 사용자는 리스트를 통해 메시지 수신

프로젝트 생성

FCM를 사용하기 위해서는 구글에서 프로젝트를 생성해야 한다. Firebase 콘솔로 접속해서 기본적인 프로젝트로 생성하면 된다.

프로젝트 설정

프로젝트가 설정이 되면 FCM를 전송하기 위한 설정을 진행하면 된다. 웹 앱 형태로 추가 할려고 한다.

프로젝트 개요 오른쪽에 톱니바퀴 모양 아이콘을 클릭하여 표시되는 컨텍스트 메뉴에서 [프로젝트 설정]을 클릭한다.

웹 앱 추가

내 앱 탭에서 오른쪽에 있는 웹 앱 아이콘을 선택해서 추가한다. 적당한 이름을 입력하고 등록한다.

SDK 추가는 모듈 설치와 스크립트 입력 방식을 선택할 수 있다. 본인의 환경에 맞게 선택하면 된다.

모듈 설치 방식

모듈설치는 npm을 이용한 설치이다. 예제 코드에 있는 키값 들은 웹 앱이 등록되면 자동으로 생성되고 화면에 표시된다. 아래는 샘플이기 때문에 그대로 사용하면 에러가 발생한다.

  1. 패키지 설치
npm install firebase
  1. 예제 코드
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "AIzaSyCei3hImPT8-tMEtIDwFQo5arqpAj-08Mig",
  authDomain: "api-project-0652658475710.firebaseapp.com",
  projectId: "api-project-0652658475710",
  storageBucket: "api-project-0652658475710.appspot.com",
  messagingSenderId: "0652658475710",
  appId: "1:0652658475710:web:071d09cbb0f9da58d9394ef"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

스크립트 입력 방식

스크립트 입력은 바로 javascript을 입력하기에 별도 설치 없이 바로 사용해서 더 단순하다. 아래 예제는 module 형태로 작성되어 있다.

<script type="module">
  // Import the functions you need from the SDKs you need
  import { initializeApp } from "https://www.gstatic.com/firebasejs/9.8.3/firebase-app.js";
  // TODO: Add SDKs for Firebase products that you want to use
  // https://firebase.google.com/docs/web/setup#available-libraries

  // Your web app's Firebase configuration
  const firebaseConfig = {
    apiKey: "AIzaSyCei3hImPT8-tMEtIDwFQo5arqpAj-8Mig",
    authDomain: "api-project-652658475710.firebaseapp.com",
    projectId: "api-project-652658475710",
    storageBucket: "api-project-652658475710.appspot.com",
    messagingSenderId: "652658475710",
    appId: "1:652658475710:web:71d09cbb0f9da58d9394ef"
  };

  // Initialize Firebase
  const app = initializeApp(firebaseConfig);
</script>

키쌍 생성

웹에서 푸시하기 위한 인증서 키 쌍을 생성해 보자. 웹 푸시 인증서 탭에서 [Generate key pair] 버튼을 클릭해서 생성하면 된다.

샘플 코드(module)

아래는 생성된 키 쌍 사용하는 샘플 코드이다. 중요한 부분은 현재 웹 서비스의 루트 경로에 “firebase-messaging-sw.js” 파일을 생성해야 한다. 테스트하기 위해서 임시로 빈 파일로 생성한다.

import { initializeApp } from "https://www.gstatic.com/firebasejs/9.8.3/firebase-app.js";
import { getToken, getMessaging } from "https://www.gstatic.com/firebasejs/9.8.3/firebase-messaging.js";

const firebaseConfig = { … };

const app = initializeApp(firebaseConfig);
const messaging = getMessaging(app);

getToken(messaging, {validKey: '위에서 생성된 키'})
.then((currentToken)=>{
  if (currentToken) {
    console.log('token:', currentToken);
    // 받은 토큰은 서버로 전송해서 저장
  } else {
    console.warn('No registration token available. Request permission to generate one.');
  }
})
.catch(err=>{
  console.error(err);
});

샘플예제

<script type="text/javascript" src="https://www.gstatic.com/firebasejs/9.8.3/firebase-app-compat.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/firebasejs/9.8.3/firebase-messaging-compat.js"></script>
<script type="text/javascript" src="/fcm_init.js"></script>
<script>
const messaging = firebase.messaging();
messaging.onMessage((payload)=>{
    console.log('>>', payload);
});
messaging.getToken({validKey: '위에서 생성된 키'})
    .then((currentToken)=>{
        console.log('> token:', currentToken);
    })
    .catch(err=>{
      console.error(err);
    });
}
function onDeleteToken(token) {
  messaging.deleteToken(token).then(()=>{
    alert('token deleted');
  }).catch(err=>{
    console.log('delete token error:', err);
  });
}
</script>

firebase-messaging-sw.js 파일

백그라운드에서 동작하며 메시지 수신 처리한다.

importScripts(
  "https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"
);
importScripts(
  "https://www.gstatic.com/firebasejs/9.8.3/firebase-app-compat.js"
);
importScripts(
  "https://www.gstatic.com/firebasejs/9.8.3/firebase-messaging-compat.js"
);
importScripts("/fcm_init.js");
const messaging = firebase.messaging();
// For more info see:
// https://firebase.google.com/docs/cloud-messaging/concept-options
messaging.onBackgroundMessage(function (payload) {
  console.log(
    "[firebase-messaging-sw.js] Received background message ",
    payload
  );
  let noti = payload.data.notification;
  if ("string" === typeof noti) {
    noti = JSON.parse(noti);
  }
  const notificationTitle = noti.title;
  const notificationOptions = {
    body: noti.body,
    icon: "/firebase-logo.png",
  };
  self.registration.showNotification(notificationTitle, notificationOptions);
});

fc_init.js 파일

공통으로 설정 초기화 파일

const firebaseConfig = {
  apiKey: "API키",
  authDomain: "인증할 도메인",
  projectId: "프로젝트 식별자",
  storageBucket: "저장할 버킷 도메인",
  messagingSenderId: "메시지 송신자 식별자",
  appId: "앱 식별자",
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

Checklist

  • 이전에 실제 사용했던 샘플 코드 확인
  • 처음 모듈 설치과 스크립트 작성에서 생성된 정보는 어디에 사용
  • 추 후에 키 쌍 생성된 값은 어디에서 사용하는가?

Legacy Cloud Messaging API

이전에 전송한 방식으로 현재 사용하려면 사용설정이 필요하다.

콘솔에서 서비스 키를 다운로드 받는다.

https://console.firebase.google.com/project/{프로젝트 ID}/settings/serviceaccounts/adminsdk

[새 비공개 키 생성] 버튼 클릭하면 json파일이 생성된다.

java 기반

서버측을 java기반 프로젝트로 구성된 경우이다.

Dependency 추가

<dependency>
  <groupId>com.google.firebase</groupId>
  <artifactId>firebase-admin</artifactId>
  <version>7.3.0</version>
</dependency>

인증정보 획득

프로젝트 설정 > 서비스 계정 탭에서 Firebase Admin SDK 항목에서 "새 비공개 키 생성"을 사용해서 획득.

https://console.firebase.google.com/project/{프로젝트ID}/settings/serviceaccounts/adminsdk

api-project-0000000000-firebase-adminsdk-xxxxx-0000000.json 파일 샘플

{
  "type": "service_account",
  "project_id": "api-project-652658475710",
  "private_key_id": "00000",
  "private_key": "-----BEGIN PRIVATE KEY-----\n(중략)_\n-----END PRIVATE KEY-----\n",
  "client_email": "firebase-adminsdk-xxxxx@api-project-00000000.iam.gserviceaccount.com",
  "client_id": "0000000000000",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-xxxxx%40api-project-99999999999.iam.gserviceaccount.com"
}

샘플코드


// 인증 관련 정보 읽어와서 초기화
ClassPathResource resource = new ClassPathResource(
    "api-project-0000000000-firebase-adminsdk-xxxxx-0000000.json");

FirebaseOptions options = FirebaseOptions.builder()
    .setCredentials(GoogleCredentials.fromStream(resource.getInputStream()))
    // .setDatabaseUrl("https://<schema>.firebaseio.com/")
    .build();
FirebaseApp.initializeApp(options, "fcm");
// FirebaseApp.initializeApp(options); // 기본 인스탄스로도 가능.

// 한 사용자에게 메시지 전송
Message message = Message.builder()
    .putData("time", "12:34")
    .setToken(token)
    .setTopic(topic)
    .setNotification(notification)
    .build();
FirebaseMessaging messaging = FirebaseMessaging.getInstance(FirebaseApp.getInstance("fcm"));
;
messaging.send(message);

// 여러 사용자에게 메시지 전송
MulticastMessage message = MulticastMessage.builder()
    .putData("time", "12:34")
    .setNotification(notification)
    .addAllTokens(tokens)
    .build();
FirebaseMessaging messaging = FirebaseMessaging.getInstance(FirebaseApp.getInstance("fcm"));
BatchResponse res = messaging.sendMulticast(message);

// 토픽 가입 및 해제
List<String> registrationTokens = Arrays.asList(token);
FirebaseMessaging.getInstance().unsubscribeFromTopic(registrationTokens, topic);

List<String> registrationTokens = Arrays.asList(token);
FirebaseMessaging.getInstance().subscribeToTopic(registrationTokens, topic);

okhttp로 직접 전송

dependency추가

<dependency>
  <groupId> com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
</dependency>

메시지 생성하고 전송

String token = …; // 보낼 대상 토큰
String msg = …; // 보낼 메시지
String fmt = "{\"message\": {\"token\": \"%s\", \"notification\": {\"title\": \"%s\",\"body\": \"%s\"},\"data\": {\"foo\": \"hi\"}}}";
String title = "알림";
String body = msg;
String message = String.format(fmt, token, title, body);
AccessToken accessToken = googleCredentials.getAccessToken();
logger.info("sendv1: accessToken[{}]", accessToken);
Request req = new Request.Builder()
    .url("https://fcm.googleapis.com/v1/projects/api-project-652658475710/messages:send")
    .addHeader("Content-Type", "application/json; UTF-8")
    .addHeader("Authorization", "Bearer " + accessToken.getTokenValue())
    .post(RequestBody.create(MediaType.parse("application/json"), message)).build();
logger.info("sendv1: message[{}]", message);
logger.info("sendv1: req[{}]", req.toString());
OkHttpClient client = new OkHttpClient();
Response res = client.newCall(req).execute();
logger.info("sendv1: res[{}]", res.body().string());

참고

[1] Firebase 클라우드 메시징, https://firebase.google.com/docs/cloud-messaging, 2022.09.16

[2] https://console.firebase.google.com/u/0/?hl=ko&pli=1

[3] FCM 마이그레이션 참고, https://firebase.google.com/docs/cloud-messaging/migrate-v1?authuser=0

반응형