반응형

https://github.com/Seungkyu-Han/Toge-do-backend

 

GitHub - Seungkyu-Han/Toge-do-backend: Toge-do 앱의 백엔드 리포지토리입니다

Toge-do 앱의 백엔드 리포지토리입니다. Contribute to Seungkyu-Han/Toge-do-backend development by creating an account on GitHub.

github.com

이번에는 앱이 실행 중이지 않더라도 실시간 알림을 받는 방법이다.

(FCM을 사용하며, 다른 방법은 솔직히 생각해보지 않았다...)

 

이번에도 Webflux와 MSA 환경이며, 시스템 구성도도 저번과 같다.

 

우선 GCP 콘솔에서 프로젝트를 만들어주고, 프론트에 말을 해준 뒤 기본 설정들을 부탁한다.

그리고 테스트 할 수 있도록 deviceToken 하나를 받아왔다.

 

우선 이 토큰이 맞는지 FCM 페이지에서 테스트를 해보자.

이 화면에서 테스트 메시지를 보낼 수 있고, 파란색의 테스트 메시지 전송 버튼을 누르면 디바이스 토큰을 입력한 뒤 메시지를 보낼 수 있다.

 

우선 테스트 메시지를 전송해보니, 해당 기기에 알림이 잘 오는 것을 확인했다.

그럼 이제 스프링에서 알림을 보내보도록 하자.

 

 

프로젝트 설정 페이지에서 공개키를 가져와 스프링에 설정해준다.

 

저렇게 예시가 작성되어 있으며, 나도 저 예시를 따라서 인증 정보를 가져올 수 있도록 했다.

 

우선 필요한 라이브러리들을 불러온다.

implementation("com.google.firebase:firebase-admin:9.4.2")

 

자바였으면 @PostConstruct였나...? 를 사용하겠지만, 코틀린을 사용하기 때문에 Init을 사용해 서버스 클래스를 생성하자마자 인증 정보를 불러오고 firebase app을 초기화해주었다.

    init{
        val serviceAccount = FileInputStream(credentials)
        val options = FirebaseOptions.builder()
            .setCredentials(GoogleCredentials.fromStream(serviceAccount))
            .build()

        FirebaseApp.initializeApp(options)
    }

 

이제 firebase로 요청하는 코드인데, 라이브러리를 제공하기 때문에 어렵지 않다.

notification을 작성 -> message를 작성 -> 작성한 message를 FirebaseMessaging 클래스로 요청이다.

 

            val notification = Notification.builder()
                .setTitle(title)
                .setBody(content)
                .build()

            val message = Message.builder()
                .setNotification(notification)
                .setToken(deviceToken)
                .build()
                
            FirebaseMessaging.getInstance().send(message)

 

이렇게 작성하여 요청하면 되고, 나는 코루틴을 사용하여

    override fun pushNotification(deviceToken: String, title: String, content: String) {
        CoroutineScope(Dispatchers.IO).launch {
            val notification = Notification.builder()
                .setTitle(title)
                .setBody(content)
                .build()

            val message = Message.builder()
                .setNotification(notification)
                .setToken(deviceToken)
                .build()
            FirebaseMessaging.getInstance().send(message)
        }
    }

 

이렇게 service 클래스를 작성했다.

아래는 작성한 전체 service 코드이다.

@Service
class FCMServiceImpl(
    @Value("\${FCM.CREDENTIALS}")
    private val credentials: String
): FCMService {

    init{
        val serviceAccount = FileInputStream(credentials)
        val options = FirebaseOptions.builder()
            .setCredentials(GoogleCredentials.fromStream(serviceAccount))
            .build()

        FirebaseApp.initializeApp(options)
    }

    override fun pushNotification(deviceToken: String, title: String, content: String) {
        CoroutineScope(Dispatchers.IO).launch {
            val notification = Notification.builder()
                .setTitle(title)
                .setBody(content)
                .build()

            val message = Message.builder()
                .setNotification(notification)
                .setToken(deviceToken)
                .build()
            FirebaseMessaging.getInstance().send(message)
        }
    }
}

 

이제 kafka listener를 사용해 해당 서비스를 요청하도록 만들어야 한다.

해당 서비스는 우선 SSE로 요청을 시도하고, 연결된 기기가 없다면 FCM을 사용해 이벤트를 전송하도록 만들었다.

 

    @KafkaListener(topics = ["FRIEND_REQUEST_TOPIC"], groupId = "seungkyu")
    fun requestFriend(message: String){
        val event = EventEnums.REQUEST_FRIEND_EVENT
        val friendRequestEventDto = objectMapper.readValue(message, FriendRequestEventDto::class.java)
        val isSSE = notificationService.publishNotification(
            id = friendRequestEventDto.receiverId,
            sseDao = SSEDao(EventEnums.REQUEST_FRIEND_EVENT, friendRequestEventDto.sender)
        )
        if (!isSSE && friendRequestEventDto.deviceToken != null){
            fcmService.pushNotification(
                deviceToken = friendRequestEventDto.deviceToken,
                title = event.eventTitle,
                content = "${friendRequestEventDto.sender}${event.eventContent}"
            )
        }
    }

 

kafka listener로 이벤트를 받고, sink가 연결되어 있지 않으며 device token이 있는 상황이라면 해당 device token을 사용해 FCM service로 요청해 앱 푸쉬 알림을 전송한다.

 

이렇게 작성하고 친구요청으로 테스트를 해보았는데, 아래와 같이 잘 가는 것을 볼 수 있었다.

 

이렇게 앱 푸쉬 서비스를 개발해보았다.

개인적으로는 SSE보다 더 쉬운 것 같았다.

+ Recent posts