구현 이유

앱 알림 기능이 필요하게 됐고, 개발을 하기 위해 서칭을 하던 중 firebase에서 FCM 메시지를 통해 앱 알림기능 구현을 구현할 수 있다는 것을 알게 됐다.

먼저, Firebase를 통해 push 메시지를 전송할 수 있는 방법은 크게 2가지가 있다.

  1. Firebase Admin SDK를 이용한다.
  2. FCM HTTP 프로토콜을 이용한다.

이중에서 Java 언어를 지원하고, 인증 및 승인 처리가 편리한 Firebase Admin SDK 방법을 사용해서 개발하기로 했다.

서버를 통한 FCM 알림 구현 방법에 대해 알아보자.

개발 과정

먼저 Firebase Admin SDK를 사용하기 위해 프로젝트에 세팅을 해줘야한다.
아래의 글을 참고해 세팅을 해주자.

https://velog.io/@khc41/Firebase-Admin-SDK-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

FCM 메시지 종류

FCM 메시지에는 크게 알림 메시지, 데이터 메시지 2가지 종류가 있다.

알림 메시지란, FCM SDK가 자동으로 알림 표시를 해주고, 데이터 메시지는 클라이언트에서 코드로 메시지를 처리해야한다.

자동으로 알림 표시를 해주는 알림 메시지를 사용할 예정이다.

메시지 구조

"message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"great match!"
    }
  }

전송하는 메시지의 json 형태는 다음과 같다.
message 안의 token과 연결된 기기에 title, body를 전송하는 구조이다.

 "android":{
       "ttl":"86400s",
       "notification"{
         "click_action":"OPEN_ACTIVITY_1"
       }
     },
     "apns": {
       "headers": {
         "apns-priority": "5",
       },
       "payload": {
         "aps": {
           "category": "NEW_MESSAGE_CATEGORY"
         }
       }
     },
     "webpush":{
       "headers":{
         "TTL":"86400"
       }
     }

android, apns, webpush 옵션을 사용해서 클라이언트 별로 설정을 따로 해줄 수 있다.

메시지 전송 종류

서버에서 메시지 전송할 때 3가지 유형으로 전송이 가능하다.

1. 특정 기기 전송
2. 여러 기기 전송
3. 주제로 메시지 전송

이 세 가지 방법에 대해 알아보자.

1. 특정 기기 전송

이 방법은 FCM 토큰과 연결된 특정 기기로만 푸시를 전송하는 방법이다.

// This registration token comes from the client FCM SDKs.
String registrationToken = "YOUR_REGISTRATION_TOKEN";

// See documentation on defining a message payload.
Message message = Message.builder()
     .setNotification(Notification.builder()
                        .setTitle("title")
                        .setBody("body")
                        .build())
    .putData("score", "850")
    .putData("time", "2:45")
    .setToken(registrationToken)
    .build();

// Send a message to the device corresponding to the provided
// registration token.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

firebase 종속성을 추가하면 사용할 수 있는 Message 클래스로 원하는 메시지를 만들어서 단일 기기 타겟으로 푸시 메시지를 전송 가능하다. (토큰은 클라이언트 개발자에게 요청하자)

notification 에 있는 부분은 알림 메시지, putData 부분에 있는 map 형식의 데이터는 데이터 메시지이다.

2. 여러 기기 전송

이 방법은 기기 토큰 목록에 연결된 다수의 기기로 푸시를 전송하는 방법이다.
최대 500개의 기기까지 전송이 된다고 하니 유의해서 발송해야 한다.

// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n"
);

MulticastMessage message = MulticastMessage.builder()
    .setNotification(Notification.builder()
                            .setTitle("title")
                            .setBody("body")
                            .build())
    .putData("score", "850")
    .putData("time", "2:45")
    .addAllTokens(registrationTokens)
    .build();
BatchResponse response = FirebaseMessaging.getInstance().sendAll(message);
if (response.getFailureCount() > 0) {
  List<SendResponse> responses = response.getResponses();
  List<String> failedTokens = new ArrayList<>();
  for (int i = 0; i < responses.size(); i++) {
    if (!responses.get(i).isSuccessful()) {
      // The order of responses corresponds to the order of the registration tokens.
      failedTokens.add(registrationTokens.get(i));
    }
  }

  System.out.println("List of tokens that caused failures: " + failedTokens);
}

BatchResponse로 오류가 발생한 토큰에 대해 알 수 있고, 로그도 남길 수 있다.

주제로 메시지 전송

마지막으로 주제로 메시지를 전송하는 방법이다.
이 방법은 클라이언트에서 직접 주제를 구독하거나, 서버에서 주제를 구독하게끔 해서 구독한 기기 모두에 푸시를 전송하는 방법이다.

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
    .setNotification(Notification.builder()
                              .setTitle("title")
                              .setBody("body")
                              .build())
    .putData("score", "850")
    .putData("time", "2:45")
    .setTopic(topic)
    .build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

주제를 구독한 기기 전체에 발송돼서 운영환경에서는 주의해서 사용해야 한다.

클라이언트 별 커스텀

클라이언트 별로 메시지를 커스텀하는 방법은 위에 Message 객체에 옵션을 추가해주는 방법으로 사용 가능하다.

Message message = Message.builder()
    .setNotification(Notification.builder()
                              .setTitle("title")
                              .setBody("body")
                              .build())
    .putData("score", "850")
    .putData("time", "2:45")
	.setAndroidConfig(AndroidConfig.builder()
                            .setTtl(3600 * 1000)
                        	.setNotification(AndroidNotification.builder()
                            .setIcon("stock_ticker_update")
                            .setColor("#f45342")
                            .build())
                        .build())
    .setApnsConfig(ApnsConfig.builder()
    					.setAps(Aps.builder()
                        .setBadge(42)
                        .build())
                    .build())
    .setTopic(topic)
    .build();

플랫폼 별 커스텀하는 방법은 아래의 링크를 참조하면 된다.

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages?hl=ko

마무리

위의 예시들을 활용해 controller단에서 api를 만들고, title, body, fcm 토큰을 받아 푸시를 전송하는 api를 직접 커스텀해서 만들 수 있으니 자유롭게 활용해서 개발해보길 바란다.

참조 문서 :
https://firebase.google.com/docs/cloud-messaging/fcm-architecture?hl=ko
https://firebase.google.com/docs/cloud-messaging/send-message?hl=ko#java

0개의 댓글