Firebase란 Google이 제공하는 클라우드 기반의 다양한 서비스를 제공하는 플랫폼이다. 웹과 모바일 앱 개발에 필요한 여러 가지 기능을 제공하여 개발자들이 애플리케이션을 더 쉽고 효율적으로 만들 수 있도록 도와준다.
프로젝트 진행 중에 상대가 쪽지를 보내면 타 어플리케이션처럼 핸드폰 화면에 알림이 뜨게 하는 작업을 맡게되었다. 그래서 전에 Firebase를 이용하여 소셜로그인을 구현할 때 봤었던 push 알람이 기억이 났고 같이 개발을 진행하던 클라이언트 개발자와 소통 후 Firebase push 알람을 구현하였다.
Firebase cloud Messaging(이하 FCM) 토큰을 사용하여 특정 기기에 알람을 보낼 수 있다. 이건 IOS와 Android에 따라 클라이언트의 코드는 달라지는 것 같은데(공식 문서를 참고) 서버에서는 FCM을 통해 푸쉬 알람을 보내는 로직은 비슷한 것 같다.
클라이언트에서 FCM을 보내주면 이 FCM을 가지고 push 알람을 보내는 로직을 구성하였다.
dependencies {
implementation 'com.google.firebase:firebase-admin:8.1.0'
}
firebase dependency를 build해준다.
@Configuration
public class FirebaseConfig {
@Value("${FIREBASE_KEY}")
private String firebaseKey;
@Bean
public FirebaseApp firebaseApp() throws IOException {
if(FirebaseApp.getApps().isEmpty()){
FileInputStream fis = new FileInputStream(firebaseKey);
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(fis))
.build();
return FirebaseApp.initializeApp(options);
}
return FirebaseApp.getInstance();
}
@Bean
FirebaseMessaging firebaseMessaging() throws IOException {
return FirebaseMessaging.getInstance(firebaseApp());
}
}
firebase key를 등록해주고 config 파일을 작성한다. firebaseApp()은 key를 가지고 firebase instance를 등록하는 메서드이고 firebaseMessaging은 FirebaseMessaging은 등록된 인스턴스에서 FirebaseMessaging 객체를 등록하는 메서드이다.
@Service
@RequiredArgsConstructor
public class FCMService {
private final FirebaseMessaging firebaseMessaging;
private final ProfileService profileService;
public void sendMessageNotification(KafkaMessage kafkaMessage) {
Profile receiverProfile = profileService.findById(kafkaMessage.receiverProfileId());
Profile senderProfile = profileService.findByUser(kafkaMessage.sender());
List<String> fcmTokens = receiverProfile.getUser().getFcmTokens();
MulticastMessage message = MulticastMessage.builder()
.putAllData(new HashMap<>() {{
put("time", LocalDateTime.now().toString());
put("senderProfileId", senderProfile.getId().toString());
}})
.setNotification(Notification.builder()
.setTitle(senderProfile.getNickname() + "님이 쪽지를 보내셨습니다.")
.setBody(kafkaMessage.content())
.build())
.addAllTokens(fcmTokens)
.build();
sendMessage(message);
}
private void sendMessage(MulticastMessage message) {
try {
firebaseMessaging.sendMulticast(message);
} catch (FirebaseMessagingException e) {
throw new RestApiException(FIREBASE_MESSAGE_SEND_FAILED);
}
}
}
이 부분이 푸쉬 알람을 보내는 서비스 로직이다. FirebaseMessaging 객체를 가져와 각 유저마다 저장되어 있는 fcm 토큰 리스트를 가져와 알람을 보내는 코드이다. MulticastMessage는 보낼 알람의 내용을 보내는 객체인데 이 객체에는 여러 데이터와 Notification을 담고 FCM 토큰 리스트를 설정하여 객체를 만든다. 그리고 FirebaseMessaging을 이용하여 알람을 보낸다.
쪽지를 보내면 이렇게 알람이 온다. 처음 푸쉬 알람을 구현해봐서 어떻게 할 지 몰랐지만 막상 해보니깐 서버에서 어려운 부분은 없없다.
클라이언트 개발자도 딱히 이 부분에 경험이 없어서 어떻게 구현해야 할지 몰랐는데 firebase 공식 문서를 많이 참조하였다. 공식 문서에 여러 방법이 있었는데 내가 주로 참고했던 이미지는 밑의 이미지였다.
내가 개발하고 있는 서비스의 클라이언트는 IOS여서 이 구조로 푸쉬알람이 보내지는 구조였다. 기기를 APNs라는 서버에 등록되고 토큰이 발행되면 이 토큰을 firebase에 보내여 FCM 토큰을 발급받게 되고 이 토큰을 서버로 보내주는 구조이다. 나는 이 토큰을 통해 푸시 알람을 보내라고 firebase에 전송하면 firebase가 다시 APNs로 보내고 APNs가 앱에 알람을 띄우라고 다시 요청한다.
이 과정을 클라이언트 개발자와 소통하면서 서로의 개발 영역이 아니어도 이 구조를 아는 것이 굉장히 많이 도움이 되었다. 분명 나는 FCM만 받아서 알람을 보내는 부분만 알아도 구현은 됬겠지만 전체적인 구조를 알게되면 훨씬 도움이 많이 되었다. 전체적인 구조를 이해하는게 굉장히 중요한 부분인 것 같다.