관리 메뉴

ふたりで

Http v1 FCM Admin SDK mulit tocken send message By topic 본문

Spring

Http v1 FCM Admin SDK mulit tocken send message By topic

graykang 2024. 6. 27. 18:01
728x90
반응형
SMALL

파이어베이스의 push message 전송용 레거시 http api 가 2024년 06월 22 이후로 사용할 수 없어

http v1 방식으로 전환 작업을 하였다.

2024년 6월 20일 전에 연장 신청을 한경우 2024년 7월 22까지 연장을 해준다.

문제는 http v1 API에서는 여러 fcm_id에 message를 전송하는 방법이 달라졌다.

해서 변경한 내용을 정리 해보려 한다.

 

여러 개의 fcm_id에 message전송을 하려면 firebase에서는 topic 네임을 사용한 전송 방식을

권장하고 있으며, 최근 업데이트된 API문서에도 여러 개의 fcm_id를 직접 사용하여 전송할 수 있던

SDK 내부 함수들이 Deprecated 되어 있는 걸 확인할 수 있다.

 

firebase에서 권장 하는 topic 방식은 내가 전송할 fcm_id에 topic을 설정하고 http v1방식으로 전송 시

message 셋 내에 topic명만 명시를 해주면 해당 topic이 등로 된 fcm_id에게 message를 firebase에서

알아서 push를 해주는 방식이다.

- 아래 링크 참고

https://firebase.google.com/docs/cloud-messaging/server?hl=ko

 

Firebase 클라우드 메시징

Google I/O 2023에서 Firebase의 주요 소식을 확인하세요. 자세히 알아보기 의견 보내기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 서버 환경 및 FCM Firebase 클

firebase.google.com

 

내가 구현한 방식은 아래와 같다.

 1. 기기별로 등록한 fcm_id를 업데이트하고 있는 DB에서 알림을 보낼 fcm_id를 최근 접속 이력이 있는 건을 조회한다.

 2. 조회된 List를 500건씩 나누어 임의의 topic으로 등록을 한다.

 3. 해당 topic으로 http v1방식 또는 fcm admin sdk의 .send()로 알림 전송 요청을 한다.

 4. 전송요청한 결과가 성공이면 2번의 등록건에 대해 topic을 제거한다.

중요한 건 1에서 조회했던 리스트를 500건으로 나눈 값만큼 루프를 돌며 처리해야 한다.

참고로 topic 등록 시 파라미터로 보낼 수 있는 fcm_id의 개수가 1000건으로 정해져 있다.

 

# 참고: firebase에서 나와 같은 방식으로 사용하는 걸 권장하는지는 모르겠다.

보통은 앱에서 fcm_id를 기준으로 정해진 topic을 등록(구독)하고 해당 정보를 DB서버에 저장하면,

message를 보낼 서버에서는 fcm_id가 저장된 DB에서 push를 보낼 fcm_id와 저장된 topic을 조회하여

알림을 보내는 방식만 설명을 하고 있다.

하지만 위의 firebase문서에는 여러 개의 fcm_id를 기준으로 topic을 등록 해제 할 수 있는 내용이 있는 걸로 봐서

나와 같은 방식으로 사용을 해도 무방해 보인다... 머 나중 가서 문제 되면 topic을 정해서 쓰면 되지 머;;;;;;;; 

 

결론은 나와 같이 특정 fcm_id들을 기준으로 그때그때 단순 알림 메시지만 보낼 것이라면 굳이 topic을 DB에 따로

관리할 필요 없이 알림 보낼 fcm_id들에 대해 먼저 topic을 등록하고 해당 topic으로 알림 전송 요청을 하고 성공하면

topic을 제거하면 되지 않을까 싶다.

 

# 적용환경.

- spring3

- tomcat7

- java8

- maven2

 

# 먼저 firebase console에 접속하여 프로젝트설정> 서비스계정> firebase admin SDK 화면에서

"새 비공개 키 생성" 버튼을 눌러 키를 서비스계정용 키를 다운로드한다.

해당 파일은 {서비스계정명}. json 파일이다. 이 파일은 나중에 FCM Admin SDK를 초기화할 때 사용하게 된다.

 

 

# 그다음 maven 프로젝트 설정 파일인 pom.xml에 dependency를 추가해 준다.

<dependency>
    <groupId>com.google.firebase</groupId>
    <artifactId>firebase-admin</artifactId>
    <version>9.2.0</version>
</dependency>
 <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.10</version> <!-- Java 8과 호환되는 버전 선택 -->
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.2.2</version>
</dependency>
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version> <!-- 최신 버전으로 교체 가능 -->
</dependency>

# 다음으로 FCM Admin SDK 설정을 해준다.

처음에 firebase console에서 생성한 비공개 키를 프로젝트 내 위치시키고 해당 파일 경로를 설정한다.

참고: 아래와 같이 설정 하면 프로젝트가 서버에 로딩 될 때 fcm admin sdk가 설정값을 기준으로 초기화 된다.

package com.graykang.comm.config;

import java.io.IOException;
import java.io.InputStream;
import javax.annotation.PostConstruct;

import org.springframework.context.annotation.Configuration;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;

@Configuration
public class FcmSdkConfig {

    @PostConstruct
    private void initialFCM() throws IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = "fcm/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json";
        try(InputStream input = classLoader.getResourceAsStream(path)){
        	
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(
                    		GoogleCredentials
                    		.fromStream(input)
                    		.createScoped(Arrays.asList("https://www.googleapis.com/auth/cloud-platform"))
			).build();
            FirebaseApp.initializeApp(options);
            System.out.println("========== FirebaseApp.initializeApp ==========token:"+options.toString());
        }catch(IOException | IllegalStateException e){
        	
        }
    }
}

- src/main/resources/fcm/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json 파일의 위치는 아래 그림 참고

(해당. json파일이 firebase console에서 생성 후 다운로드한 비공개 키이다)

 

# 다음 초기화한 SDK를 사용하여 알림 메시지를 전송해 보자.

먼저 알림을 보낼 fcm_id목록을 조회한 후 해당 목록을 기준으로 topic을 등록하고 등록이 성공되면 알림을 보내고

알림 전송이 성공하면 fcm_id목록을 기준으로 등록했던 topic을 해제해 준다.

//sendFcmid 에는 전체 리스트를 루프돌며 500건씩 나누어 값이 들어온다
sendFcmid = new String[Math.min(arrRcvFCMKey.length - (sendMaxValue * j), sendMaxValue)];

ArrayList fcmIdList = new ArrayList(Arrays.asList(sendFcmid));

regtopic = PushUtil.regFcmIdsTopic(fcmIdList, "mulitsend");//topic 등록 topic명은 꼴리는대로
//System.out.println("++++++++++++++++++++"+j+"++++regFcmIdsTopic:"+regtopic.toJSONString());

if(regtopic != null) {
	//topic에 알림전용 요청
    pushResult = PushUtil.sendFcmPushMsgSDKTopic(push, regtopic.get("regTopicName").toString());
    //topic해제 꼴리는대로 등록한 topic명 해제
    JSONObject unregtopic = PushUtil.unRegFcmIdsTopic(fcmIdList, "mulitsend");
    //System.out.println("+++++++++++++++++++"+j+"+++++unregtopic:"+unregtopic.toJSONString());
}

 

1. PushUtil.regFcmIdsTopic(fcmIdList, "mulitsend");//topic 등록 

    @SuppressWarnings("unchecked")
	public static JSONObject regFcmIdsTopic(List<String> sendFcmid, String topicName) {
    	JSONObject resultJSON = new JSONObject();
    	
    	try {
			TopicManagementResponse response = FirebaseMessaging.getInstance().subscribeToTopic(sendFcmid, topicName);
			//System.out.println(response.getSuccessCount() + " tokens were subscribed successfully");
			resultJSON.put("code", 0);
			resultJSON.put("regTopicName", topicName);
			resultJSON.put("sucessCnt", response.getSuccessCount());
			
		} catch (FirebaseMessagingException e) {
			// TODO Auto-generated catch block
			resultJSON.put("code", -1);
			resultJSON.put("regTopicName", topicName);
			resultJSON.put("msg", e.getMessage());
			e.printStackTrace();
		}
		return resultJSON;
	}
728x90
반응형
SMALL

2. PushUtil.sendFcmPushMsgSDKTopic(push, regtopic.get("regTopicName").toString());//topic으로 알림 전송 요청.

	@SuppressWarnings("unchecked")
	public static JSONObject sendFcmPushMsgSDKTopic(JSONObject msgObj, String topicType) {
		JSONObject reBodyJson = new JSONObject();
		Message message = Message.builder()
				.setNotification(
                                    Notification.builder()
                                    .setTitle(msgObj.get("msgtitle").toString())
                                    .setBody(msgObj.get("msg").toString()).build()
				)
				.putAllData(msgObj)
			        .setTopic(topicType)
			        .build();

			// Send a message to the devices subscribed to the provided topic.
			String response = null;
			try {
				response = FirebaseMessaging.getInstance().send(message);
		        reBodyJson.put("response", response);
			} catch (FirebaseMessagingException e) {
				reBodyJson.put("error", e.getMessagingErrorCode());
				e.printStackTrace();
			}
			// Response is a message ID string.
			//System.out.println("Successfully sent message: " + response);		
		return reBodyJson;
	}

 

3. PushUtil.unRegFcmIdsTopic(fcmIdList, "mulitsend");//topic 해제

    @SuppressWarnings("unchecked")
	public static JSONObject unRegFcmIdsTopic(List<String> arrFcmIdList, String topicName) {
    	JSONObject resultJSON = new JSONObject();
    	
    	try {
			TopicManagementResponse response = FirebaseMessaging.getInstance().unsubscribeFromTopic(arrFcmIdList, topicName);
			//System.out.println(response.getSuccessCount() + " tokens were subscribed successfully");
			resultJSON.put("code", 0);
			resultJSON.put("unTopicName", topicName);
			resultJSON.put("UnsubscribeCnt", response.getSuccessCount());
			
		} catch (FirebaseMessagingException e) {
			// TODO Auto-generated catch block
			resultJSON.put("code", -1);
			resultJSON.put("unTopicName", topicName);
			resultJSON.put("msg", e.getMessage());
			e.printStackTrace();
		}
		return resultJSON;
	}
728x90
반응형
LIST
Comments