Skip to content

[FEAT] 일정 및 준비/실행 항목 알람 구현 - #228

Merged
taerimiiii merged 27 commits into
developfrom
feat/larfor/222-alarm
Aug 11, 2026
Merged

[FEAT] 일정 및 준비/실행 항목 알람 구현#228
taerimiiii merged 27 commits into
developfrom
feat/larfor/222-alarm

Conversation

@taerimiiii

@taerimiiii taerimiiii commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🔗 이슈 번호

#️⃣ 기능 설명

알람 기능을 구현했습니다. ALARM 약관 동의·FCM 토큰 등록·알람 on/off 토글·일정/준비·실행 항목 리마인드 예약·일정 수정 시 알람 보정·완료 시 알람 취소·알람 조회까지, 명세서 기준 API와 Redis 기반 지연 발송 파이프라인을 포함합니다.

📌 작업 내용

DB

  • Flyway V18: termsALARM 타입 추가, users.alarm_state 컬럼 추가
  • TermType.ALARM enum 및 Users.alarmState 필드 반영
  • Firebase Admin SDK 연동 (FirebaseConfig, FIREBASE_CREDENTIALS_BASE64 환경변수)
  • firebase-admin 의존성 추가, deploy workflow에 Firebase secret 반영
  • Redis ZSET 기반 알람 지연 큐 (AlarmDelayedQueueRepository) — Redisson 없이 StringRedisTemplate 사용
  • @EnableSchedulingAlarmReminderDispatcher(15초 주기 FCM 발송 컨슈머)

API — F100 (알람 설정/조회)

  • POST /api/v1/alarms/term — ALARM 약관 동의 및 alarm_state 활성화
  • POST /api/v1/alarms/push-token — FCM 토큰 검증 후 Redis 저장
  • PATCH /api/v1/alarms/state — 알람 접근 권한 토글 (활성화 시 약관 동의 필수)
  • GET /api/v1/alarms/my — 내 알람 목록 커서 페이징 (updated_at DESC, size 1~100)
  • GET /api/v1/alarms/{reminderId} — 알람 단건 조회
  • PATCH /api/v1/alarms/{eventId} — 일정 수정(C107) 후 알람 발송 시각·제목·본문 보정

API — F101 / F102 (리마인드 예약)

  • POST /api/v1/alarms/remind/event/{eventId} — 일정 리마인드 알람 생성
    • 일반 일정: 시작 1일 전 / 종일 일정: 당일 07:00
  • POST /api/v1/alarms/remind/action-item/{actionItemId} — 준비/실행 항목 리마인드 알람 생성
    • UNTIMED_PREP: 부모 일정 시작 2시간 전
    • TIMED_ACTION: displayTime 또는 표시일 07:00
  • 반복 일정/항목: 발송 직후 다음 회차 자동 재스케줄링 (EventRecurrenceCalculator)

도메인 로직

  • Reminders 엔티티 factory/reschedule/상태 전이 메서드 추가
  • AlarmReminderScheduleService — 생성·보정·취소 및 발송 시각 계산
  • ActionItemService.updateActionItemStatusCOMPLETED 처리 시 예약 알람 취소
  • AlarmReminderDispatcher — 발송 직전 completed_at / soft delete / alarm_state 재검증
  • AlarmErrorCode — F100/F101/F102 관련 에러 코드 정의
  • Swagger 문서 (AlarmControllerDocs)

✅ 체크리스트

  • Assignees, Labels를 모두 지정했나요?
  • GitHub Copilot의 자동 코드 리뷰 제안을 검토하고 필요한 부분을 반영했나요?
  • PR 머지 전 빌드 및 CI가 정상 작동하는지 확인했나요?

📸 스크린샷 (선택)

  • 알람 약관 동의 성공
image
  • fcm 푸시 토큰 발급 성공
image
  • fcm 푸시 토큰 발급 시 이미 푸시 토큰이 존재하는 경우 - 실패 응답
image
  • 일정 리마인드 알람 발송 성공
image
  • 일정 리마인드 알람 발송 대상이 존재하지 않는 경우 - 실패 응답
image
  • 일정 리마인드 알람 발송 대상의 시각이 과거인 경우 - 실패 응답
image
  • 일정 리마인드 알람 발송 대상과 이미 동일한 알람이 존재하는 경우 - 실패 응답
image
  • 일정 리마인드 알람 발송 실행 시 알람 발송 권한이 없는 경우 - 실패 응답
image
  • 준비/실행 항목 리마인드 알람 발송 성공
image
  • 준비/실행 항목 알람 발송 대상이 존재하지 않는 경우 - 실패 응답
image
  • 준비/실행 항목 알람 발송 대상의 시각이 과거인 경우 - 실패 응답
image
  • 준비/실행 항목 알람 발송 대상과 이미 동일한 알람이 존재하는 경우 - 실패 응답
image
  • 준비/실행 항목 알람 발송 실행 시 알람 발송 권한이 없는 경우 - 실패 응답
image
  • 알람 비활성화 성공
image
  • 알람 활성화 성공
image
  • 알람 비/활성화 시 알람 약관에 미동의한 경우 - 실패 응답
image
  • 일정 수정에 따른 알람 수정 성공
image
  • 수정하고자 하는 알람의 부모 일정이 없는 경우 - 실패 응답
image
  • 알람 단건 조회 성공
image
  • 알람 단건 조회 알람이 존재하지 않는 경우
image
  • 알람 목록 조회 성공
image
  • 알람 목록 조회 다음페이지 성공
image
  • 알람 목록 조회 커서 값 오류기입 시 실패 응답
image
  • 알람 목록 조회 사이즈 값 오류 기입 시 실패 응답
image
  • 변경 사항

    • 알람 약관 동의, FCM 토큰 등록, 알람 활성화·비활성화 API를 추가했습니다.
    • 일정 및 준비·실행 항목의 리마인드 생성·조회·수정·취소 기능을 추가했습니다.
    • Redis ZSET 기반 지연 알람 큐와 15초 주기 FCM 발송 컨슈머를 구현했습니다.
    • 반복 일정과 항목의 다음 회차 알람을 자동 예약합니다.
    • 완료된 항목의 예약 알람을 취소합니다.
    • 일정 및 하위 항목 변경 시 알람 정보를 보정합니다.
    • 알람 목록의 커서 기반 페이지네이션과 Swagger 문서를 추가했습니다.
    • Firebase Admin SDK, FCM 전송 처리, 재시도 및 운영 환경 인증 설정을 추가했습니다.
  • 변경 목적

    • 일정과 준비·실행 항목에 대한 FCM 리마인드 알림을 제공합니다.
    • 일정 변경, 완료 상태, 반복 설정을 예약 알람에 반영합니다.
  • 호환성 및 주요 영향

    • users 테이블에 alarm_state 컬럼을 추가했습니다.
    • terms.term_typeALARM 값을 추가했습니다.
    • 기존 API 계약을 변경하지 않습니다.
    • FIREBASE_CREDENTIALS_BASE64 설정이 필요합니다.
  • 테스트

    • 변경된 테스트 파일은 없습니다.
    • 자동화된 테스트 실행 내역은 확인되지 않았습니다.

@taerimiiii taerimiiii self-assigned this Aug 10, 2026
@taerimiiii taerimiiii added the feat 새로운 기능 추가 label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@taerimiiii, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 29 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f8763823-3dc6-4433-a176-ef64eafbc455

📥 Commits

Reviewing files that changed from the base of the PR and between 6da54f8 and 66d19a3.

📒 Files selected for processing (6)
  • src/main/java/com/tryna/domain/reminder/entity/AlarmReminderQueueOutbox.java
  • src/main/java/com/tryna/domain/reminder/repository/AlarmReminderQueueOutboxRepository.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderQueueOutboxRelay.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderQueueRelayService.java
  • src/main/resources/db/migration/V19__add_reminder_outbox.sql
📝 Walkthrough

Walkthrough

The PR adds Firebase Cloud Messaging, alarm consent and state management, alarm REST endpoints, cursor-based alarm queries, reminder scheduling, Redis delayed delivery, recurring reminder rescheduling, and completion-based reminder cancellation.

Changes

Alarm and Reminder Delivery

Layer / File(s) Summary
Alarm platform and Firebase foundation
build.gradle, .github/workflows/deploy.yml, src/main/java/com/tryna/global/..., src/main/java/com/tryna/domain/term/..., src/main/java/com/tryna/domain/user/..., src/main/resources/...
Adds Firebase configuration, FCM validation and delivery, alarm consent storage, alarm state, scheduling enablement, migration support, and alarm error mappings.
Alarm contracts and API workflows
src/main/java/com/tryna/domain/alarm/controller/..., src/main/java/com/tryna/domain/alarm/dto/*, src/main/java/com/tryna/domain/alarm/service/..., src/main/java/com/tryna/domain/alarm/util/AlarmCursorCodec.java, src/main/java/com/tryna/domain/reminder/repository/RemindersRepository.java
Adds alarm endpoints, API documentation, response records, push-token registration, alarm-state and term workflows, and cursor-based alarm queries.
Reminder scheduling and persistence
src/main/java/com/tryna/domain/reminder/entity/Reminders.java, src/main/java/com/tryna/domain/reminder/repository/..., src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java, src/main/java/com/tryna/domain/event/util/EventRecurrenceCalculator.java
Adds reminder lifecycle operations, delayed-queue access, recurrence calculation, reminder creation, correction, cancellation, and notification content calculation.
Scheduled dispatch and completion cleanup
src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java, src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java, src/main/java/com/tryna/domain/action/service/ActionItemService.java
Adds scheduled reminder dispatch, FCM notification delivery, retry handling, reminder status transitions, recurring rescheduling, and cancellation when non-recurring action items are completed.

Possibly related PRs

  • tryna-team/backend#28: Both changes use Reminders and RemindersRepository for reminder lifecycle behavior.
  • tryna-team/backend#40: This PR extends ActionItemService to cancel reminders when action items are completed.
  • tryna-team/backend#145: Both changes modify reminder lifecycle handling for deleted or completed targets.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 대부분의 알람 요구 사항을 구현했지만 준비·실행 항목 수정에 따른 알람 수정 API 구현이 확인되지 않습니다 [#222]. 준비·실행 항목 수정 시 기존 알람의 발송 시각, 제목, 본문을 보정하는 API와 관련 로직을 추가하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 19.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 일정 및 준비·실행 항목 알람 구현이라는 주요 변경 사항을 명확하게 요약합니다.
Description check ✅ Passed 이슈 번호, 기능 설명, 작업 내용, 체크리스트, 선택적 스크린샷을 포함해 템플릿을 충실히 작성했습니다.
Out of Scope Changes check ✅ Passed Firebase, Redis 지연 큐, 알람 API, 재예약, 취소, 조회 변경은 모두 연결된 알람 기능 요구 사항과 관련됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/larfor/222-alarm

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 19

🧹 Nitpick comments (13)
src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java (1)

81-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the original cause before you map it to a 500 error code.

Both try blocks catch every Exception and replace it with AlarmErrorCode.F101_EVENT_ALARM_500 or AlarmErrorCode.F102_ACTIONITEM_ALARM_500. The stack trace is discarded. A constraint violation, a Redis timeout, and a mapping error all become the same opaque response, which makes production diagnosis hard.

Also note that the two blocks are structurally identical. Extract a shared helper if you touch this area again.

♻️ Proposed change
         } catch (BusinessException e) {
             throw e;
         } catch (Exception e) {
+            log.error("일정 리마인더 생성에 실패했습니다. userId={}, eventId={}", userId, eventId, e);
             throw new BusinessException(AlarmErrorCode.F101_EVENT_ALARM_500);
         }

Add @Slf4j to the class.

Also applies to: 122-139

🤖 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/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`
around lines 81 - 98, Add Lombok `@Slf4j` to AlarmReminderScheduleService and log
the caught original exception, including its stack trace, immediately before
converting it to F101_EVENT_ALARM_500 or F102_ACTIONITEM_ALARM_500 in both try
blocks. Preserve BusinessException propagation and the existing mapped error
responses; optionally use a shared helper for the identical logging-and-mapping
logic.
src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java (1)

31-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a parameterized script type instead of the raw List.

RedisScript<List> forces the unchecked cast at Line 53. Declare the script as RedisScript<List<String>> with an explicit type reference, or keep the raw form and document why. The current form compiles with a raw-type warning.

Also note the unpack(dueItems) call. Lua limits the argument count of unpack. The current caller passes BATCH_SIZE = 50, so this is safe today. If any caller raises limit to several thousand, the script fails. Consider clamping limit inside popDue.

🤖 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/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java`
around lines 31 - 37, Update POP_DUE_SCRIPT to use a parameterized
RedisScript<List<String>> with an explicit type reference, and adjust popDue to
remove the resulting unchecked conversion. In popDue, clamp the requested limit
to a safe maximum before passing it to the Lua script so unpack(dueItems) cannot
exceed Lua’s argument limit, while preserving normal BATCH_SIZE behavior.
src/main/java/com/tryna/domain/alarm/util/AlarmCursorCodec.java (1)

29-29: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Specify the charset explicitly.

String.getBytes() uses the platform default charset. The decoder on line 42 lets Jackson detect the encoding. The current payload holds only ASCII, so the values match today, but the pairing breaks if the payload ever carries non-ASCII text. Use StandardCharsets.UTF_8 on both sides.

♻️ Proposed change
-            return Base64.getUrlEncoder().withoutPadding().encodeToString(json.getBytes());
+            return Base64.getUrlEncoder().withoutPadding()
+                    .encodeToString(json.getBytes(StandardCharsets.UTF_8));

Add the import:

import java.nio.charset.StandardCharsets;
🤖 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/com/tryna/domain/alarm/util/AlarmCursorCodec.java` at line 29,
Update the encoding and decoding logic in AlarmCursorCodec to use
StandardCharsets.UTF_8 explicitly instead of the platform-default charset,
adding the necessary import and applying it consistently to both String-to-byte
and byte-to-String conversions.
src/main/resources/application.yaml (1)

20-21: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider requiring the credentials in production profiles.

The empty default lets the application start without Firebase credentials. FcmPushService then logs a warning and drops every push. Alarm delivery fails silently in that state. If you want local runs to stay easy, keep the empty default for the local profile and require the value in the production profile, or add a startup health indicator that reports the disabled state.

🤖 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/resources/application.yaml` around lines 20 - 21, Update the
Firebase credentials configuration used by FcmPushService so production requires
FIREBASE_CREDENTIALS_BASE64 instead of defaulting to empty, while preserving an
empty default for the local profile if needed. Ensure production startup fails
or clearly reports the missing credential rather than allowing pushes to be
silently dropped.
src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java (2)

76-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the original failure information.

The generic catch block discards the exception. No log entry and no cause remain, so a production F100_ALARMLIST_500 gives no way to find the root cause. Log the exception and attach it as the cause.

♻️ Proposed change
         } catch (BusinessException e) {
             throw e;
         } catch (Exception e) {
+            log.error("알람 목록 조회에 실패했습니다. userId={}", userId, e);
             throw new BusinessException(AlarmErrorCode.F100_ALARMLIST_500);
         }

Add @Slf4j to the class for the logger. Confirm that BusinessException can carry a cause; if it cannot, the log entry alone is enough.

🤖 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/com/tryna/domain/alarm/service/AlarmQueryService.java` around
lines 76 - 80, Update the generic catch block in AlarmQueryService to log the
original exception with the class logger, and preserve it as the cause when
constructing BusinessException if that constructor supports causes. Add Lombok’s
`@Slf4j` annotation to AlarmQueryService; if BusinessException cannot accept a
cause, retain the exception details in the log while keeping the existing error
code.

83-86: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reconsider F100_ALARMLIST_409 for the single-alarm path.

validateActiveUser runs for both getAlarm and getMyAlarms. A deleted user calling GET /api/v1/alarms/my/{reminderId} receives the alarm-list error code and its message about the list query. Use a shared user-state error code, or add a dedicated code for the detail endpoint, so the response matches the called endpoint.

The local variable user is also unused; the call can stand alone.

🤖 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/com/tryna/domain/alarm/service/AlarmQueryService.java` around
lines 83 - 86, The validateActiveUser method currently uses the
alarm-list-specific F100_ALARMLIST_409 code for both detail and list requests;
replace it with a shared user-state code or a dedicated detail-endpoint code,
and remove the unused user local by invoking the repository lookup directly
while preserving the exception behavior.
src/main/java/com/tryna/domain/reminder/repository/RemindersRepository.java (1)

20-36: 🚀 Performance & Scalability | 🔵 Trivial

Confirm the supporting index for the keyset query.

The keyset predicate and the ORDER BY r.updatedAt DESC, r.reminderId DESC need a composite index on (user_id, updated_at DESC, reminder_id DESC). Without it, every alarm-list request sorts the user's full reminder set. The existence checks on lines 39-47 and the collection queries on lines 50-63 likewise need indexes on target_event_id plus reminder_status and on target_action_item_id plus reminder_status.

#!/bin/bash
# Show the reminders table definition and every index declared for it.
fd -e sql . src/main/resources/db/migration --exec rg -n -i -C6 'CREATE TABLE[[:space:]]+(IF NOT EXISTS[[:space:]]+)?reminders\b|CREATE[[:space:]]+(UNIQUE[[:space:]]+)?INDEX[^;]*reminders'

# Show index declarations on the JPA entity.
fd -g 'Reminders.java' src/main/java --exec rg -n -C3 '`@Table`|`@Index`|`@Column`'
🤖 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/com/tryna/domain/reminder/repository/RemindersRepository.java`
around lines 20 - 36, Update the reminders database schema or the Reminders
entity indexing configuration to add composite indexes for (user_id, updated_at
DESC, reminder_id DESC), (target_event_id, reminder_status), and
(target_action_item_id, reminder_status), reusing the project’s existing
migration or `@Table/`@Index conventions.
src/main/java/com/tryna/global/exception/GlobalExceptionHandler.java (1)

70-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider replacing the URI chain with a declarative map.

Each new feature adds another branch to this chain, so the handler now knows about every endpoint. A Map of method plus path pattern to ErrorCode, or an @ExceptionHandler in a controller advice scoped to the alarm package, keeps the routing knowledge next to the feature.

Also note that line 72 uses startsWith, so any future path with the /api/v1/alarms/my prefix inherits F100_ALARMLIST_400_3. If you only want the list and detail endpoints, use an AntPathMatcher pattern such as /api/v1/alarms/my and /api/v1/alarms/my/*.

🤖 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/com/tryna/global/exception/GlobalExceptionHandler.java` around
lines 70 - 73, Refactor the endpoint-specific URI chain in
GlobalExceptionHandler into a declarative method-and-path mapping, or move
alarm-specific handling into scoped controller advice. Preserve the existing
error codes, and replace the broad startsWith matching for /api/v1/alarms/my
with exact matching for the list endpoint plus a single-segment detail pattern
so unrelated prefixed paths do not inherit F100_ALARMLIST_400_3.
src/main/java/com/tryna/domain/alarm/controller/docs/AlarmControllerDocs.java (1)

119-131: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a distinct path for alarm correction.

correctEventAlarms documents PATCH /api/v1/alarms/{eventId}, and getAlarm documents GET /api/v1/alarms/{reminderId}. The same single-segment template carries two different identifier types. Clients and future maintainers can confuse an eventId with a reminderId. A path such as /api/v1/alarms/correct/event/{eventId} matches the existing /remind/event/{eventId} style and removes the ambiguity.

🤖 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/com/tryna/domain/alarm/controller/docs/AlarmControllerDocs.java`
around lines 119 - 131, Update the route mapping for correctEventAlarms to use a
distinct event-correction path such as /api/v1/alarms/correct/event/{eventId},
matching the existing /remind/event/{eventId} convention, while keeping
getAlarm’s reminderId path unchanged.
src/main/java/com/tryna/domain/alarm/controller/AlarmController.java (1)

45-50: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider centralizing the authentication guard.

All eight endpoints repeat the same userId == null check. Extract it into a private helper, or resolve it once through an argument resolver or @PreAuthorize. This removes eight copies of the same branch and prevents a future endpoint from omitting the check.

♻️ Example helper
+    private Long requireUserId(Long userId) {
+        if (userId == null) {
+            throw new BusinessException(AuthErrorCode.AUTH_401);
+        }
+        return userId;
+    }
🤖 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/com/tryna/domain/alarm/controller/AlarmController.java` around
lines 45 - 50, Centralize the repeated authentication validation used by the
AlarmController endpoints: extract the userId == null check and AUTH_401
exception into a private helper, then reuse it from agreeAlarmTerm and the other
authenticated endpoint methods. Preserve the existing authenticated behavior and
exception type while removing duplicated guard branches.
src/main/java/com/tryna/domain/alarm/service/AlarmTermService.java (1)

36-42: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use distinct error codes for the two failure causes.

Line 38 and line 41 both throw F100_ALARM_TERM_400. Line 38 means the ALARM term row does not exist, which is a server-side configuration problem. Line 41 means the user already agreed, which is a client conflict. Clients cannot tell the two apart, and a missing term row is reported as a client error. Add a 409-style code for the duplicate agreement and a 500-style code for the missing term, consistent with F100_PUSH_TOKEN_409 and F100_PUSH_TOKEN_500 in AlarmPushTokenService.

🤖 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/com/tryna/domain/alarm/service/AlarmTermService.java` around
lines 36 - 42, Use distinct error codes in AlarmTermService: replace the missing
ALARM term exception in the findLatestTermsByTypes flow with a 500-style code,
and replace the existing-agreement exception in the userAgreedTermsRepository
check with a 409-style code. Add or reuse AlarmErrorCode entries consistent with
F100_PUSH_TOKEN_500 and F100_PUSH_TOKEN_409.
src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java (2)

21-23: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Remove @Transactional from this method.

This method touches only Redis and Firebase. Neither participates in the JPA transaction, so the annotation grants no atomicity or rollback. It does hold a database connection for the whole method, including the blocking FCM dry-run send inside fcmPushService.validateToken. Under a slow Firebase response, connections are held for no benefit.

♻️ Proposed change
-    `@Transactional`
     public void registerPushToken(Long userId, String fcmPushToken) {

Also confirm that the Firebase SDK in use is configured with a send timeout. Without one, a slow Firebase response occupies the request thread.

🤖 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/com/tryna/domain/alarm/service/AlarmPushTokenService.java`
around lines 21 - 23, Remove `@Transactional` from registerPushToken in
AlarmPushTokenService so the Redis/Firebase-only flow does not hold a JPA
connection; also verify that the configured Firebase SDK client has an explicit
send timeout and add or configure one if missing.

36-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Decouple the push-token TTL from the refresh-token lifetime.

The TTL comes from jwtTokenProvider.getRefreshExpirationSeconds(). The lifetime of an FCM push token and the lifetime of a JWT refresh token are unrelated concerns. A change to the refresh-token policy silently changes push retention. Introduce a dedicated configuration property for the push-token TTL.

🤖 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/com/tryna/domain/alarm/service/AlarmPushTokenService.java`
around lines 36 - 37, Update AlarmPushTokenService to derive the FCM token
Duration from a dedicated push-token TTL configuration property rather than
jwtTokenProvider.getRefreshExpirationSeconds(). Inject or reuse the established
configuration mechanism, and pass that value to fcmTokenRedisRepository.add
while leaving refresh-token expiration handling unchanged.
🤖 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.

Inline comments:
In @.github/workflows/deploy.yml:
- Line 110: Remove FIREBASE_CREDENTIALS_BASE64 from the task definition’s
environment-variables and store it in AWS Secrets Manager or SSM Parameter Store
instead. Configure the ECS container secrets entry with valueFrom referencing
that secret, and grant access only to the task execution role.

In `@src/main/java/com/tryna/domain/alarm/controller/AlarmController.java`:
- Around line 159-161: Update the success message returned by the action-item
endpoint in AlarmController so it identifies a 준비/실행 항목 reminder alarm rather
than a 일정 reminder, and accurately states that the reminder was scheduled rather
than sent. Leave the existing response code and response payload unchanged.
- Around line 142-144: Update the response code in AlarmController’s success
response from F101_EVEMT_ALARM_200 to F101_EVENT_ALARM_200, matching the
AlarmErrorCode token and preserving the existing response message and payload.

In `@src/main/java/com/tryna/domain/alarm/dto/AlarmDetailResponse.java`:
- Around line 20-33: Update AlarmDetailResponse.from to read both targetEvent
and targetActionItem associations into local variables and null-check them
before accessing their IDs, rather than inferring the association solely from
TargetType. Preserve null IDs for missing associations so inconsistent reminders
do not throw during AlarmQueryService.getMyAlarms mapping, and remove the
TargetType import if it is no longer used.

In `@src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java`:
- Around line 32-34: Update the registration method containing the
F100_PUSH_TOKEN_409 guard to allow rotated FCM tokens: inspect
FcmTokenRedisRepository’s available write operations and replace the user’s
stored token or add the new token for multi-device support. Do not reject merely
because any token exists; only retain a 409 when the identical token is already
stored if that product rule is required.

In `@src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java`:
- Around line 64-70: Update the cursor pagination flow in AlarmQueryService and
its matching query logic to use an immutable ordering key, preferably createdAt
plus reminderId, instead of updatedAt. Encode the same immutable fields in
AlarmCursorCodec.Cursor and ensure the repository ordering and cursor predicates
use that identical keyset order.

In `@src/main/java/com/tryna/domain/alarm/service/FcmPushService.java`:
- Around line 31-34: Update
src/main/java/com/tryna/domain/alarm/service/FcmPushService.java:31-34 to return
UNAVAILABLE when FirebaseApp.getApps() is empty, unless a real token-format
validation is performed; update FcmPushService.send at
src/main/java/com/tryna/domain/alarm/service/FcmPushService.java:58-80 to return
a delivery outcome that lets the dispatcher retry transient failures and remove
UNREGISTERED tokens; update src/main/resources/application.yaml:20-21 to require
FIREBASE_CREDENTIALS_BASE64 in production or expose Firebase’s disabled state
through a health indicator.
- Around line 31-34: Update the Firebase-uninitialized branch in FcmPushService
to return TokenValidationResult.UNAVAILABLE instead of VALID, ensuring unchecked
non-blank tokens are not stored as usable tokens and callers can return
F100_PUSH_TOKEN_500. Only retain VALID if this branch first performs the
required token length and character-set validation.
- Around line 40-42: Configure finite Firebase connect and read timeouts during
FirebaseApp initialization, using the existing FirebaseOptions setup rather than
changing FcmPushService.send validation flow. Ensure
FirebaseMessaging.send(message, true) uses those bounded timeout values so
stalled FCM requests cannot block the token-validation request indefinitely.

In `@src/main/java/com/tryna/domain/event/util/EventRecurrenceCalculator.java`:
- Around line 55-77: Update nextMonthly and nextYearly to derive the target
month or year from current without retaining current’s clamped day, then apply
event.getRecurrenceDayOfMonth() to that target using the month’s valid-day
clamp. Preserve the existing behavior when recurrenceDayOfMonth is null and
ensure configured dates such as 31 or February 29 recover their canonical day in
later occurrences.

In
`@src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java`:
- Around line 49-65: Add a re-enqueue method to AlarmDelayedQueueRepository
alongside popDue that schedules a reminder ID back into QUEUE_KEY with a short
backoff score, then update AlarmReminderDispatcher.dispatchDueReminders to
invoke it whenever dispatchOne fails. Preserve the existing SCHEDULED state and
ensure the failed reminder is available for a later poll instead of being
permanently removed.

In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java`:
- Around line 164-168: Update AlarmReminderDispatcher.java at lines 164-168 and
188-192: in both reschedule paths, repeatedly advance the occurrence with
EventRecurrenceCalculator.nextOccurrenceDateAfter until
computeEventReminderTimeForOccurrence or
computeActionItemReminderTimeForOccurrence returns a future time, enforcing a
maximum number of advances; schedule only that future time, and call markSent()
in the action-item path when no future occurrence is found.
- Around line 110-123: Update sendPush to handle FCM delivery per token without
allowing one failure to abort the entire reminder dispatch: catch and log
exceptions for individual fcmPushService.send calls, continue processing
remaining tokens, and return success only when at least one token is delivered
while preserving the existing false result for an empty token set. If
FcmPushService exposes sendEachForMulticast, prefer its per-token result
handling to avoid sequential network calls and still report partial success.
- Around line 46-78: Move the transactional per-reminder logic from
AlarmReminderDispatcher.dispatchOne into a separate Spring-managed
AlarmReminderDispatchExecutor bean, including its required collaborators, and
invoke that bean from dispatchDueReminders instead of self-invoking dispatchOne.
Preserve the existing dispatch behavior while ensuring each item runs through
the transactional proxy; add a test verifying the reminder status is persisted
after one dispatch cycle.

In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`:
- Around line 251-255: Update buildEventAlarmBody to ensure the concatenated
date-time and description never exceed the 255-character alarm_body column
limit. Truncate the final trimmed body to 255 characters while preserving the
existing formatting and null-description handling.
- Around line 160-175: Update correctEventAlarms to validate recomputed
schedules before rescheduling: compute the event schedule once outside its
reminder loop, and process each event or action-item reminder only when
isFuture(scheduledAt) returns true, preventing null or past queue entries. Add
the isFuture helper using a non-null check and comparison with the current time.
For reminders that fail validation, confirm and implement the intended
behavior—skip them or cancel via markCanceled() and
alarmDelayedQueueRepository.cancel(...).
- Around line 307-318: Update deriveActionItemOccurrenceDate in
AlarmReminderScheduleService to handle recurring TIMED_ACTION items with null
offsetDays by deriving a valid occurrence relative to the recurring reminder
rather than reusing the stored occurrenceDate. Add the smallest appropriate
migration or runtime guard, while preserving existing behavior for non-recurring
items and timed actions with a defined offset.

In `@src/main/java/com/tryna/global/exception/AlarmErrorCode.java`:
- Around line 85-101: Correct the code string values in the three
F101_EVENT_ALARM_400_1, F101_EVENT_ALARM_400_2, and F101_EVENT_ALARM_400_3 enum
entries from “EVEMT” to “EVENT” so they match the enum names and public API
contract. If “EVEMT” is specification-mandated, retain it and document that
spelling as intentional instead.
- Around line 139-155: Update the messages for F102_ACTIONITEM_ALARM_403,
F102_ACTIONITEM_ALARM_409, and F102_ACTIONITEM_ALARM_500 in AlarmErrorCode so
they refer to the relevant action item (준비/실행 항목) instead of 일정, while
preserving each message’s permission, duplicate-alarm, and push-send error
meaning.

---

Nitpick comments:
In `@src/main/java/com/tryna/domain/alarm/controller/AlarmController.java`:
- Around line 45-50: Centralize the repeated authentication validation used by
the AlarmController endpoints: extract the userId == null check and AUTH_401
exception into a private helper, then reuse it from agreeAlarmTerm and the other
authenticated endpoint methods. Preserve the existing authenticated behavior and
exception type while removing duplicated guard branches.

In
`@src/main/java/com/tryna/domain/alarm/controller/docs/AlarmControllerDocs.java`:
- Around line 119-131: Update the route mapping for correctEventAlarms to use a
distinct event-correction path such as /api/v1/alarms/correct/event/{eventId},
matching the existing /remind/event/{eventId} convention, while keeping
getAlarm’s reminderId path unchanged.

In `@src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java`:
- Around line 21-23: Remove `@Transactional` from registerPushToken in
AlarmPushTokenService so the Redis/Firebase-only flow does not hold a JPA
connection; also verify that the configured Firebase SDK client has an explicit
send timeout and add or configure one if missing.
- Around line 36-37: Update AlarmPushTokenService to derive the FCM token
Duration from a dedicated push-token TTL configuration property rather than
jwtTokenProvider.getRefreshExpirationSeconds(). Inject or reuse the established
configuration mechanism, and pass that value to fcmTokenRedisRepository.add
while leaving refresh-token expiration handling unchanged.

In `@src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java`:
- Around line 76-80: Update the generic catch block in AlarmQueryService to log
the original exception with the class logger, and preserve it as the cause when
constructing BusinessException if that constructor supports causes. Add Lombok’s
`@Slf4j` annotation to AlarmQueryService; if BusinessException cannot accept a
cause, retain the exception details in the log while keeping the existing error
code.
- Around line 83-86: The validateActiveUser method currently uses the
alarm-list-specific F100_ALARMLIST_409 code for both detail and list requests;
replace it with a shared user-state code or a dedicated detail-endpoint code,
and remove the unused user local by invoking the repository lookup directly
while preserving the exception behavior.

In `@src/main/java/com/tryna/domain/alarm/service/AlarmTermService.java`:
- Around line 36-42: Use distinct error codes in AlarmTermService: replace the
missing ALARM term exception in the findLatestTermsByTypes flow with a 500-style
code, and replace the existing-agreement exception in the
userAgreedTermsRepository check with a 409-style code. Add or reuse
AlarmErrorCode entries consistent with F100_PUSH_TOKEN_500 and
F100_PUSH_TOKEN_409.

In `@src/main/java/com/tryna/domain/alarm/util/AlarmCursorCodec.java`:
- Line 29: Update the encoding and decoding logic in AlarmCursorCodec to use
StandardCharsets.UTF_8 explicitly instead of the platform-default charset,
adding the necessary import and applying it consistently to both String-to-byte
and byte-to-String conversions.

In
`@src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java`:
- Around line 31-37: Update POP_DUE_SCRIPT to use a parameterized
RedisScript<List<String>> with an explicit type reference, and adjust popDue to
remove the resulting unchecked conversion. In popDue, clamp the requested limit
to a safe maximum before passing it to the Lua script so unpack(dueItems) cannot
exceed Lua’s argument limit, while preserving normal BATCH_SIZE behavior.

In `@src/main/java/com/tryna/domain/reminder/repository/RemindersRepository.java`:
- Around line 20-36: Update the reminders database schema or the Reminders
entity indexing configuration to add composite indexes for (user_id, updated_at
DESC, reminder_id DESC), (target_event_id, reminder_status), and
(target_action_item_id, reminder_status), reusing the project’s existing
migration or `@Table/`@Index conventions.

In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`:
- Around line 81-98: Add Lombok `@Slf4j` to AlarmReminderScheduleService and log
the caught original exception, including its stack trace, immediately before
converting it to F101_EVENT_ALARM_500 or F102_ACTIONITEM_ALARM_500 in both try
blocks. Preserve BusinessException propagation and the existing mapped error
responses; optionally use a shared helper for the identical logging-and-mapping
logic.

In `@src/main/java/com/tryna/global/exception/GlobalExceptionHandler.java`:
- Around line 70-73: Refactor the endpoint-specific URI chain in
GlobalExceptionHandler into a declarative method-and-path mapping, or move
alarm-specific handling into scoped controller advice. Preserve the existing
error codes, and replace the broad startsWith matching for /api/v1/alarms/my
with exact matching for the list endpoint plus a single-segment detail pattern
so unrelated prefixed paths do not inherit F100_ALARMLIST_400_3.

In `@src/main/resources/application.yaml`:
- Around line 20-21: Update the Firebase credentials configuration used by
FcmPushService so production requires FIREBASE_CREDENTIALS_BASE64 instead of
defaulting to empty, while preserving an empty default for the local profile if
needed. Ensure production startup fails or clearly reports the missing
credential rather than allowing pushes to be silently dropped.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b23e92ee-0aa9-4727-9317-5bd6d4a81c3a

📥 Commits

Reviewing files that changed from the base of the PR and between c811702 and 01c026c.

📒 Files selected for processing (33)
  • .github/workflows/deploy.yml
  • build.gradle
  • src/main/java/com/tryna/TrynaApplication.java
  • src/main/java/com/tryna/domain/action/service/ActionItemService.java
  • src/main/java/com/tryna/domain/alarm/controller/AlarmController.java
  • src/main/java/com/tryna/domain/alarm/controller/docs/AlarmControllerDocs.java
  • src/main/java/com/tryna/domain/alarm/dto/ActionItemReminderResponse.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmCorrectionResponse.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmDetailResponse.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmListResponse.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmPushTokenRequest.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmStateResponse.java
  • src/main/java/com/tryna/domain/alarm/dto/EventReminderResponse.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmStateService.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmTermService.java
  • src/main/java/com/tryna/domain/alarm/service/FcmPushService.java
  • src/main/java/com/tryna/domain/alarm/util/AlarmCursorCodec.java
  • src/main/java/com/tryna/domain/event/util/EventRecurrenceCalculator.java
  • src/main/java/com/tryna/domain/reminder/entity/Reminders.java
  • src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java
  • src/main/java/com/tryna/domain/reminder/repository/RemindersRepository.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java
  • src/main/java/com/tryna/domain/term/enums/TermType.java
  • src/main/java/com/tryna/domain/term/repository/UserAgreedTermsRepository.java
  • src/main/java/com/tryna/domain/user/entity/Users.java
  • src/main/java/com/tryna/global/config/FirebaseConfig.java
  • src/main/java/com/tryna/global/exception/AlarmErrorCode.java
  • src/main/java/com/tryna/global/exception/GlobalExceptionHandler.java
  • src/main/resources/application.yaml
  • src/main/resources/db/migration/V18__add_alarm_term_and_state.sql

Comment thread .github/workflows/deploy.yml
Comment thread src/main/java/com/tryna/domain/alarm/dto/AlarmDetailResponse.java
Comment thread src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java Outdated
Comment thread src/main/java/com/tryna/global/exception/AlarmErrorCode.java
Comment thread src/main/java/com/tryna/global/exception/AlarmErrorCode.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java (1)

74-93: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Map reminder uniqueness conflicts to 409.

The unique indexes already prevent concurrent duplicate inserts for the same target, schedule time, and delivery channel. Map DataIntegrityViolationException to F101_EVENT_ALARM_409 or F102_ACTIONITEM_ALARM_409 instead of returning a 500 response.

🤖 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/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`
around lines 74 - 93, Update the exception handling in
AlarmReminderScheduleService around the remindersRepository.save calls to catch
DataIntegrityViolationException caused by reminder uniqueness constraints and
throw F101_EVENT_ALARM_409 for event reminders or F102_ACTIONITEM_ALARM_409 for
action-item reminders. Preserve existing handling for unrelated exceptions.
🤖 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.

Inline comments:
In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`:
- Line 63: Update AlarmReminderScheduleService validation calls to use dedicated
400 error codes for disabled alarms: replace the codes passed at the alarm-state
checks near validateAlarmStateEnabled with new distinct disabled-state codes,
while preserving F101_EVENT_ALARM_403 and F102_ACTIONITEM_ALARM_403 for
ownership-denial checks.

In `@src/main/java/com/tryna/global/config/SecurityConfig.java`:
- Around line 45-46: Require authentication for alarm consent and FCM token
registration: remove both alarm paths from allowUris in SecurityConfig.java;
update the corresponding AlarmController methods to use `@AuthenticationPrincipal`
Long userId with a null check; and update both declarations in
AlarmControllerDocs.java to include `@SecurityRequirement`(name = "bearerAuth")
plus the hidden authenticated-user parameter. Apply changes in
SecurityConfig.java lines 45-46 and AlarmControllerDocs.java lines 33-45; the
controller implementation requires the related AlarmController methods.

---

Outside diff comments:
In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java`:
- Around line 74-93: Update the exception handling in
AlarmReminderScheduleService around the remindersRepository.save calls to catch
DataIntegrityViolationException caused by reminder uniqueness constraints and
throw F101_EVENT_ALARM_409 for event reminders or F102_ACTIONITEM_ALARM_409 for
action-item reminders. Preserve existing handling for unrelated exceptions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 30e848d3-3756-44fa-9066-fb54d0a0137a

📥 Commits

Reviewing files that changed from the base of the PR and between 01c026c and 669bf3c.

📒 Files selected for processing (6)
  • src/main/java/com/tryna/domain/alarm/controller/AlarmController.java
  • src/main/java/com/tryna/domain/alarm/controller/docs/AlarmControllerDocs.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java
  • src/main/java/com/tryna/domain/reminder/repository/RemindersRepository.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java
  • src/main/java/com/tryna/global/config/SecurityConfig.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/tryna/domain/alarm/service/AlarmQueryService.java
  • src/main/java/com/tryna/domain/alarm/controller/AlarmController.java

Comment thread src/main/java/com/tryna/global/config/SecurityConfig.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

Inline comments:
In `@src/main/java/com/tryna/domain/alarm/service/FcmPushService.java`:
- Around line 93-105: Update the multicast send flow in FcmPushService to
partition tokens into batches of at most 500 before calling
sendEachForMulticast. Send each batch with the existing notification and data
payload, then merge each BatchResponse’s per-token outcomes in original token
order so token/outcome alignment is preserved. Apply failure handling per batch
without marking unrelated successful batches as transient failures.
- Around line 176-180: Update mapDeliveryResult to stop treating every
INVALID_ARGUMENT as UNREGISTERED: map it to a separate permanent
delivery-failure result, while preserving UNREGISTERED for UNREGISTERED and
SENDER_ID_MISMATCH. Only classify INVALID_ARGUMENT as UNREGISTERED when the
payload is explicitly known to be valid, and ensure
AlarmReminderDispatchExecutor retains tokens for payload failures.

In
`@src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java`:
- Around line 51-53: Change ReminderRepository.reEnqueue to track retry attempts
and return false once the configured maximum is reached, marking the reminder as
failed instead of scheduling it again; otherwise preserve the one-minute backoff
and return true. Update AlarmReminderDispatchExecutor.sendPush and
AlarmReminderDispatcher.dispatchDueReminders to mark the reminder failed when
reEnqueue returns false, and clear the retry counter after a successful send.

In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java`:
- Around line 167-177: The reschedule paths reuse stale alarm-body text from the
previous occurrence. Expose the body-builder methods already used by
AlarmReminderScheduleService, then in AlarmReminderDispatchExecutor update both
the event path at
src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java:167-177
and the action-item path at :188-198 to build the body for nextScheduledAt and
pass it to reminder.reschedule instead of reminder.getAlarmBody().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5a34b3e4-6e63-47ce-9034-43618c8a88db

📥 Commits

Reviewing files that changed from the base of the PR and between 669bf3c and 4efb1e1.

📒 Files selected for processing (11)
  • src/main/java/com/tryna/domain/alarm/controller/AlarmController.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmDetailResponse.java
  • src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java
  • src/main/java/com/tryna/domain/alarm/service/FcmPushService.java
  • src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java
  • src/main/java/com/tryna/global/config/FirebaseConfig.java
  • src/main/resources/application-prod.yaml
  • src/main/resources/application.yaml
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/resources/application.yaml
  • src/main/java/com/tryna/domain/alarm/service/AlarmPushTokenService.java
  • src/main/java/com/tryna/domain/alarm/controller/AlarmController.java
  • src/main/java/com/tryna/domain/alarm/dto/AlarmDetailResponse.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderScheduleService.java

Comment thread src/main/java/com/tryna/domain/alarm/service/FcmPushService.java
Comment thread src/main/java/com/tryna/domain/alarm/service/FcmPushService.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In
`@src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java`:
- Around line 118-121: Remove the complete FCM token from the PERMANENT_FAILURE
warning in AlarmReminderDispatchExecutor, logging only reminder.getReminderId()
or an approved non-reversible token fingerprint. Apply the same redaction to the
exception log near the referenced branch, ensuring no application log includes
the raw outcome.token().
- Around line 192-193: Update AlarmReminderDispatchExecutor so the event
reminder path at
src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java:192-193
records the Redis scheduling change in an outbox within the same transaction as
reminder.reschedule, then relays it to Redis only after commit; apply the same
outbox flow to the action-item reminder path at
src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java:213-215.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da69502c-852c-493c-aef6-b8cf20a39400

📥 Commits

Reviewing files that changed from the base of the PR and between 4efb1e1 and 6da54f8.

📒 Files selected for processing (4)
  • src/main/java/com/tryna/domain/alarm/service/FcmPushService.java
  • src/main/java/com/tryna/domain/reminder/repository/AlarmDelayedQueueRepository.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatchExecutor.java
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/tryna/domain/reminder/service/AlarmReminderDispatcher.java

@taerimiiii
taerimiiii merged commit c758858 into develop Aug 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 일정 및 준비/실행 항목 알람 구현

1 participant