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들이 더 이상 사용될 수 없게 되었다. - 그래서 지금은
sendEach
같은 메소드를 통해서 메시지 1건당 요청 1번으로 보내야 한다.- 이것때문에 서버쪽에서 리소스 부담이 많아져서 커뮤니티에서 불만이 많았었고, 지금도 많다.
- 제목이랑 내용 등이 같으면 묶어서 토큰만 리스트로 붙여서 발송하는 방식이 있긴하다.
- 다만 모양만 일괄방송이고 내부 로직을 까보면 똑같이 반복 발송이다.
sendEach
도 기존과 동일하게 1번 실행할 때 최대 500개의 메시지를 인자로 전달할 수 있다.
- 전송 결과는 SendResponse라는 클래스에 담겨서 반환된다.
- SendResponse는 com.google.firebase.messaging 패키지에 포함되어 있다.
- 응답 결과를 얻는 과정은 다음과 같다.
- FirebaseMessaging.getInstance()를 통해 인스턴스를 획득한다.
- 메소드마다 반환 타입이 다르긴 하지만 메시지를 발송하면 BatchResponse로 반환하는 sendEach를 실행한다고 가정한다.
- 메시지를 발송 후 그 결과값인 BatchResponse에서
getResponses
를 실행해서 SendResponse 목록을 가져온다. - 결과에 따라 처리를 진행한다. (※ SendResponse를 res라는 이름의 변수로 선언했다고 가정)
- res.isSuccessful()
- 메시지 발송의 성공 여부를 반환한다. (boolean)
- res.getException().getMessage()
- 발송 실패 시 그 원인에 대한 메시지를 반환한다. (String)
- res.isSuccessful()