앱 푸시 서버 설명(Firebase Cloud Messaging 사용)

- 용어 정의

 

FCM Architecture

 

1. 클라이언트 앱 : 푸쉬 알림을 받는 역할

2. Notification Server : 알림을 전송하는 서버로, FCM이 해당됨.

3. Provider : Spring Boot서버가 역할을 수행하며,
    - 앱에서 Sender ID값을 받아 해당값으로 FCM에서 Registration Token을 발급받은후 해당값을 Provider에 전달하여 DB등에 등록하며 해당값은 알림 전송에 사용됨. (Registration Token 등록시 iOS, Android등 OS구분값이 필요함.)
    - Provider의 경우 요청이 있을 경우 등록된 Registration Token을 이용하여 Notification Server에 요청을 전송하여 앱에 알림을 전송함.
4. Platform-level Transport Layer : 메시지 전송을 핸들링하는 전송계층
    - Android : ATL (Android Transport Layer)
    - iOS : APNs (Apple Push Notification services)
    - Web : Web Push Protocol

 


- 동작 설명

1. 앱에서 Registration Token 획득 : 앱에서 Token을 획득하기 위하여 FCM 서버에 요청한 후 Token을 획득함.
2. 서버에 Registration Token 저장 : 획득한 Token을 서버로 전송하여 DB에 저장함. 후에 메시지를 전송하기 위한 ID값으로서 사용자 테이블등에 필드를 추가하여 저장함. 또한 ios, android을 구분하기 위하여 os 구분값 필드도 필요함.

3. 서버에 저장된 Token을 이용하여 메시지 전송 : 서버에서 해당 Token값을 이용하여 FCM에 메지시 전송을 요청함.
4. Listener를 통해 앱에서 메시지 수신 : 앱에서는 Listener를 통해 FCM으로부터 메시지를 수시함.

 

 

- FCM 등록

1. https://console.firebase.google.com/ -> 해당 사이트에서 개발자 등록 및 프로젝트 생성

2. 프로젝트 설정 - 서비스 계정 메뉴에서 새 비공개 키 새성하여 해당 파일을 다운로드 받고

api-common 프로젝트에서 src/main/resources/serviceAccountKey.json 해당 경로에 serviceAccountKey.json 이름으로 붙여넣기 함.
해당 화면에서 서비스 키 등록 코드를 제공해주고 있는데 해당 코드의 경우 이미 FirebaseConfig.class로 만들어져 있음.

 

3. 프로젝트 설정 - 일반 메뉴에서 iOS 및 안드로이드 버튼을 클릭하여 알림을 받기 위하여 앱 내용 등록

프로젝트 생성하여 해당 정보 등록 후 해당 google-services.json 정보 파일을 다운로드 하여 앱 코드내에 복사 붙여넣기 한 후 해당 키값 등록 필요함.

 

- iOS FCM 등록
1. https://developer.apple.com/account -> 해당 사이트에서 개발자 등록 및 Apple Push Notification service (APNs) 를 선택 후 인증키 발급


2. Firebase Console화면에서 프로젝트 설정 - 클라우드 메시징 메뉴에서 1번에서 발급받은 p8 인증키를 등록함.

 

 

- Data Message 구성 요소 설명
user-defined custom key-value 쌍으로 적절한 키를 설정해, 클라이언트 앱에 데이터 페이로드를 전송함 (주의 사항으로 예약어는 사용불가함 : from, notification, message_type 및 google, gcm으로 시작되는 모든 단어)

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", // 메시지를 전송할 앱 Registration Token값
    "notification": {
      "title": "test title",  // 공통 Title
      "body": "test body"     // 공통 Body
    },
    "data":{
      "test" : "test1",
      "name" : "kang",
      "note" : "testtest"
    }, // 공통 Data
    "android": {
      "notification": {
        "click_action": "TOP_STORY_ACTIVITY"
      }
    }, // 안드로이드 전용 값
    "apns": {
      "payload": {
        "aps": {
          "category" : "NEW_MESSAGE_CATEGORY"
        }
      }
    } // iOS 전용 값
  }
}

데이터 구성의 경우 일반적으로는 기본 공통값만으로 가능할 것으로 보임. 앱 전용 값들의 경우 어떻게 활용가능할지는 앱쪽 전문가의 확인 및 도움이 필요함.


- 앱 전송 스프링 부트 설정 코드

1. 위 Firebase Admin SDK 에서 다운로드 받은 키정보값을 다운로드 받은 후 serviceAccountKey.json으로 변경하여 해당 resource에 붙여넣기 함.

2. application.yml 에 파일에 다음과 같이 설정 정보 파일명등을 등록함.

app:
  fcm:
    config: classpath:serviceAccountKey.json

3. 최종적으로 FirebaseConfig.java 파일을 생성하여 스프링 기동시 Bean에 등록함.

package com.x2bee.api.sample.base.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;
import java.io.FileInputStream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;

@Configuration
public class FirebaseConfig {

    @Value("${app.fcm.config:classpath:serviceAccountKey.json}")
    private Resource resource;

    @PostConstruct
    public void initFirebase() {
        try {
            GoogleCredentials credentials = GoogleCredentials.fromStream(resource.getInputStream());
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(credentials)
                    .build();
            if (FirebaseApp.getApps().isEmpty()) {
                FirebaseApp.initializeApp(options);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}



-  테스트 전송 코드

package com.x2bee.api.sample.app.sample;

import com.google.firebase.messaging.*;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;

@Service
public class FirebaseService {

    public String sendNotification() {
        try {
            String token = "ccQ-I2VQQo-YCcDaKhDzQZ:APA91bEvRMI2D1XN6jqhKtwcZIPK_OPV0arhvrfOD2g8zMlSpH2efT7gYDrxYFkrRzF0byJuBfI7wqosU8WiqEAChNyrS24wn_TaZtIhjqu1NSDTtVRpks_29JH2oxPMLKozfE_AcF7u";

            Notification notification = Notification.builder()
                    .setTitle("test title")
                    .setBody("test body")
                    .setImage("")
                    .build();

            Map<String, String> data = new HashMap<>();
            data.put("test1", "test1 kang");
            data.put("test2", "test2 kang");
            data.put("test3", "test3 kang");

            Message message = Message.builder()
                    .setToken(token)
                    .setNotification(notification)
                    .putAllData(data)
                    .build();

            String response = FirebaseMessaging.getInstance().send(message);
            return response;

        } catch (FirebaseMessagingException e) {
            e.printStackTrace();
            return "Failed";
        }
    }

}

// 여러건을 전송할경우엔 다음과 같이 list로 구성하여서 sendEach 함수를 호출함.
List<Message> messages = Arrays.asList(
    Message.builder()
        .setNotification(Notification.builder()
            .setTitle("test8888")
            .setBody("test8888 999999999")
            .build())
        .setToken(registrationToken1)
        .build(),
    // ...
    Message.builder()
        .setNotification(Notification.builder()
            .setTitle("test8888")
            .setBody("test8888 999999999")
            .build())
        .setToken(registrationToken2)
        .build()
);

BatchResponse response = FirebaseMessaging.getInstance().sendEach(messages);

다음과 같이 작성한 서비스 함수를 Postman으로 호출하여 테스트함.

 

다음과 같이 정상적으로 테스트용 안드로이드앱에서 알림을 수신한 것을 확인

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유