JAVA (SPRING)

[JAVA] Spring WebFlux 에서 FCM(FireBase Cloud Messaging) 보내기

박현국 2025. 4. 17. 09:24

목차

  1. 비동기 메시징
  2. FCM 에서의 pub/sub
  3. 포그라운드와 백그라운드에서의 메시징(알람 + 데이터) 구조
  4. 기능 구현

 

 

비동기 메세징

비동기

  • 메시지를 보낸 순간 바로 응답을 기다리지 않아도 됨.
  • FCM도 비동기 방식처리인데, 서버가 메시지를 큐(Queue) 에 저장해두고, 적절한 시점에 클라이언트에 전달하는 것

동기 방식과의 차이

동기 방식 요청 → 서버 → 응답올때까지 기다림
비동기 방식 서버에서 FCM 으로 메시지 발송 → 기다리지 않음 → 클라이언트는 나중에 수신

 

 

FCM 에서의 Pub/Sub

Publisher(Pub) : 메시지 전송

Subscriber : 특정 Topic 을 구독하고 있다가 메시지 수신

Pub/Sub System : Publisher 에게 메시지를 받아서 Subscriber 에게 전달하는 중간 다리 역할

 

동작 순서

  1. 서버가 특정 EndPoint 에 대한 Topic 에 메시지를 발행
  2. 해당 Topic 을 구독하고 있는 Subscriber 을 찾아 메시지를 보관 또는 즉시 전달하여 클라이언트(Subscriber) 가 비동기로 수신

특징 

  1. 비동기 처리 : Subscriber 과 Publisher 가 동시에 실행되지 않아도 됨
  2. 느슨한 결합 : Subscrebier 과 Publisher 가 서로를 몰라도 됨
  3. 확장성 : 구독자 수가 많아져도 구조적으로 유연하 하나의 메시지를 여러 구독자에게 전달 가능

 

 

Foreground 와 Background 에서의 메시징(알림 + 데이터) 구조

포그라운드 상태

  • 사용자가 앱을 켜고 현재 화면에서 사용 중인 상태 (ex. 인스타그램을 실행해서 피드를 보고 있을 때)
  • 알림은 시스템이 자동으로 표시하지 않으며 앱에서 직접 처리해야함
  • 데이터(메시지 내용)는 바로 수신 가능

백그라운드 상태

  • 앱이 켜져는 있지만 화면에 보이지 않는 상태
    * 홈 버튼 눌렀을때
    * 다른 앱으로 전환했을때
    * 잠금 화면 상태일때 등
  • 알림은 시스템이 자동으로 알림을 띄어줌
  • 데이터는 앱이 완전히 종료되었으면 수신 안될 수 있음

 

구현

 

구현 방법을 설명하기 전, FCM 에서 메시징 요청 방법은

 1. HTTP v1 전송 요청

 2. Admin SDK 

이 두가지 방법이 있는데, 특징 차이를 살펴 보면

방식 HTTP V1 API Admin SDK
인증 방식 REST API (직접 HTTP 요청) 공식 SDK 사용
메시지 구조 직접 JSON 구성 SDK 메서드로 구성
유연성 높음 일부 최신 기능 미지원 및 커스텀 메시징이 필요한 경우 제한
설정 인증 토큰 발급과 헤더 구성 등을 직접 처리해야함 토큰 관리, 인증 등이 자동화되어 있어, 코드가 간결함.


여기서 HTTP V1 방식은 최근에 나온 방식으로 앞서, 설명했다시피 유연성은 높지만, 설정해야할 요소들이 있다. 하지만 미리 설정해두고 나면 향 후 다른 최신 기능이나, 커스텀 메시징을 사용할때 매우 편리할 것이다.
그래서 필자는 HTTP V1 API 방식을 사용했다.

1. Firebase 콘솔에서 새프로젝트에서 Messaging 새 프로젝트 생성

 

2. FCM api 를 콜할 uri 경로 설정 (API 가이드에서 확인 가능) 

String apiUri = "https://fcm.googleapis.com/v1/projects/" + PROJECTID + "/messages:send";

여기서 ProjectId 는 FCM 콘솔 -> 프로젝트 설정에서 확인 가능

 

3. FCM 에서 사용할 프로젝트에 대한 Secret Key 에 대한 정보와 FCM API V1 에서 사용할 Scopes 경로를 설정해준다. 

private static String getAccessToken() throws IOException {
    InputStream fis = new ClassPathResource(FIREBASE_CONFIG_URL).getInputStream();

    GoogleCredentials googleCredentials = GoogleCredentials
            .fromStream(fis)
            .createScoped(List.of(SCOPES));
    googleCredentials.refresh();
    return googleCredentials.getAccessToken().getTokenValue();
}

여기서 Secret Key 는 본인 프로젝트에서 발급 받으면 되고, SCOPES 는 https://developers.google.com/identity/protocols/oauth2/scopes

 

Google API의 OAuth 2.0 범위  |  Authorization  |  Google for Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 Google API의 OAuth 2.0 범위 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 문서에서는 필

developers.google.com

해당 링크에서 사용할 범위를 찾아서 해당 URI 링크를 넣어주면 된다. 필자는 FCM 관련 URI 를 사용하였다.
(구글에 있는 이유는 Firebase 는 구글에서 관리하는 개발 플랫폼이기 때문.)

 

4. 2 에서 설정한 Uri 경로와 요청 헤더를 FCM API 가이드에 맞게 설정하여, JSON 직렬화 후 양식에 맞게 요청한다.

public CMap sendMessage(CParam param) throws IOException {
    String apiUri = "https://fcm.googleapis.com/v1/projects/" + PROJECTID + "/messages:send";

    // http 헤더
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.setBearerAuth(getAccessToken());

    String json = CMapConverter.toJson(param);

    HttpEntity<String> request = new HttpEntity<>(json, headers);
    ResponseEntity<CResult> response = restTemplate.exchange(apiUri, HttpMethod.POST, request, CResult.class);

    return response.getBody();
}

 

 

5. 요청

{
  "message": {
    "topic": "all",
    "notification": {
      "title": "테스트",
      "body": "테스트입니당."
    },
    "data": {
      "story_id": "ㅎㅎ"
    }
  }
}

 

여기서 "topic" : all 은 모든 구독자에게 메세지를 보낸다는 의미. 만약 특정 사용자에게 보내고 싶다면 해당 사용자의 토큰 값을 알고 있어야 한다.

 

이렇게 API 가이드 양식에 맞게 요청을 하게 되면, Topic 을 구독한 subscriber 에게 정상적으로 문자가 오는 걸 확인할 수 있다.

 

 

 

 

 

'JAVA (SPRING)' 카테고리의 다른 글

[JAVA] 카카오 알림톡 API 구현 (Ncloud 환경)  (1) 2025.04.15
[JAVA] ObjectUtils, CollectionUtils  (3) 2025.03.06
[JAVA] LinkedHashSet  (0) 2025.03.05
IP-PBX 와 IP-Centrex 방식의 차이  (0) 2025.02.18
OSI 7 Layer, TCP/IP 4Layer  (0) 2025.02.17