대머리개발자

앱 푸시 - FCM - 조금 고도화 본문

개발이야기/개념

앱 푸시 - FCM - 조금 고도화

대머리개발자 2025. 2. 27. 14:50
728x90

기존 단순하게 테스트를 위해서 진행했던 부분을 고도화

https://hcnmy.tistory.com/227

 

앱 푸시 - FCM

자세한 내용은 해당 블로그에 확인한다. 너무나도 잘 기록되어있다. ㄳhttps://zuminternet.github.io/FCM-PUSH/ FCM 푸시 파헤치기파일럿부터 적용까지 진행했던 FCM 푸시를 파헤치며 기초 가이드북처럼 정

hcnmy.tistory.com

 

 

일단 앱 푸쉬를 위한 남들 다하는 카프카는 By Pass 하였다.

 

많아야 천명 정도에 10개 미만으로 보내는 푸쉬이기 때문에....해보곤 싶었지만 투머취!!

 

충분히 Flux 만 활용해도  anyway!!

 

목표 : 

1. 실패한 사용자(미설치, 미수신)에겐 문자를 보낸다.

2. 푸쉬에 해당하는 알람을 생성 해야한다. (7일 보관)

 

미수신 체크가 생각보다 녹록하지 않았고...휘밤

 

멤버십 가입자에게만 보내는 푸쉬이기 때문에 간단(?)하게 토픽을 이용하려고 했으나...

하나하나 콘트롤 할 수 있도록 토큰으로 방향을 잡았다.

 

생각보다 많은 포인트에서 짱구를 굴려야 한다.

 

 

 

일단 FCM 푸쉬에 대한 보장을 하지 않는다. 따라서 예외처리가 발생되면 최소 3번을 다시 보낸다. (미수신을 최소화 하자 ㅠ)

그리고 오류가 난 토큰은 계속 에러가 발생하기 때문에 관련 예외가 발생하면 토큰을 삭제 해 준다.

 

사용자 distinct을 해준 이유는 디바이스가 하나 이상이기 때문에.

....

그냥 모든 것은 코드로 보자.

 

flatMap과 Map 본질을 느낄수 있는 구문이다..

 fcmFlux
.flatMap(fcm -> {
    if(Constants.NOT_EXIST.equals(fcm.getToken())){
        return Mono.just(Void.TYPE); // 미설치 사용자는 토큰이 없겠쥬.
    }
    return  sendPushMessage(message, fcm.getToken());
})
.then(Mono.delay(Duration.ofSeconds(5)))  // 클라이언트가 수신 응답을 싸주는 시간을 넉넉하게 기둘하장.
.thenMany(fcmFlux.distinct(Fcm::getUserUID))
.flatMap( fcm ->
          // 클라이언트 정상적인 응답이 없으면 createAlaram을 임의로 만들고 문자 숑숑!
         createAlarmIf(MessageAlarm.builder().messageId(message.getId()).userUID(fcm.getUserUID()).build())
         .flatMap(isSend -> isSend && (message.getContentType().equalsIgnoreCase(Constants.CLASS) || message.getSendSmsOnPushFailure())? sendTextAfter(buildMessage(message, fcm.getUserUID())) : Mono.empty())
)
.subscribe();

 

// 중요한게 뭐냐 AI 가 다 만들어준다....첫번째 이미지 처럼 와꾸만 Flow만 생각하고. 디테일한것은. AI 친구에게 맡긴다.

// 근데 이것도 질문이 중요하다.

// 첫번째로는 단순 for 문 돌려서 3번 체크를 했다... 해서 내가 retry 어저꾸 친구가 있는것 같은데.라고 했더니 그제야 다시 바꿔준다. ㅋㅋㅋ 처음 부터 최선을 다하지 않는다. 나쁜놈이 ㅋ

public Mono<Object> sendMessageByToken(MessageGrpc.Message message) {
    return Mono.create(sink -> {
        try {
           ...
        } catch (FirebaseMessagingException e) {
            sink.error(e);
        }
    })
    .onErrorResume(e -> { // retryWhen 밖으로 이동
        if (e instanceof FirebaseMessagingException fme){
           if(fme.getMessagingErrorCode() == MessagingErrorCode.UNREGISTERED || fme.getMessagingErrorCode() == MessagingErrorCode.INVALID_ARGUMENT){
               fcmRepository.deleteFcmByToken(message.getFcmToken()).subscribeOn(Schedulers.boundedElastic()).subscribe();
               return Mono.empty(); // 메시지 전송 중단
           }
        }
        return Mono.error(e); // 다른 예외는 다시 throw
    })
    .retryWhen(Retry.backoff(2, Duration.ofSeconds(1))
            .filter(throwable -> throwable instanceof FirebaseMessagingException
                    && ((FirebaseMessagingException) throwable).getMessagingErrorCode() != MessagingErrorCode.UNREGISTERED) // UNREGISTERED는 제외
            .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) ->
                  retrySignal.failure()
            )
    );
}

 

 

사요나라...아....AI 학습해야 하는데..

728x90

'개발이야기 > 개념' 카테고리의 다른 글

1년이 더해지는 날짜 Formatter 이슈  (0) 2025.01.03
앱 푸시 - FCM  (0) 2024.11.27
flatMap vs map  (1) 2024.11.20
API 호출 vs DB View  (0) 2024.11.11
비트 연산은 생각보다 파워풀한 친구다.  (0) 2024.08.19