Java/라이브러리 활용

[Java] Spring Boot Firebase Cloud Message(FCM) 푸시 메시지 API 구축 -1 : firebase-admin

adjh54 2024. 2. 21. 21:38
728x170
해당 글에서는 Spring Boot API에서 FCM으로 통신하여 FCM Push Message 전송 API 구축을 하는 방법에 대해 알아봅니다.


 
 

 

💡 [참고] FCM 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 링크
FCM Spirng Boot API 구성 : 단순 전송 기능 https://adjh54.tistory.com/432
FCM React Native 초기 환경 설정 https://adjh54.tistory.com/431
FCM React Native Notifee 메시지 수신 구성 https://adjh54.tistory.com/433
FCM React Natiive Notifee 메시지 전송 및 예약 발송 구성 https://adjh54.tistory.com/434
FCM Spring Boot API 구성: 예약 발송 기능 https://adjh54.tistory.com/438
FCM Github Repository : Spring Boot Firebase Cloud API https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-fcm
FCM 스케줄러 Github Repository : Spring Boot Quarts https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-scheudler


 

 

 

 

 

1) Firebase 환경설정


💡 Firebase 환경설정

- 아래의 공식 사이트에 접근하여 환경설정을 수행합니다.
 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

 

1. Firebase 사이트 → 프로젝트 개요 → 프로젝트 설정 버튼을 누릅니다.


 
 

2. 프로젝트 설정 → 서비스 계정 → 자바 선택 → ‘새 비공개 키 생성’ 버튼을 누릅니다.


 
 

3. ‘키 생성’ 버튼을 누릅니다.


 
 
 

4. 생성된 json 파일을 프로젝트 resources/firebase 디렉터리를 생성하고 경로에 옮겨둡니다.


 
 
 

2) Spring Boot 환경설정


1. 전체 데이터 흐름


💡 전체 데이터 흐름

1. Spring Boot FcmController로 API를 호출합니다.
- HTTP Method POST 형태로 구성한 ‘api/v1/fcm/send’로 FCM 토큰(디바이스에서 발급받은 토큰), 푸시 메시지 제목, 내용을 파라미터로 전달합니다.

2. FcmServiceImple
- FCM과 통신하는 URL로 해당 메시지 정보를 전달합니다. 해당 통신 방법은 RestTemplate을 이용하여 동기식 전송을 수행하였습니다.

3. FCM Service
- FCM Service에서는 전달받은 토큰에 따라 디바이스로 푸시 메시지를 전송하고 종료합니다.

 
 

2. 의존성 주입


dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'   // Spring Boot Web
    implementation 'com.google.firebase:firebase-admin:9.2.0'           // Google Firebase Admin
    implementation 'com.fasterxml.jackson.core:jackson-core:2.16.1'     // Jackson Data Bind

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

 
 

[ 더 알아보기 ]

💡firebase-admin

- Java에서 사용할 수 있는 Firebase Admin SDK를 의미합니다. 이를 이용하면 Firebase Authentication, Cloud Firestore, Cloud Messaging (FCM), Realtime Database 등의 Firebase 서비스를 서버에서 직접 액세스 하고 관리할 수 있습니다.

 
 

3. DTO


💡 FcmSendDto

- 모바일에서 전달받은 객체를 매핑하는 DTO입니다
필드 값 설명
token - 디바이스 기기에서 발급받은 FCM 토큰 값을 의미합니다. 이는 디바이스로 전송하기 위해서는 전송 주체가 되는 디바이스 기기가 필요하기에 해당 내용이 들어갑니다.
title - 디바이스 기기로 전송하려는 푸시메시지의 제목
body - 디바이스 기기로 전송하려는 푸시메시지의 내용

 

/**
 * 모바일에서 전달받은 객체
 *
 * @author : lee
 * @fileName : FcmSendDto
 * @since : 2/21/24
 */
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FcmSendDto {
    private String token;

    private String title;

    private String body;

    @Builder(toBuilder = true)
    public FcmSendDto(String token, String title, String body) {
        this.token = token;
        this.title = title;
        this.body = body;
    }
}

 
 

💡 FcmMessageDto

- FCM에 실제 전송될 데이터의 DTO입니다.
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

/**
 * FCM 전송 Format DTO
 *
 * @author : lee
 * @fileName : FcmMessageDto
 * @since : 2/21/24
 */
@Getter
@Builder
public class FcmMessageDto {
    private boolean validateOnly;           
    private FcmMessageDto.Message message;

    @Builder
    @AllArgsConstructor
    @Getter
    public static class Message {
        private FcmMessageDto.Notification notification;
        private String token;
    }

    @Builder
    @AllArgsConstructor
    @Getter
    public static class Notification {
        private String title;
        private String body;
        private String image;
    }
}

 

 

💡 [참고] 위에 객체는 아래의 형태를 갖추기 위해 구성하였습니다.

- 기본적으로 메시지 단건 전송을 위해서는 아래와 같은 정보를 포함하여서 전송하여야 합니다.
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
   "message":{
      "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
      "notification":{
        "body":"This is an FCM notification message!",
        "title":"FCM Message"
      }
   }
}
 

앱 서버 전송 요청 작성  |  Firebase 클라우드 메시징

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

firebase.google.com

 

💡 [참고] 위에 데이터의 경우는 아래의 공식 사이트의 요청 데이터(Request Body) 형태로 구성한 DTO입니다.

- 해당 내용이 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
 

Method: projects.messages.send  |  Firebase Cloud Messaging REST API

 

firebase.google.com

 

REST Resource: projects.messages  |  Firebase Cloud Messaging REST API

 

firebase.google.com

 

 

4. FcmController


 💡 FcmController

- 모바일로부터 사용자 FCM 토큰, 메시지 제목, 내용을 받아서 서비스를 처리하는 Controller를 구성하였습니다.
/**
 * FCM 관리하는 Controller
 *
 * @author : lee
 * @fileName : FcmController
 * @since : 2/21/24
 */

@Slf4j
@RestController
@RequestMapping("/api/v1/fcm")
public class FcmController {

    private final FcmService fcmService;

    public FcmController(FcmService fcmService) {
        this.fcmService = fcmService;
    }

    @PostMapping("/send")
    public ResponseEntity<ApiResponseWrapper<Object>> pushMessage(@RequestBody @Validated FcmSendDto fcmSendDto) throws IOException {
        log.debug("[+] 푸시 메시지를 전송합니다. ");
        int result = fcmService.sendMessageTo(fcmSendDto);

        ApiResponseWrapper<Object> arw = ApiResponseWrapper
                .builder()
                .result(result)
                .resultCode(SuccessCode.SELECT_SUCCESS.getStatus())
                .resultMsg(SuccessCode.SELECT_SUCCESS.getMessage())
                .build();
        return new ResponseEntity<>(arw, HttpStatus.OK);
    }
}

 
 
 

5. Service


 💡 Service

- FCM Service를 처리하는 인터페이스입니다. sendMessageTo() 메서드를 추가하였습니다.
import java.io.IOException;

/**
 * FCM SERVICE
 *
 * @author : lee
 * @fileName : PushMessageService
 * @since : 2/21/24
 */
@Service
public interface FcmService {

    int sendMessageTo(FcmSendDto fcmSendDto) throws IOException;

}

 
 

6. ServiceImpl


💡 ServiceImpl

- FCM과 통신하여 모바일에서 받은 정보를 기반으로 메시지를 전송합니다.
메서드 설명
sendMessageTo 메시지를 구성하고 토큰을 받아서 FCM으로 메시지 처리를 수행하는 비즈니스 로직
getAccessToken Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다.
makeMessage FCM 전송 정보를 기반으로 메시지를 구성합니다. (Object -> String)

 

💡 해당 부분 중 API_URL는 https://fcm.googleapis.com/v1/projects/><프로젝트 명>/messages:send 으로 구성이 되어 있습니다.
- Firebase에 접근하여 프로젝트 설정 - 프로젝트 ID 부분을 입력하시면 됩니다.

 

💡 [참고] 프로젝트 명은 어디서 확인이 가능할까?
- Firebase에 접근하여 프로젝트 설정 - 프로젝트 ID 부분을 입력하시면 됩니다.

 
 

[ 더 알아보기 ]

💡 Object Mapper에서 오류가 납니다.

- Jackson 라이브러리를 이용하여 직렬화를 수행하였습니다.
- 사용방법이 궁금하시면 아래의 글을 참고하시면 도움이 됩니다. https://adjh54.tistory.com/375
 

[Java] Spring Boot 환경에서 Jackson 모듈 활용하기 : JSON 파싱, 직렬화, 역 직렬화, JSON 파일 읽어오기/

해당 글에서는 Spring Boot 환경에서 Jackson 라이브러리를 활용하는 방법에 대해서 알아봅니다. 1) Jackson 💡 Jackson - JSON 데이터 작업을 하기 위한 인기 있는 Java 라이브러리입니다. - JSON 파일을 읽거

adjh54.tistory.com

 

/**
 * FCM 서비스를 처리하는 구현체
 *
 * @author : FcmServiceImpl
 * @fileName : PushMessageServiceImpl
 * @since : 2/21/24
 */
@Service
public class FcmServiceImpl implements FcmService {

    /**
     * 푸시 메시지 처리를 수행하는 비즈니스 로직
     *
     * @param fcmSendDto 모바일에서 전달받은 Object
     * @return 성공(1), 실패(0)
     */
    @Override
    public int sendMessageTo(FcmSendDto fcmSendDto) throws IOException {

        String message = makeMessage(fcmSendDto);
        RestTemplate restTemplate = new RestTemplate();
       /**
         * 추가된 사항 : RestTemplate 이용중 클라이언트의 한글 깨짐 증상에 대한 수정
         * @refernece : https://stackoverflow.com/questions/29392422/how-can-i-tell-resttemplate-to-post-with-utf-8-encoding
         */
        restTemplate.getMessageConverters()
                .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + getAccessToken());

        HttpEntity entity = new HttpEntity<>(message, headers);

        String API_URL = "<https://fcm.googleapis.com/v1/projects/adjh54-a0189/messages:send>";
        ResponseEntity response = restTemplate.exchange(API_URL, HttpMethod.POST, entity, String.class);

        System.out.println(response.getStatusCode());

        return response.getStatusCode() == HttpStatus.OK ? 1 : 0;
    }

    /**
     * Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다.
     *
     * @return Bearer token
     */
    private String getAccessToken() throws IOException {
        String firebaseConfigPath = "firebase/adjh54-dev-firebase-key.json";

        GoogleCredentials googleCredentials = GoogleCredentials
                .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())
                .createScoped(List.of("<https://www.googleapis.com/auth/cloud-platform>"));

        googleCredentials.refreshIfExpired();
        return googleCredentials.getAccessToken().getTokenValue();
    }

    /**
     * FCM 전송 정보를 기반으로 메시지를 구성합니다. (Object -> String)
     *
     * @param fcmSendDto FcmSendDto
     * @return String
     */
    private String makeMessage(FcmSendDto fcmSendDto) throws JsonProcessingException {

        ObjectMapper om = new ObjectMapper();
        FcmMessageDto fcmMessageDto = FcmMessageDto.builder()
                .message(FcmMessageDto.Message.builder()
                        .token(fcmSendDto.getToken())
                        .notification(FcmMessageDto.Notification.builder()
                                .title(fcmSendDto.getTitle())
                                .body(fcmSendDto.getBody())
                                .image(null)
                                .build()
                        ).build()).validateOnly(false).build();

        return om.writeValueAsString(fcmMessageDto);
    }
}

 
 

💡 [참고] FCM 전송 API 문서
 

Firebase Cloud Messaging API  |  Firebase Cloud Messaging REST API

Firebase Cloud Messaging (FCM) is a cross-platform messaging solution that lets you reliably send messages at no cost.

firebase.google.com

 
 

3) 구성 테스트하기


 

1. API 호출


💡 API 호출

- [POST] http://localhost:8000/api/v1/fcm/send 엔드포인트로 JSON 데이터로 token, title, body 값을 포함하여 전송을 하여 성공적으로 결과코드로 1을 전달받았습니다.

 

 

💡 [참고] API로 호출하는 값 중 Token 값은 어떻게 확인이 가능할까?

- 제가 구성한 모바일 환경인 React Native에서는 아래와 같이 FCM Token을 발급받습니다.
- 아래와 같이 @react-native-firebase/messaging 라이브러리 내에서 getToken() 메서드를 호출하면 디바이스의 FCM 토큰을 발급받을 수 있습니다.
import messaging from '@react-native-firebase/messaging';

/**
 * INIT APP
 * @returns
 */
const App = () => {

	useEffect(() => {
		getFcmToken();
		subscribe();
	}, []);

	/**
	 * FCM 토큰을 받습니다.
	 */
	const getFcmToken = async () => {
		const fcmToken = await messaging().getToken();
		console.log('[+] FCM Token :: ', fcmToken);
	};
}

 

💡 위 메서드를 호출하면 아래와 같이 FCM 토큰 값이 발급되는데 해당 값을 JSON 객체에 포함하여 전송합니다.

 

 


 

2. 모바일 확인


 
 

 

💡 [참고] 모바일 환경에서 테스트를 해보고 싶으시면 React Native 기반의 아래의 글들을 참고하시면 도움이 됩니다.
 

[RN] Firebase Cloud Message(FCM) 이해 및 환경설정, 간단 테스트: Android

해당 글에서는 Firebase Cloud Message(FCM)에 대해 이해하고 환경을 구성하며 메시지를 수신하는 형태를 테스트하는 환경 구성 방법에 대해 이해를 돕기 위해 작성하였습니다. 1) FCM(Firebase Cloud Message)

adjh54.tistory.com

 

[RN] React Native Firebase Cloud Message(FCM) 활용하여 푸시 메시지 수신 구성 : Notifee

해당 글에서는 실제 FCM을 이용하여 메시지를 전송하고 수신하는 방법에 대한 활용방법에 대해 알아봅니다. 💡 [참고] FCM 최초 환경 구성 과정 및 Spring Boot 기반 API 구성에 대해 궁금하시면 아래

adjh54.tistory.com

 

 

 

 

💡 [참고] FCM의 예약 발송 기능에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
 

[Java] Spring boot Firebase Cloud Message(FCM) 구축 -2 : 예약 발송 Scheduler(Spring Boot Quartz)

해당 글에서는 Spring Boot 환경에서 FCM 예약 발송 기능을 Spring Boot Quartz(Scheduler)를 이용하여 구성하는 방법에 대해서 알아봅니다. 💡 [참고] FCM 관련해서 구성 내용에 대해 궁금하시면 아래의 글을

adjh54.tistory.com

 

 

 

💡[참고] 해당 구성한 내용의 Repository입니다.
 

blog-codes/spring-boot-fcm at main · adjh54ir/blog-codes

Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.

github.com

 

 


오늘도 감사합니다 😀
 
 
 
 

그리드형