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 패키지에 포함되어 있다. 응답 결과를 얻는 과정은 다음과 같다.FirebaseMessaging.getInstance()를 통해 인스턴스를 획득한다. 메소드마다 반환 타입이 다르긴 하지만 메시지를 발송하면 BatchResponse로 반환하는 sendEach를 실행한다고 가정한다. 메시지를 발송 후 그 결과값인 BatchResponse에서 getResponses
를 실행해서 SendResponse 목록을 가져온다. 결과에 따라 처리를 진행한다. (※ SendResponse를 res라는 이름의 변수로 선언했다고 가정)res.isSuccessful()메시지 발송의 성공 여부를 반환한다. (boolean) res.getException().getMessage()발송 실패 시 그 원인에 대한 메시지를 반환한다. (String) 출처