
- FCM의 Data Message 구조로 푸시 알림을 보내고 받습니다.
- 클라이언트는 onMessageReceived를 통해 푸시 알림을 원하는 아이콘, 원하는 데이터를 넣어 커스텀할 수 있습니다.
- 백엔드는 토큰과 Map 형태의 data 두 개의 필드로 메시지를 구성하여 data에 필요한 데이터를 넣어서 보낼 수 있습니다.
구현 항목
FCMConfig.javaFCMData.javaFCMSend.java먼저 포함해야 하는 정보에 맞게 FCMData의 인스턴스 생성 메서드를 선택해 FCMData를 생성합니다.
이후 FCMService의 sendFCM에 해당 FCMData와 보내야 하는 기기의 token 값을 넣어 호출합니다.
그럼 기본적인 리턴값인 success / error를 받습니다.
public String sendPushNotification() {
try {
FCMData matchFCM = FCMData.instanceOfMatchFCM("123", "Hello, Match!", "2022-06-15T12:34:56");
fcmService.sendFCM("abcd", matchFCM);
return "success";
} catch (Exception e) {
return "Error";
}
}
firebasApp 관련 설정을 해줍니다.
@Configuration,@Bean으로 init을 수행하고, messaging할 수 있도록 메서드를 추가합니다.
@Configuration
public class FCMConfig {
@Bean
public FirebaseApp initializeFirebase() throws IOException {
InputStream serviceAccount = getClass().getClassLoader().getResourceAsStream("serviceAccountKey.json");
if (serviceAccount == null) {
throw new IOException("File not found: serviceAccountKey.json");
}
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
return FirebaseApp.initializeApp(options);
}
@Bean
public FirebaseMessaging firebaseMessaging(FirebaseApp firebaseApp) {
return FirebaseMessaging.getInstance(firebaseApp);
}
}
우리가 클라이언트로 보내줘야 하는 FCM은 알림 종류에 따라 두가지로 나뉩니다.
채팅방 룸 id, 메시지 타입이 필요없는 경우와 필요한 경우죠!
FCM의 Data Message 타입을 사용했습니다.
클라이언트 수신 데이터 예시
{
"sentTime":1718814481730,
"data":
{
"senderId":"123",
"message":"Hello, Match!",
"createdAt":"2022-06-15T12:34:56",
"additionalData":
{\"fcmType\":\"match\"}"},
"messageId":"0:1718814481739128%c6e5ad3bf9fd7ecd",
"ttl":2419200,
"from":"929499276402"
}
그래서 기본적으로 FCMData class는 다음의 멤버변수를 가집니다.
private final String senderId;
private final String message;
private final String createdAt;
private final Map<String, String> additionalData; //message 별로 추가 필요한 데이터 넣어주기
추가로 필요한 멤버변수는 additionalData에 map key-value로 저장했습니다. 그리고 최종적으로 FCM으로 보낼 때는 Gson을 이용해 json + String 으로 변환해줬습니다!
@Getter
public class FCMData {
private final String senderId;
private final String message;
private final String createdAt;
private final Map<String, String> additionalData; //message 별로 추가 필요한 데이터 넣어주기
private FCMData(String senderId, String message, String createdAt) {
this.senderId = senderId;
this.message = message;
this.createdAt = createdAt;
this.additionalData = new HashMap<>();
this.additionalData.put("fcmType", "match");
}
private FCMData(String senderId, String message, String createdAt, String chatRoomId, String messageType) {
this.senderId = senderId;
this.message = message;
this.createdAt = createdAt;
this.additionalData = new HashMap<>();
this.additionalData.put("fcmType", "chat");
this.additionalData.put("chatRoomId", chatRoomId);
this.additionalData.put("messageType", messageType);
}
/*인연 FCMData 생성자*/
public static FCMData instanceOfMatchFCM(String senderId, String message, String createdAt) {
return new FCMData(senderId, message, createdAt);
}
/*ChatFCMData 생성자*/
public static FCMData instanceOfChatFCM(String senderId, String message, String createdAt, String chatRoomId, String messageType) {
return new FCMData(senderId, message, createdAt, chatRoomId, messageType);
}
public Map<String, String> toMap() {
Gson gson = new Gson();
Map<String, String> map = new HashMap<>();
map.put("senderId", this.senderId);
map.put("message", this.message);
map.put("createdAt", this.createdAt);
map.put("additionalData", gson.toJson(this.additionalData));
return map;
}
}
package com.jungle.chalnaServer.infra.fcm;
import com.jungle.chalnaServer.infra.fcm.dto.FCMData;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class FCMDataTest {
@Test
void TestMatchFCM(){
FCMData matchFCM = FCMData.instanceOfMatchFCM("123", "Hello, Match!", "2022-06-15T12:34:56");
assertEquals("123", matchFCM.getSenderId());
assertEquals("Hello, Match!", matchFCM.getMessage());
assertEquals("2022-06-15T12:34:56", matchFCM.getCreatedAt());
// additionalData 확인
Map<String, String> additionalData = matchFCM.getAdditionalData();
assertNotNull(additionalData);
assertEquals("match", additionalData.get("fcmType"));
}
@Test
void testInstanceOfChatFCM() {
// 생성자 테스트
FCMData chatFCM = FCMData.instanceOfChatFCM("456", "Hello, Chat!", "2022-06-16T12:34:56", "roomId123", "text");
assertEquals("456", chatFCM.getSenderId());
assertEquals("Hello, Chat!", chatFCM.getMessage());
assertEquals("2022-06-16T12:34:56", chatFCM.getCreatedAt());
// additionalData 확인
Map<String, String> additionalData = chatFCM.getAdditionalData();
assertNotNull(additionalData);
assertEquals("chat", additionalData.get("fcmType"));
assertEquals("roomId123", additionalData.get("chatRoomId"));
assertEquals("text", additionalData.get("messageType"));
}
}
FCM 푸시 알림을 보내야 할 때 호출해서 사용합니다.
@Service
public class FCMService {
public void sendFCM(String fcmToken, FCMData fcmData) throws Exception{
Message.Builder messageBuilder = Message.builder()
.setToken(fcmToken)
.putAllData(fcmData.toMap());
Message message = messageBuilder.build();
String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message: " + response);
}
}
FirebaseMessagingService를 상속받고, onMessageReceived를 sendNotification 등과 같은 메서드를
푸시 알림 클릭 시 해당 채팅방 화면 혹은 알림 센터 화면으로 이동하는 경우 처리 필요
npx react-native link react-native-screens
npx react-native link react-native-safe-area-context
메시지 종류
Notification Message
Data Message
Notification Message는 주로 사용자에게 표시되는 알림을 위해 설계된 메시지 유형입니다. 이 메시지는 기본적으로 시스템 트레이에 표시되며, 사용자가 알림을 클릭하면 애플리케이션이 열립니다.
onMessageReceived 메서드를 통해 수신되고, 개발자가 커스터마이징하여 처리할 수 있습니다.Data Message는 개발자에게 완전한 제어권을 부여하는 메시지 유형입니다. 이 메시지는 애플리케이션에 전달되어 개발자가 직접 처리할 수 있으며, 사용자에게 직접적인 알림을 표시할지 여부를 결정할 수 있습니다.
onMessageReceived 메서드를 통해 수신되며, 개발자가 메시지를 커스터마이징하여 처리할 수 있습니다.FCM은 또한 Notification Message와 Data Message를 결합한 형태의 메시지를 지원합니다. 이 유형의 메시지는 Notification Message의 장점과 Data Message의 유연성을 모두 제공합니다.
onMessageReceived 메서드를 통해 수신되고, 개발자가 커스터마이징하여 처리할 수 있습니다.