diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt index 1f96de1..7a51f63 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/fcm/FcmService.kt @@ -29,8 +29,14 @@ class FcmService { creatorId: Long? = null, auditionId: Long? = null ) { - if (tokens.isNotEmpty()) { - logger.info("os: $container") + if (tokens.isEmpty()) return + logger.info("os: $container") + + var targets = tokens + val maxAttempts = 3 + var attempt = 1 + + while (attempt <= maxAttempts && targets.isNotEmpty()) { val multicastMessage = MulticastMessage.builder() .addAllTokens(tokens) @@ -85,8 +91,34 @@ class FcmService { } val response = FirebaseMessaging.getInstance().sendEachForMulticast(multicastMessage.build()) - logger.info("[FCM] ✅ 성공: ${response.successCount}") - logger.info("[FCM] ❌ 실패: ${response.failureCount}") + val failedTokens = mutableListOf() + + response.responses.forEachIndexed { index, res -> + if (!res.isSuccessful) { + val exception = res.exception + val token = targets[index] + + if (exception?.messagingErrorCode == MessagingErrorCode.UNREGISTERED) { + logger.error("[FCM] ❌ UNREGISTERED → $token") + // 필요 시 DB에서 삭제 + } else { + logger.error("[FCM] ❌ 실패: $token / ${exception?.messagingErrorCode}") + failedTokens.add(token) + } + } + } + + if (failedTokens.isEmpty()) { + logger.info("[FCM] ✅ 전체 전송 성공") + return + } + + targets = failedTokens + attempt++ + } + + if (targets.isNotEmpty()) { + logger.error("[FCM] ❌ 최종 실패 대상 ${targets.size}명 → $targets") } }