-
kafka 브로커에서 컨슈머가 메세지를 소비
- 메세지 처리에 대하여 rabbitMQ가 아닌 kafka를 적용한 것에 대하여
- Kafka 이벤트 소싱이나 로그 수집, 대용량 데이터를 처리하고 지속적으로 로그를 기록
- rabbitMQ는 채팅 서비스와 같이 메시지의 빠른 전달과 순서 보장이 더 중요한 경우에 적합
← 알림은 빠른 전달 보다는 보낸다는 그 자체가 중요.
-
메세지에는 알림을 처리위한 dto가 존재
-
팔로우/댓글/게시글 등 알람에 대한 데이터가 모두 다름
- 댓글/게시글은 글에 대한 정보도 포함되어 있어야 하지만
팔로우/좋아요 의 경우 단순히
“XXX님이 회원님을 팔로우하였습니다 / 회원님의 게시글을 좋아합니다 / 회원님의 댓글을 좋아합니다. 로 이루어짐
-
메세지로 받은 데이터를 이용하여 각 알림 형식에 대한 dto 를 생성
-
이를 log 및 추후 적용할 알림 미 발송 시 재발송을 위해 DB 에 저장
- DB는 MongoDB를 이용 (알림 형식이 전부 다르기에 NoSQL 적용)
-
MongoDB 저장 시 changeStream을 통해 이를 감지
- MongoDB Change Streams는 데이터베이스에서 변경 이벤트를 실시간으로 구독하고 처리할 수 있는 기능
- 메세지를 소비하고 바로 sse 연결하는 방법도 있지만, 데이터 확보를 위해 처리 시점 변경
But
- KafkaListener 내부에서 MongoDB에 데이터를 저장하고, 그 후 SseEmitter로 실시간 알림을 처리하는 로직은 ChangeStream을 사용하지 않아도 됨
- Kafka를 통해 수신한 메시지에 대한 처리가 이미 이뤄졌기 때문. ChangeStream은 MongoDB의 데이터베이스 변화에 따른 실시간 스트리밍을 처리하는 방법이며, Kafka로 이미 이벤트를 처리하고 있으므로 별도로 ChangeStream을 사용할 필요는 없음
- ChangeStream을 사용하여 MongoDB의 변화를 감지하고, 이를 SSE로 전달하는 방식은 Kafka를 사용하지 않는 경우에 적합
- KafkaListener를 통해 이벤트를 처리한 후, 데이터를 MongoDB에 저장하고 이를 SSE로 클라이언트에 전달하는 방식은 이미 Kafka가 이벤트를 처리하고 있기 때문에 ChangeStream을 사용할 필요가 없음
- Kafka와 ChangeStream은 서로 다른 방식의 실시간 처리 기술 → 같은 시스템에서 둘을 혼합해서 사용할 때는 Kafka가 이벤트 처리 역할을 하므로 ChangeStream을 추가로 사용할 필요는 없음
-
이후 SseEmitter를 이용하여 알림 데이터를 클라이언트로 전달
- SseEmitter 자체는 HTTP 요청의 메소드와 관계없이, 클라이언트와 서버 간의 지속적인 연결을 제공하는 역할
- 주로 GET 방식에서 사용 (서버가 클라이언트로부터의 요청을 처리한 후 응답을 지연시킬 수 있기 때문)
- POST 메소드로 데이터를 보내고, 그 결과로 SSE 이벤트를 전송하는 방식도 가능
- 단순히 sse를 사용할 경우, 어플리케이션 접속 시 알림을 받을 수는 있지만,
알림의 경우는 포그라운드 보다는 백그라운드 상황에서 받는 것이 목적에 부합하다 여김
- 이를 처리하기 위해 push 알림이 필요하다고 판단
-
아래는 sse 처리 예시
// Kafka 메시지를 읽어 SseEmitter를 통해 클라이언트로 전달
@KafkaListener(topics = "notification-topic", groupId = "notification-group")
public void handleKafkaMessage(String message) {
for (SseEmitter emitter : emitters) { // 여러 클라이언트(SSE 연결)가 있을 수 있으므로, 각 클라이언트에게 메시지를 전송
try {
// 수신한 Kafka 메시지를 SSE 형식으로 각 클라이언트에 전송
emitter.send(SseEmitter.event().name("notification").data(message));
} catch (IOException e) {
// SSE 연결이 끊어졌을 경우 해당 클라이언트를 목록에서 제거
emitters.remove(emitter);
}
}
}
- SseEmitter.event(): sse의 이벤트를 정의하는 메서드
- name(): SSE 이벤트의 이름을 설정(클라이언트가 수신할 이벤트의 유형)
- data(): 이벤트와 함께 전송할 데이터를 설정
-
push 알림의 경우 fcm(Firebase Cloud Messaging) 을 통한 처리가 필요
- sse를 사용 시 클라이언트 측에 사용자 계정과 관련된 이벤트를 실시간 전달
- 서버에서 알림 데이터 전송 받고, 클라이언트가 UI를 실시간으로 업데이트
- sse를 사용하여 push 알림과 비슷한 처리는 가능하지만, 브라우저/앱 활성화 상태에서만 가능
← fcm 과의 병행 필요
← 포그라운드에서는 sse로 실시간 알림이 제공될 수 있지만, 백그라운드 상태에서는 fcm 같은 푸시 알림 시스템이 필요
-
푸시 알림 시스템에 fcm 외에도
Apple(Apple 생태계에 최적화)
MS(비용 발생/Azure 기반이 아니면 활용도 Low)
Amazon(비용 발생)
측에서도 제공한다는 것을 추가 인지
-
FCM의 경우는 google 측
-
Android, ios, web 등 다양한 플랫폼 지원
-
확장성 뛰어나며 무료
-
google에 의존하기에, google 서비스를 차단한 국가(ex:중국)에서는 사용 불가능하며, Safari는 web push 에서 fcm을 지원하지 않음
- Chrome, Firefox, Edge와 같은 브라우저에서 지원
- Safari는 Web Push를 자체적으로 구현했으며, Apple의 **APNs(Apple Push Notification Service)**를 기반으로 동작
- iOS 앱에서 푸시 알림을 보내는 경우, FCM은 APNs와 통합
- FCM은 푸시 알림 요청을 Google 서버를 통해 Apple의 APNs로 전달하여 iOS 기기에 알림을 전송(네이티브 앱에서만 가능하며, Safari 웹 푸시에서는 작동 X)
- Safari(iOS)에서도 지원하려면 APNs 인증서를 활용해 Web Push를 구현
- iOS에서 PWA의 푸시 알림은 Safari에서만 가능하고, Chrome에서는 PWA의 푸시 알림이 지원되지 않음
- iOS에서 Chrome이 Safari의 엔진을 사용하기 때문
- Chrome은 iOS에서 Apple의 제약으로 인해 자체적인 푸시 알림 기능을 구현할 수 없음
- Chrome이 Safari의 엔진을 사용하더라도 APNs는 크롬이 아닌 Safari에만 제공됨
-
PWA 기반 웹 푸시 알림에서는 FCM이 대체로 가장 적합하다고 판단
← Chrome과 같은 주요 브라우저와 최적화
- PWA(Progressive Web App)
- 웹 기술(HTML, CSS, JavaScript)을 사용해 개발되지만, 앱과 유사한 사용자 경험을 제공하는 웹 애플리케이션(웹 앱)
- 다양한 기기(데스크톱, 태블릿, 모바일)에 적응
- Service Worker를 사용해 오프라인 상태에서도 데이터를 캐싱하여 작동 가능
- 사용자가 PWA를 기기에 설치해 네이티브 앱처럼 사용 가능
- Web Push API와 같은 기능을 통해 푸시 알림을 지원
- 사용자가 브라우저에서 알림을 허용하면 서버에서 알림을 푸시