Skip to content

Conversation

@minwoo1999
Copy link
Member

@minwoo1999 minwoo1999 commented Nov 9, 2025

✨ PR 제목

feat: FCM 전송 실패 시 무효 토큰 정리 로직 추가


🔗 관련 이슈


📘 작업 유형

  • ✨ Feature (기능 추가)
  • 🐞 Bugfix (버그 수정)
  • 🔧 Refactor (코드 리팩토링)
  • ⚙️ Chore (환경 설정)
  • 📝 Docs (문서 작성 및 수정)
  • ✅ Test (기능 테스트)
  • 🎨 style (코드 스타일 수정)

📙 작업 내역

  • FCM 전송 결과를 구조화하기 위한 FcmSendResult 데이터 클래스 추가
  • FcmService에서 단일 토큰 발송 → 멀티캐스트 전송(sendEach)으로 변경
  • FirebaseMessagingException 처리 및 무효 토큰(UNREGISTERED, INVALID_ARGUMENT) 필터링 로직 추가
  • NotificationService에서 FCM 전송 결과 기반으로 invalidTokens 탐지 시 deviceDomainService.removeDevicesByTokens() 호출
  • DeviceDomainService, DeviceRepository, DeviceJpaRepositorydeleteByTokens / deleteByFcmTokenIn 메서드 추가
  • 서버 DB에 누적되는 무효 FCM 토큰 자동 정리 처리

🧪 테스트 내역

  • 유효한 FCM 토큰으로 정상 발송되는지 확인
  • UNREGISTERED, INVALID_ARGUMENT 토큰이 감지될 경우 DB에서 삭제되는지 확인
  • 빈 토큰 리스트 입력 시 처리 로직 정상 동작 확인
  • 기존 푸시 발송 기능에 영향 없음 확인

🎨 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
FCM 전송 로그 무효 토큰 정리 로그

✅ PR 체크리스트

  • 커밋 메시지가 명확합니다
  • PR 제목이 컨벤션에 맞습니다
  • 관련 이슈 번호를 작성했습니다
  • 기능이 정상적으로 작동합니다
  • 불필요한 코드를 제거했습니다

💬 추가 설명 or 리뷰 포인트

  • Firebase 전송 실패 시점(MessagingErrorCode.UNREGISTERED, INVALID_ARGUMENT)을 기준으로 무효 토큰을 삭제하도록 설계했습니다.
  • 추후 무효 토큰 정리 로직을 배치나 이벤트 기반으로 확장할 수 있습니다.
  • 대량 발송 시 성능 이슈가 있는지 모니터링이 필요합니다.

Summary by CodeRabbit

  • 새로운 기능

    • 푸시 알림을 배치 방식으로 여러 기기에 동시에 전송하여 전달 효율성 개선
  • 개선 사항

    • 유효하지 않거나 등록되지 않은 기기 토큰 자동 정리
    • 알림 전송의 성공/실패 통계 추적 및 로깅 강화
    • 오류 처리 및 신뢰성 개선

@coderabbitai
Copy link

coderabbitai bot commented Nov 9, 2025

Walkthrough

FCM 알림 전송을 단일 토큰에서 멀티캐스트 API로 리팩토링하고, 성공/실패/무효 토큰을 추적하는 FcmSendResult를 도입하며, FCM 실패 후 무효 토큰을 가진 디바이스를 제거하는 정리 로직을 추가합니다.

Changes

응집군 / 파일(들) 변경 요약
FCM 서비스 리팩토링
batch/src/main/kotlin/org/yapp/batch/service/FcmService.kt
단일 토큰 sendNotification 메서드를 멀티캐스트 sendMulticastNotification(tokens, title, body) 메서드로 변경. FirebaseMessaging.sendEach를 사용하여 배치 메시지 전송. BatchResponse 처리 및 무효 토큰 추출. FirebaseMessagingException 처리 추가.
FcmSendResult 데이터 클래스
batch/src/main/kotlin/org/yapp/batch/dto/FcmSendResult.kt
새로운 데이터 클래스 추가: successCount, failureCount, invalidTokens 속성. 팩토리 메서드: of(), empty(), allFailed().
알림 서비스 업데이트
batch/src/main/kotlin/org/yapp/batch/service/NotificationService.kt
상수 NO_SUCCESSFUL_DEVICES, NO_DEVICES_SENT 추가. 새로운 sendMulticastNotification API 사용. 무효 토큰 처리: deviceDomainService.removeDevicesByTokens() 호출. 토큰 필터링 로직 개선 (isNotBlank() 적용).
디바이스 도메인 서비스
domain/src/main/kotlin/org/yapp/domain/device/DeviceDomainService.kt
새로운 공개 메서드 removeDevicesByTokens(tokens: List<String>) 추가.
디바이스 저장소 인터페이스
domain/src/main/kotlin/org/yapp/domain/device/DeviceRepository.kt
새로운 메서드 deleteByTokens(tokens: List<String>) 정의.
JPA 저장소 구현
infra/src/main/kotlin/org/yapp/infra/device/DeviceJpaRepository.kt
새로운 메서드 deleteByFcmTokenIn(tokens: List<String>) 추가. @Modifying(clearAutomatically = true)@Query 어노테이션으로 대량 삭제 쿼리 구현.
저장소 구현체
infra/src/main/kotlin/org/yapp/infra/device/DeviceRepositoryImpl.kt
deleteByTokens(tokens: List<String>) 메서드 구현. JPA 저장소의 deleteByFcmTokenIn() 호출 위임.

Sequence Diagram(s)

sequenceDiagram
    participant NS as NotificationService
    participant FS as FcmService
    participant FM as FirebaseMessaging
    participant DDS as DeviceDomainService
    participant DR as DeviceRepository
    
    rect rgb(220, 240, 255)
    Note over NS,DR: 멀티캐스트 알림 발송 및 무효 토큰 정리 흐름
    end
    
    NS->>FS: sendMulticastNotification(tokens, title, body)
    
    rect rgb(245, 245, 220)
    FS->>FS: buildNotification(title, body)
    FS->>FS: 모든 토큰에 대해 배치 메시지 생성
    end
    
    FS->>FM: sendEach(messages)
    FM-->>FS: BatchResponse (successCount, failureCount, responses)
    
    rect rgb(240, 250, 240)
    FS->>FS: processFcmResponse()
    FS->>FS: 무효 토큰 추출<br/>(NotRegistered, InvalidArgument)
    end
    
    FS-->>NS: FcmSendResult(successCount, failureCount, invalidTokens)
    
    rect rgb(255, 240, 245)
    alt invalidTokens 존재
        NS->>NS: 로깅 (무효 토큰 경고)
        NS->>DDS: removeDevicesByTokens(invalidTokens)
        DDS->>DR: deleteByTokens(invalidTokens)
        DR->>DR: 데이터베이스에서 토큰 삭제
    end
    end
    
    NS-->>NS: 반환 (성공 여부, 발송 디바이스 수)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의 깊게 검토할 영역:
    • FcmService.ktprocessFcmResponse() 메서드: 무효 토큰 추출 로직의 정확성 (NotRegistered, InvalidArgument 판별)
    • NotificationService.ktsendToDevices() 메서드: 무효 토큰 정리 로직이 올바르게 통합되었는지 확인
    • FcmSendResult.kt: 팩토리 메서드들의 초기값 설정 검증
    • 리포지토리 계층의 삭제 작업이 트랜잭션 내에서 안전하게 수행되는지 확인
    • 토큰 필터링 로직 변경의 영향: isNotBlank() 적용 전후 동작 비교

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 PR의 주요 변경 사항을 명확하게 반영하고 있습니다. 멀티디바이스에서 무효한 FID/FCM 토큰 정리 로직 추가라는 핵심 목표를 정확히 전달하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경 사항이 #129 이슈의 요구사항을 충족합니다. FCM 멀티캐스트 API 전환, 무효 토큰 감지, DB 정리 로직이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 무효한 FCM 토큰 정리 로직 구현과 관련된 범위 내에 있습니다. 추가된 메서드와 클래스들이 모두 이 목표를 달성하기 위한 것입니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-433-feature/#129

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b55b7fc and dfa063f.

📒 Files selected for processing (7)
  • batch/src/main/kotlin/org/yapp/batch/service/FcmSendResult.kt (1 hunks)
  • batch/src/main/kotlin/org/yapp/batch/service/FcmService.kt (1 hunks)
  • batch/src/main/kotlin/org/yapp/batch/service/NotificationService.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/device/DeviceDomainService.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/device/DeviceRepository.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/device/DeviceJpaRepository.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/device/DeviceRepositoryImpl.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfa063f and edfdd03.

📒 Files selected for processing (4)
  • batch/src/main/kotlin/org/yapp/batch/dto/FcmSendResult.kt (1 hunks)
  • batch/src/main/kotlin/org/yapp/batch/service/FcmService.kt (1 hunks)
  • batch/src/main/kotlin/org/yapp/batch/service/NotificationService.kt (4 hunks)
  • infra/src/main/kotlin/org/yapp/infra/device/DeviceJpaRepository.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation

Comment on lines 51 to 56
if (errorCode == MessagingErrorCode.UNREGISTERED || errorCode == MessagingErrorCode.INVALID_ARGUMENT) {
invalidTokens.add(failedToken)
logger.warn("Invalid FCM token found: {}. Error: {}", failedToken, errorCode)
} else {
logger.error("Failed to send to token: {}. Error: {}", failedToken, errorCode, sendResponse.exception)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

INVALID_ARGUMENT 토큰 삭제 조건 재검토 필요

MessagingErrorCode.INVALID_ARGUMENT는 토큰 포맷 오류뿐 아니라 메시지 페이로드나 TTL 등 요청 파라미터 전체에 대한 유효성 실패에서도 반환됩니다. 따라서 일시적인 메시지 구성 오류만 발생해도 모든 토큰이 removeDevicesByTokens로 삭제되어 정상 토큰까지 손실될 수 있습니다. UNREGISTERED만 삭제 대상으로 유지하거나, INVALID_ARGUMENT의 상세 메시지가 실제 “invalid registration token”인지 확인한 뒤에만 삭제하도록 분기해 주세요.(firebase.google.com)

🤖 Prompt for AI Agents
In batch/src/main/kotlin/org/yapp/batch/service/FcmService.kt around lines 51 to
56, the code currently treats MessagingErrorCode.INVALID_ARGUMENT the same as
UNREGISTERED and deletes tokens; change this so only UNREGISTERED tokens are
unconditionally removed, and for INVALID_ARGUMENT inspect the exception detail
(e.g., exception.message or error description) and only add the token to
invalidTokens if the message explicitly indicates an invalid registration token
(contains "invalid registration token" or equivalent); otherwise log the
INVALID_ARGUMENT as an error/warning and do not delete the token.

@move-hoon move-hoon merged commit a9260f5 into develop Nov 9, 2025
1 check passed
@move-hoon move-hoon changed the title [BOOK-433] refactor: 멀티디바이스에서 유효하지 않은 FID/FCM 토큰 정리 로직 추가 (#129) refactor: 멀티디바이스에서 유효하지 않은 FID/FCM 토큰 정리 로직 추가 (#129) Nov 9, 2025
@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 9, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@move-hoon move-hoon changed the title refactor: 멀티디바이스에서 유효하지 않은 FID/FCM 토큰 정리 로직 추가 (#129) refactor: 멀티디바이스에서 유효하지 않은 FID/FCM 토큰 정리 로직 추가 Nov 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-433/feat] 멀티디바이스에서 유효하지 않은 FID/FCM 토큰 정리 로직 추가

3 participants