FCM 백엔드 구현

최성민·2023년 5월 1일
0

FCM 전체 플로우

  1. 클라이언트 -> firebase : 토큰요청 (firebase가 클라이언트를 식별하는 수단)
  2. 클라이언트 -> 서버 : 토큰전달
  3. 서버 : user_id와 FB토큰을 맵핑해서 DB에 저장
  4. 서버에서 이벤트 발생시 알림을 전달해줄 클라이언트의 FB 토큰을 DB에서 가져옴
  5. 서버에서 해당 FB토큰과 알림을 firebase에 전달
  6. firebase에서 해당 FB토큰에 해당하는 클라리언트한테 알림을 전달 (푸쉬알림)

1. 새 비공개 키 생성

application-ENV파일에 해당 파일내의 내용을 json형식으로 저장

'application-ENV.yml'
firebase-service-key: |
  {
    "type": "",
    "project_id": "",
    "private_key_id": "f",
    "private_key": "",
    "client_email": "",
    "client_id": "",
    "auth_uri": "",
    "token_uri": "",
    "auth_provider_x509_cert_url": "": ""
  }
application.yml
spring:
  profiles:
    include:
      - ENV

2. firebase admin sdk 초기화

firebase admin sdk 초기화란?

서버 측에서 Firebase 서비스와 상호 작용하기 위한 도구
서버 애플리케이션에서 Firebase 서비스와의 연결을 설정하는 과정
푸시 알림을 보내거나 인증, 데이터베이스 액세스, 스토리지 등의 Firebase 기능을 사용할 수 있다

Firebase Admin SDK를 사용하면 스프링 부트 서버에서 iOS와 Android 각각의 디바이스 토큰을 수집하고, 이를 대상으로 푸시 알림을 전송할 수 있습니다. 이렇게하면 스프링 부트 서버는 iOS 및 Android 모바일 애플리케이션에 대해 푸시 알림을 일괄적으로 처리할 수 있습니다.

firebase 초기화 단계 (chat gpt 답변)

Firebase Admin SDK 초기화 단계는 다음과 같습니다:

Firebase 프로젝트 설정: Firebase 콘솔에서 새 프로젝트를 생성하고 필요한 Firebase 서비스를 활성화합니다.

서비스 계정 키 생성: Firebase 콘솔에서 서비스 계정 키를 생성하고 다운로드합니다. 서비스 계정 키는 Firebase Admin SDK가 서버에서 Firebase 서비스와 통신할 수 있는 인증 정보를 제공합니다.

SDK 종속성 추가: 사용하는 언어와 프레임워크에 따라 Firebase Admin SDK 종속성을 프로젝트에 추가합니다. 종속성 관리 도구를 사용하여 SDK를 가져올 수 있습니다.

SDK 초기화: 서버 애플리케이션의 초기화 단계에서 Firebase Admin SDK를 초기화합니다. 서비스 계정 키를 사용하여 Firebase 서비스와의 연결을 설정하고, 인증 정보를 로드하여 애플리케이션에 Firebase 기능을 사용할 수 있도록 준비합니다.

Firebase Admin SDK 초기화는 서버의 시작 시점 또는 필요한 곳에서 수행될 수 있습니다. 주로 백엔드 애플리케이션의 진입점 또는 서비스 초기화 단계에서 Firebase Admin SDK 초기화 코드가 작성됩니다.

Firebase Admin SDK를 초기화하면 서버에서 Firebase의 다양한 기능을 사용할 수 있으며, 백엔드 애플리케이션에서 Firebase 서비스와 상호 작용할 수 있습니다.

구현

  1. 디펜던시 추가
dependencies {
	implementation 'com.google.firebase:firebase-admin:9.1.1'
}
  1. fcm admin sdk 초기화
'FcmController'
@Configuration
public class FcmConfig {

    @Value("${firebase-service-key}")
    private String serviceAccountJson;

    @PostConstruct
    public void init() throws IOException {
        FirebaseOptions options = FirebaseOptions.builder()
                .setCredentials(GoogleCredentials.fromStream(
                        new ByteArrayInputStream(serviceAccountJson.getBytes(StandardCharsets.UTF_8))))
                .build();
        FirebaseApp.initializeApp(options);
    }
}

공식문서에서는 파일 경로로 파일 자체를 inputstream을 해서 비공개 키값을 넣어 sdk를 초기화 하는데
나는 파일 내용을 꺼내와서 변수에 json형식 저장하고 가져왔다.

3. 클라이언트 Fcm DB에 저장하기

REDIS와 RDB중 저장할 DB선택

REDIS 선정

  • 긴 문자열 같은 경우는 디스크에서 메모리로 가져오는 작업이 비용이 조금 클 것 같아서 메모리 레디스에 저장하는 법을 선택했다
  • 동일한 요청이 반복될 때 캐시에서 데이터를 가져오므로 데이터베이스에 대한 부하를 줄일 수 있다.

예상되는문제점

  • 레디스가 꺼지면 데이터가 다 날라간다
  • 이는 15분마다 스냅샷을 통해 어느정도 해결할 수 있을 듯하다
  • 레디스 서버부하 위험성이 더 클까? -> 서버부하테스트에서 생각하자
  • redis가 꺼졌을때 클라이언트가 registeration token을 업데이트나 삭제를 했다면?
    • 이건 RDM에서도 마찬가지로 생각해야할 문제
    • 서버에서 Error를 띄워줄것
    • 프론트에서 이것을 어떻게 처리해야할까?
    • 10분마다 다시 요청?

클라이언트에게 registration ID를 받고 저장하기

public class FcmController {

    private final FcmService fcmService;

    //클라이언트에게 FCM registraion을 받아 user_id값과 매필하여 DB에 저장하기
    @PostMapping("/send/registeration-token")
    public void saveClientId(@RequestBody FcmClientRequest fcmClientRequest){
        fcmService.saveClientId(fcmClientRequest);
    }
}
public class FcmService {

    public void saveClientId(FcmClientRequest fcmClientRequest){
        String clientId = fcmClientRequest.getClientRegistrationToken();
        //여기에 레파지토리에서 유저 Id값과 함께 저장
    }
}

4. FireBase에 push알림 보내기

공식문서 예제코드

단일 대상 Fcm 보내기 샘플코드

String registrationToken = "YOUR_REGISTRATION_TOKEN";

Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setToken(registrationToken)
    .build();

String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message: " + response);FirebaseMessagingSnippets.java

다수 대상 Fcm 보내기 샘플코드

List<String> registrationTokens = Arrays.asList(
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n"
);

MulticastMessage message = MulticastMessage.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .addAllTokens(registrationTokens)
    .build();
BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(message);
System.out.println(response.getSuccessCount() + " messages were sent successfully");FirebaseMessagingSnippets.java

서버 보내기 요청 작성을 위한 샘플코드

Message message = Message.builder()
    .setNotification(Notification.builder()
        .setTitle("$GOOG up 1.43% on the day")
        .setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
        .build())
    .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("industry-tech")
    .build();FirebaseMessagingSnippets.java

단일 대상 Fcm 보내기 커스텀

    public void fcmSpecificPushAlarm(int userId){
        Object value = redisTemplate.opsForValue().get(userId);
        if(value == null){
            System.out.println("The registration token for the user"+ userId +" is not exist");
        }else {
            String registrationToken = (String) value;
            	Message message = Message.builder()
                	// Android, Anse 공통설정
                	.setNotification(Notification.builder()
                	.setTitle("제목제목")
                	.setBody("바디바디")
                    .build()
                    )
                    // Android 설정
                    .setAndroidConfig(AndroidConfig.builder()
                    	.setTtl(3600 * 1000)
                        .setNotification(AndroidNotification.builder()
                        .setIcon("stock_ticker_update")
                        .setColor("#f45342").build()
                        ).build()
                    )
                    //Apple Push Notification service 설정
                    .setApnsConfig(ApnsConfig.builder()
                            .setAps(Aps.builder().build()
                            ).build()
                    ).build();
            try{
                String response = FirebaseMessaging.getInstance().send(message);
                System.out.println("Successfully sent message: " + response);
            } catch (FirebaseMessagingException e) {
                throw new FcmSendException(e.getMessage());
            }
        }
    }

참고자료

https://firebase.google.com/docs/cloud-messaging/server?hl=ko#firebase-admin-sdk-for-fcm
https://firebase.google.com/docs/cloud-messaging/send-message?hl=ko#customize-messages-across-platforms

profile
칠전팔기 화이팅

0개의 댓글

관련 채용 정보