Skip to content

fix: 알림 전송 로직 수정#2278

Merged
Soundbar91 merged 9 commits into
developfrom
fix/2277-notification-service
May 28, 2026
Merged

fix: 알림 전송 로직 수정#2278
Soundbar91 merged 9 commits into
developfrom
fix/2277-notification-service

Conversation

@Soundbar91
Copy link
Copy Markdown
Collaborator

@Soundbar91 Soundbar91 commented May 28, 2026

🔍 개요


🚀 주요 변경 내용

알림 전송 로직 수정

  • 모든 알림을 배치로 전송하도록 로직을 수정했습니다.
  • FCM 알림 전송 후 Notification을 저장하도록 수정했습니다.
    • Notification 저장의 경우 부가적인 기능이라고 판단하여 try-catch으로 예외를 핸들링하고 로그를 남기는 방식으로 수정했습니다.

💬 참고 사항


✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

Summary by CodeRabbit

Release Notes

  • Refactor

    • Simplified notification delivery system by removing per-notification result tracking
    • Implemented batch notification sending for improved efficiency
    • Streamlined public notification APIs
  • Tests

    • Removed notification service test suite

Review Change Stack

@Soundbar91 Soundbar91 self-assigned this May 28, 2026
@github-actions github-actions Bot added 공통 백엔드 공통으로 작업할 이슈입니다. 버그 정상적으로 동작하지 않는 문제상황입니다. labels May 28, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Notification service refactored to dispatch batches of FCM messages via FcmSendRequest instead of per-message sending with result tracking. NotificationPersistenceService removed, persistence inlined with error logging; result-based public APIs removed; callers simplified to void push methods.

Changes

Notification Batch Sending Refactor

Layer / File(s) Summary
FcmClient consolidation for batch sending
src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java
FcmClient.sendMessage(...) now includes inline device token validation and consolidated send/logging logic, removing the separate sendMessageWithResult(...) method that previously returned boolean success indicators.
NotificationService refactor to batch FCM with inline persistence
src/main/java/in/koreatech/koin/domain/notification/service/NotificationService.java
NotificationService.pushNotification(...) and pushNotifications(...) refactored to build FcmSendRequest lists and call fcmClient.sendMessages(...) for batch delivery, followed by notificationJdbcRepository.batchInsert(...) with try/catch exception logging. Removed NotificationDeliveryResult record, pushNotificationsWithResult(...) method, and prior per-notification sending with transaction-callback machinery. Import updates support batch-send dependencies and remove transaction synchronization imports.
KeywordNotificationService caller update
src/main/java/in/koreatech/koin/domain/notification/service/KeywordNotificationService.java
KeywordNotificationService.notifyArticleKeyword(...) and notifyLostItemKeyword(...) updated to call pushNotifications(...) instead of pushNotificationsWithResult(...), reflecting the removal of the result-returning API.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • BCSDLab/KOIN_API_V2#2263: Introduces FcmClient.sendMessages(List<FcmSendRequest>) batch API and FcmSendRequest data class that this PR consumes in refactored NotificationService.
  • BCSDLab/KOIN_API_V2#2165: Updates ArticleKeywordEventListener notification delivery logic; directly impacted by removal of pushNotificationsWithResult(...) in this PR.
  • BCSDLab/KOIN_API_V2#2237: Connected through keyword-notification dispatch; its ArticleKeywordNotificationService/LostItemKeywordNotificationService call the old pushNotificationsWithResult(...) API removed here.

Suggested reviewers

  • BaeJinho4028
  • taejinn
  • dh2906

Poem

🐰 A bundle of messages sent in a hop,
Batch-FCM makes delivery stop,
Results removed, simplicity wins,
Where once many flows, now one path begins! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 4

❌ Failed checks (1 warning, 3 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The linked issue #2277 lacks concrete requirements or detailed specifications; only a vague objective 'NotificationService 개선' (improve NotificationService) is provided without measurable criteria. Review the linked issue #2277 to confirm whether the batch sending refactoring, FCM-first ordering, and exception handling align with the intended improvements.
Out of Scope Changes check ❓ Inconclusive The PR removes NotificationPersistenceService entirely and deletes NotificationServiceTest (156 lines), which may exceed the scope of a notification logic fix. Clarify whether complete removal of NotificationPersistenceService and deletion of existing test coverage align with the #2277 objectives and whether new tests should be added.
Description check ❓ Inconclusive The PR description (in Korean) clearly outlines the refactoring: batch sending, FCM-first order, and try-catch exception handling. However, it does not address why NotificationPersistenceService was removed. Add explicit explanation for removing NotificationPersistenceService and deleting test coverage; clarify the rationale for moving persistence after FCM sending.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '알림 전송 로직 수정' (notification transmission logic fix) accurately describes the main change: refactoring notification delivery from per-notification to batch sending.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2277-notification-service

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.

@github-actions github-actions Bot requested review from BaeJinho4028 and dh2906 May 28, 2026 04:12
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 28, 2026

Unit Test Results

669 tests   666 ✔️  1m 24s ⏱️
167 suites      3 💤
167 files        0

Results for commit 226fa10.

♻️ This comment has been updated with latest results.

Copy link
Copy Markdown
Contributor

@dh2906 dh2906 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!

}
saveNotificationAfterSend(notification);
return new NotificationDeliveryResult(notification, true);
notificationJdbcRepository.batchInsert(notifications);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

현재는 FCM 배치 전송 결과를 확인하지 않고 전체 notifications를 저장해서, 전송에 실패한 알림도 같이 저장될 수 있어 보이는데 의도인건지 궁금합니다!
notification 테이블에 성공/실패에 대한 필드가 없어서 구분짓기 어려워보여요

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

실패한 알림의 경우 제외할 수 있도록 이후 작업으로 진행할려고 합니다 !

Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java (1)

62-93: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Blank token validation missing in batch send, unlike single-message sendMessage.

sendMessage validates blank tokens at lines 40-42 and returns early, but sendMessages passes requests directly to FCM without filtering. If a request contains a blank/null device token, it will cause an FCM error.

Additionally, the try-catch wraps the entire loop, so if any batch fails (including due to a blank token), all subsequent batches are skipped.

🔧 Proposed fix: Filter blank tokens and move error handling inside the loop
 public void sendMessages(List<FcmSendRequest> requests) {
-    try {
-        for (int start = 0; start < requests.size(); start += FCM_MESSAGE_BATCH_SIZE) {
+    List<FcmSendRequest> validRequests = requests.stream()
+        .filter(r -> StringUtils.hasText(r.targetDeviceToken()))
+        .toList();
+
+    if (validRequests.isEmpty()) {
+        return;
+    }
+
+    for (int start = 0; start < validRequests.size(); start += FCM_MESSAGE_BATCH_SIZE) {
+        try {
-            int end = Math.min(start + FCM_MESSAGE_BATCH_SIZE, requests.size());
-            List<Message> messages = requests.subList(start, end).stream()
+            int end = Math.min(start + FCM_MESSAGE_BATCH_SIZE, validRequests.size());
+            List<Message> messages = validRequests.subList(start, end).stream()
                 .map(request -> Message.builder()
                     // ... existing mapping
                     .build()
                 )
                 .toList();

             FirebaseMessaging.getInstance().sendEach(messages);
+        } catch (Exception e) {
+            log.warn("FCM 알림 배치 전송 실패 (batch start={})", start, e);
         }
-    } catch (Exception e) {
-        log.warn("FCM 알림 전송 실패", e);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java` around
lines 62 - 93, sendMessages currently builds and sends batches without
validating FcmSendRequest.targetDeviceToken (unlike sendMessage) and wraps the
whole loop in one try-catch, so a bad token aborts all batches; fix by filtering
requests to exclude null/blank targetDeviceToken before mapping to Message (use
same validation logic as sendMessage) and move the try-catch inside the for-loop
around the per-batch send (FirebaseMessaging.getInstance().sendEach) so failures
in one batch (or from a bad token) are logged and skipped while subsequent
batches still run; reference sendMessages, sendMessage, FCM_MESSAGE_BATCH_SIZE,
FcmSendRequest.targetDeviceToken, and FirebaseMessaging.getInstance().sendEach.
🧹 Nitpick comments (2)
src/main/java/in/koreatech/koin/domain/notification/service/NotificationService.java (1)

42-66: 💤 Low value

@Transactional scope encompasses external FCM call.

The transaction starts before the FCM call (line 59) and includes the external network operation. If batchInsert were to throw an unchecked exception not caught by the try-catch (e.g., from infrastructure issues), the transaction would roll back but FCM messages would already be sent. Consider whether the transactional boundary should only wrap the persistence portion, or document this acceptable trade-off.

The current error handling for persistence failures (lines 61-65) aligns with the PR objectives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/in/koreatech/koin/domain/notification/service/NotificationService.java`
around lines 42 - 66, The method pushNotifications currently carries
`@Transactional` and thus wraps the external fcmClient.sendMessages call; move the
transaction boundary so only the DB insert is transactional: remove
`@Transactional` from pushNotifications, create a new private `@Transactional`
method (e.g., saveNotifications(List<Notification>) that calls
notificationJdbcRepository.batchInsert(notifications), and in pushNotifications
call fcmClient.sendMessages(fcmSendRequests) first then wrap the
saveNotifications call in the existing try/catch to log failures; this ensures
the external FCM call is outside the transactional scope while preserving your
persistence error handling.
src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java (1)

40-42: 💤 Low value

Consider adding a debug/info log when skipping due to blank token.

The silent early return makes it harder to diagnose why a notification wasn't sent. A debug-level log would help with troubleshooting without adding noise in production.

📝 Proposed addition
     if (!StringUtils.hasText(targetDeviceToken)) {
+        log.debug("Skipping FCM send: blank device token");
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java` around
lines 40 - 42, Add a debug/info log inside FcmClient where you currently
early-return on a blank token (the check using
StringUtils.hasText(targetDeviceToken)) so callers can see why a notification
was skipped; update the method (e.g., sendNotification or whichever method
contains the token check) to call the class logger (e.g., logger.debug or
logger.info) before returning, including the targetDeviceToken variable and a
brief message like "Skipping send: blank targetDeviceToken" to aid
troubleshooting without spamming production logs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java`:
- Around line 62-93: sendMessages currently builds and sends batches without
validating FcmSendRequest.targetDeviceToken (unlike sendMessage) and wraps the
whole loop in one try-catch, so a bad token aborts all batches; fix by filtering
requests to exclude null/blank targetDeviceToken before mapping to Message (use
same validation logic as sendMessage) and move the try-catch inside the for-loop
around the per-batch send (FirebaseMessaging.getInstance().sendEach) so failures
in one batch (or from a bad token) are logged and skipped while subsequent
batches still run; reference sendMessages, sendMessage, FCM_MESSAGE_BATCH_SIZE,
FcmSendRequest.targetDeviceToken, and FirebaseMessaging.getInstance().sendEach.

---

Nitpick comments:
In
`@src/main/java/in/koreatech/koin/domain/notification/service/NotificationService.java`:
- Around line 42-66: The method pushNotifications currently carries
`@Transactional` and thus wraps the external fcmClient.sendMessages call; move the
transaction boundary so only the DB insert is transactional: remove
`@Transactional` from pushNotifications, create a new private `@Transactional`
method (e.g., saveNotifications(List<Notification>) that calls
notificationJdbcRepository.batchInsert(notifications), and in pushNotifications
call fcmClient.sendMessages(fcmSendRequests) first then wrap the
saveNotifications call in the existing try/catch to log failures; this ensures
the external FCM call is outside the transactional scope while preserving your
persistence error handling.

In `@src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java`:
- Around line 40-42: Add a debug/info log inside FcmClient where you currently
early-return on a blank token (the check using
StringUtils.hasText(targetDeviceToken)) so callers can see why a notification
was skipped; update the method (e.g., sendNotification or whichever method
contains the token check) to call the class logger (e.g., logger.debug or
logger.info) before returning, including the targetDeviceToken variable and a
brief message like "Skipping send: blank targetDeviceToken" to aid
troubleshooting without spamming production logs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8dc99d3a-1229-49e9-b405-7db00108f7b7

📥 Commits

Reviewing files that changed from the base of the PR and between 3117b06 and 226fa10.

📒 Files selected for processing (5)
  • src/main/java/in/koreatech/koin/domain/notification/service/KeywordNotificationService.java
  • src/main/java/in/koreatech/koin/domain/notification/service/NotificationPersistenceService.java
  • src/main/java/in/koreatech/koin/domain/notification/service/NotificationService.java
  • src/main/java/in/koreatech/koin/infrastructure/fcm/FcmClient.java
  • src/test/java/in/koreatech/koin/unit/domain/notification/service/NotificationServiceTest.java
💤 Files with no reviewable changes (2)
  • src/test/java/in/koreatech/koin/unit/domain/notification/service/NotificationServiceTest.java
  • src/main/java/in/koreatech/koin/domain/notification/service/NotificationPersistenceService.java

@Soundbar91 Soundbar91 merged commit a5ba8b1 into develop May 28, 2026
7 checks passed
@Soundbar91 Soundbar91 deleted the fix/2277-notification-service branch May 28, 2026 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

공통 백엔드 공통으로 작업할 이슈입니다. 버그 정상적으로 동작하지 않는 문제상황입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[공통] NotificationService 개선

2 participants