FCM
포스트
취소

FCM

FCM이란?

  • 플랫폼에 종속되지 않고 푸시 메시지를 전송할 수 있는 교차 플랫폼 메시징 솔루션
  • Firebase Cloud Messaging

왜 사용할까?

  • 플랫폼에 종속되지 않는다.
    • 플랫폼 환경별로 개발해야하는 불현함을 해소한다.
    • Android : GCM (Google Cloud Messaging)
    • iOS : APNS (Apple Push Notification service)
  • 사용되는 리소스가 감소한다.
    • 실시간으로 푸시 메시지를 받으려면 사용자는 항상 서버에 접속해있어야 한다.
      • 이는 사용자 기기의 배터리 및 네트워크 리소스를 크게 낭비한다.
    • 클라우드 메시징 서버를 중간다리로 둔다면 낮은 배터리와 네트워크 리소스만 사용해도 된다.

환경설정

의존성 추가

//build.gradle
implementation 'com.google.firebase:firebase-admin:9.3.0'

FirebaseApp에 대한 인스턴스 생성

  • 만약에 패키징이 WAR인 경우에는 Resource 클래스를 통해 파일 정보를 불러들여도 된다.
  • 다만 패키징이 JAR일 경우에는 경로 문제로 인하여 ResourceLoader를 통해 파일 정보를 불러들여야 한다.
@Slf4j
@Configuration
public class FirebaseConfig {
    @Autowired
    private ResourceLoader resourceLoader;

    private FirebaseApp firebaseApp;

    //@Value("classpath:firebase_service_key.json")
    //private Resource firebaseServiceKey;

    @PostConstruct
    public FirebaseApp initFirebase() {
        try {
            // Service Account를 이용하여 Fireabse Admin SDK 초기화
            /*FileInputStream serviceAccount = new FileInputStream(firebaseServiceKey.getFile());
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                    .build();
            firebaseApp = FirebaseApp.initializeApp(options);*/

            InputStream serviceAccount = resourceLoader.getResource("classpath:firebase_service_key.json").getInputStream();
            FirebaseOptions options = FirebaseOptions.builder()
                    .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                    .build();
            firebaseApp = FirebaseApp.initializeApp(options);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("[FirebaseConfig] " + e.getMessage());
        }

        if(firebaseApp != null){
            log.info("[FirebaseConfig] initialize : " + firebaseApp.getName());
        } else {
            log.info("[FirebaseConfig] firebaseApp is null");
        }

        return firebaseApp;
    }

    @Bean
    public FirebaseMessaging initFirebaseMessaging() {
        FirebaseMessaging instance = null;
        if(firebaseApp != null){
            instance = FirebaseMessaging.getInstance(firebaseApp);
        }
        return instance;
    }
}

데이터를 처리할 객체

  • 데이터를 처리하기 위한 객체를 생성한다.
  • 푸시를 보내는 다양한 방법이 있어서 파라미터도 많아져야 하지만
    간단하게만 보낼꺼면 처리용 객체도 간단하게 만들면 된다.
@Getter
@Setter
public class FcmLogVO {
    private String title; //제목
    private String body; //내용
    private String target; //송신 대상
    private String action; //전달할 데이터
}

메시지 가공하기

  • ADMIN SDK를 통해 메시지를 전달할 때는 다양한 객체를 사용한다.
  • 그 중에서 많이 쓰이는 건 Message와 Notification이다.
  • Message와 Notification은 com.google.firebase.messaging 패키지에 포함되어 있다.
  • 디테일하게 사용하고 싶을 때는 직접 소스를 까서 확인하도록 하고 지금은 간단하게만 알아보자.
Message
.builder()
    .setNotification(
        Notification
        .builder()
            .setTitle("제목")
            .setBody("내용")
        .build()
    )
    .setToken("토큰")
    .putData("키", )
.build()
  • setTitle
    • 제목을 설정한다.
  • setBody
    • 내용을 설정한다.
  • setToken
    • 토큰을 설정한다.
    • 토큰은 Firebase에서 각 기기에 대해서 고유하게 발급하는 고유값을 의미한다.
    • Firebase는 토큰을 통해 각 기기에 메시지를 발송할 수 있다.
    • 토큰은 각 플랫폼마다 획득하는 방법이 조금씩 다르다.
  • pudData
    • 데이터를 설정한다.
    • 고유한 키와 해당 키에 매핑할 데이터를 설정한다.
    • 데이터는 여러 건 설정할 수 있다.
    • 데이터는 문자열 타입만 설정할 수 있다.

메시지 전송하기

  • 기존에는 sendAll이라고 해서 최대 500개의 메시지를 HTTP 요청 딱 1번만에 보낼 수 있었다.
  • 그런데 2024년 여름에 sendAll같은 배치(= 일괄전송) API들이 더 이상 사용될 수 없게 되었다.
    • 현재 deprecated된 메소드들을 써서 호출하면 오류가 발생한다.
    • 정확한 오류 내용은 기억은 안 나는데 ADMIN SDK 내부 소스에 보면 오류가 발생했을 때 해당 오류가 뭔지 체크하기 위한 enum이 하나 있는데,
      deprecated된 API를 호출하면 그 enum에 포함되지 않은 문자열을 반환해서 그걸 enum으로 변환하려다가 오류가 났었다.
    • 참고
  • 그래서 지금은 sendEach같은 메소드를 통해서 메시지 1건당 요청 1번으로 보내야 한다.
    • 이것때문에 서버쪽에서 리소스 부담이 많아져서 커뮤니티에서 불만이 많았었고, 지금도 많다.
    • 제목이랑 내용 등이 같으면 묶어서 토큰만 리스트로 붙여서 발송하는 방식이 있긴하다.
    • 다만 모양만 일괄방송이고 내부 로직을 까보면 똑같이 반복 발송이다.
    • sendEach도 기존과 동일하게 1번 실행할 때 최대 500개의 메시지를 인자로 전달할 수 있다.
  • 전송 결과는 SendResponse라는 클래스에 담겨서 반환된다.
    • SendResponse는 com.google.firebase.messaging 패키지에 포함되어 있다.
    • 응답 결과를 얻는 과정은 다음과 같다.
      1. FirebaseMessaging.getInstance()를 통해 인스턴스를 획득한다.
      2. 메소드마다 반환 타입이 다르긴 하지만 메시지를 발송하면 BatchResponse로 반환하는 sendEach를 실행한다고 가정한다.
      3. 메시지를 발송 후 그 결과값인 BatchResponse에서 getResponses를 실행해서 SendResponse 목록을 가져온다.
      4. 결과에 따라 처리를 진행한다. (※ SendResponse를 res라는 이름의 변수로 선언했다고 가정)
        • res.isSuccessful()
          • 메시지 발송의 성공 여부를 반환한다. (boolean)
        • res.getException().getMessage()
          • 발송 실패 시 그 원인에 대한 메시지를 반환한다. (String)

출처

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.