Skip to content

[Feat] 일정 조율 알림 구현 - #145

Merged
jyvnee merged 7 commits into
developfrom
feat/#142/date-poll-notification
Oct 9, 2026
Merged

jyvnee merged 7 commits into
developfrom
feat/#142/date-poll-notification

Conversation

@jyvnee

@jyvnee jyvnee commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

개요

#39(PR #140)에서 만든 Outbox 기반 푸시 발송 인프라에, 서비스 정책에 정의된 일정 조율 알림 4종을 연결합니다. 네 알림 모두 각 유스케이스 트랜잭션 안에서 NotificationService로 앱 내 알림과 Outbox를 함께 저장하고, 실제 푸시는 기존 Outbox Worker가 비동기로 발송합니다.

작업 내용

알림 타입 발생 시점 수신자 문구
DATE_POLL_STARTED 일정 조율(POLL) 만남 생성 생성자 제외 참여자 새로운 일정 조율이 시작됐어요, 되는 날짜를 체크해주세요
DATE_POLL_RESPONDED 참여자의 후보 일정 응답 만남 방장(본인 응답 시 제외) {닉네임} 님이 일정 조율에 응답했어요
DATE_POLL_REMINDER 방장의 리마인드 요청(신규 API) 미응답 참여자 아직 응답 안 하셨어요, 되는 날짜를 체크해주세요
MEETING_DATE_CONFIRMED 후보 일정 확정(실제 상태 전환 시) 방장 제외 참여자 일정이 {M월 D일[~M월 D일]}로 확정됐어요
  • 알림 명령 조립: MeetingNotificationCommands에서 문구와 중복 방지 키를 만듭니다. meeting → notification 단방향 의존만 추가했습니다.
  • 조율 요청 알림: MeetingService.createPollMeeting(). FIXED 만남에는 보내지 않습니다.
  • 응답 알림: MeetingDatePollService.respondDatePoll(). 방장 위임 후 방장 본인이 응답하면 보내지 않습니다.
  • 확정 알림: MeetingService.confirmMeetingDate(). 같은 날짜로 다시 확정한 멱등 요청에는 보내지 않고, 탈퇴한 멤버는 제외합니다.
  • 미응답 참여자 조회 쿼리: MeetingParticipantRepository.findAllUnrespondedActiveWithUserByMeetingId(). 생성자, 응답 기록이 있는 참여자(빈 배열 응답 포함), 탈퇴한 멤버를 제외하고 사용자까지 fetch join합니다.
  • 리마인드 서비스: MeetingDatePollService.remindUnrespondedParticipants(). 만남 row를 잠근 뒤 방장·조율 방식·조율 중 상태를 검증하고, 미응답자가 없으면 MEETING-E024를 반환합니다.
  • 리마인드 API: POST /api/v1/meetings/{meetingId}/date-poll/reminders → 204 No Content
  • Swagger 수정(fix 커밋): 리마인드와 만남 삭제 API의 204 응답에 CommonResponse 본문 스키마가 붙던 문제를 반환 타입을 ResponseEntity<Void>로 바꿔 해결했습니다.

테스트

./gradlew ktlintCheck
./gradlew test
  • 결과: 전부 성공 (567개 테스트 통과, 건너뜀 0)
  • 추가·수정한 테스트
    • MeetingServiceTest: 조율 요청 알림 명령(FIXED 만남 제외), 확정 알림 수신자(방장·탈퇴자 제외)와 멱등 재확정
    • MeetingDatePollServiceTest: 응답 알림(방장 본인 응답 제외), 리마인드 명령과 실패 흐름(E018, E024, E020)
    • MeetingRepositoryTest: 미응답 참여자 조회 조건
    • MeetingDatePollReminderIntegrationTest(신규, MySQL): 미응답자에게만 알림·Outbox 저장, 같은 시각 재요청 시 새 알림 저장
    • MeetingScheduleControllerTest: 204 본문 없음, 409 MEETING-E024
    • MeetingOpenApiTest(신규): 리마인드와 만남 삭제의 204 응답 문서에 본문 스키마가 없는지 검증
    • MeetingDatePollServiceConcurrencyTest: 응답 시 알림이 생기므로 정리 단계에서 알림·Outbox를 먼저 삭제하도록 수정
  • 의존성·설정 변경이 없어 ./gradlew build는 실행하지 않았습니다.

API 변경

  • 신규: POST /api/v1/meetings/{meetingId}/date-poll/reminders
    • 요청 본문 없음, 성공 시 204 No Content(MEETING-S010)
    • 실패: 400 MEETING-E012, 403 MEETING-E018, 404 MEETING-E010, 409 MEETING-E020, 409 MEETING-E024(신규, 미응답자 없음)
  • 기존 만남 생성·후보 일정 응답·일정 확정·만남 삭제 API의 실제 요청·응답은 변경 없음(만남 삭제는 Swagger 문서 표기만 수정)
  • Notion API 명세서 미응답자 리마인드를 구현 기준으로 갱신했습니다.

DB 및 설정 변경

  • 없음

리뷰 시 참고 사항

  • H2 서비스 테스트의 mock 사용: NotificationService가 MySQL 전용 upsert(ON DUPLICATE KEY UPDATE)를 써서 H2 기반 서비스 테스트에서는 실행되지 않습니다. 해당 테스트에서는 NotificationService를 @MockitoBean으로 대체하고 전달된 명령을 검증했으며, 실제 저장은 MySQL 통합 테스트로 확인했습니다. Kotlin non-null 매개변수와 Mockito 매처가 충돌해 mockingDetails로 호출 인자를 읽었습니다.
  • 리마인드 중복 방지 키: DATE_POLL_REMINDER:{meetingId}:{요청 시각 ms}:{요청별 UUID}:{recipientUserId}. 요청 시각만으로는 같은 밀리초 재요청이 중복 처리되는 문제가 있어 요청마다 UUID를 추가했습니다.
  • 쿨다운 미적용: 이번 범위에서 리마인드 재전송 제한을 두지 않았습니다(Notion 명세의 429 MEETING-E025 제외). 클라이언트의 요청 중 버튼 비활성화를 전제로 하며, 남용 문제가 생기면 후속 이슈로 추가합니다.
  • 만남 row 잠금: 쿨다운이 없어 필수는 아니지만, 일정 확정과 같은 잠금을 사용해 확정과 동시에 들어온 리마인드가 확정 이후 상태를 보고 E020으로 거절되도록 했습니다.
  • 명세와 다른 점: 확정 알림은 정책 표의 "여정 멤버 전원" 대신 다른 알림처럼 행동한 본인(방장)을 제외합니다. E020 메시지("일정 조율 중인 만남만 확정할 수 있습니다.")는 명세대로 재사용해 리마인드 문맥에서는 다소 어색할 수 있습니다.
  • 후속 작업
    • MEETING_PROPOSED(방장에게 여정 제안) 알림은 제안 기능 기획이 정해지지 않아 제외했습니다.
    • 그룹 삭제·계획 삭제·회원 탈퇴 API도 204 Swagger에 본문 스키마가 붙는 같은 문제가 있습니다(범위 밖).
    • Notion 설계 문서(만남 생성 및 일정 조율 API 설계)가 휴지통에 있어 202 표기를 갱신하지 못했습니다.

체크리스트

  • 변경 범위에 맞는 테스트를 실행했습니다.
  • 계층 책임을 지키고 필요한 문서를 갱신했습니다.
  • 비밀번호, 토큰, 개인정보 등 민감정보를 포함하지 않았습니다.
  • 관련 없는 변경을 포함하지 않았습니다.

Summary by CodeRabbit

  • 새 기능

    • 일정 투표를 시작하거나 참여자가 응답하면 관련 참여자에게 알림을 보냅니다.
    • 방장은 일정 투표 미응답자에게 리마인드를 보낼 수 있습니다. 미응답자가 없거나 투표가 종료된 경우 요청이 거부됩니다.
    • 일정이 확정되면 방장을 제외한 참여자에게 확정 알림을 보냅니다.
  • 개선 사항

    • 모임 삭제와 리마인드 전송은 본문 없이 응답하며, 성공 여부는 상태 코드로 확인할 수 있습니다.

@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: team-belog/belog-server/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c9f183b6-c477-4448-b0fc-f210bdfe9e4a

📥 Commits

Reviewing files that changed from the base of the PR and between fc5f19e and 2bbd655.


📒 Files selected for processing (17)
  • src/main/kotlin/org/com/belog/meeting/code/MeetingErrorCode.kt
  • src/main/kotlin/org/com/belog/meeting/code/MeetingSuccessCode.kt
  • src/main/kotlin/org/com/belog/meeting/controller/MeetingController.kt
  • src/main/kotlin/org/com/belog/meeting/controller/MeetingScheduleController.kt
  • src/main/kotlin/org/com/belog/meeting/controller/swagger/MeetingScheduleSwagger.kt
  • src/main/kotlin/org/com/belog/meeting/controller/swagger/MeetingSwagger.kt
  • src/main/kotlin/org/com/belog/meeting/repository/MeetingParticipantRepository.kt
  • src/main/kotlin/org/com/belog/meeting/service/MeetingDatePollService.kt
  • src/main/kotlin/org/com/belog/meeting/service/MeetingNotificationCommands.kt
  • src/main/kotlin/org/com/belog/meeting/service/MeetingService.kt
  • src/test/kotlin/org/com/belog/meeting/controller/MeetingOpenApiTest.kt
  • src/test/kotlin/org/com/belog/meeting/controller/MeetingScheduleControllerTest.kt
  • src/test/kotlin/org/com/belog/meeting/repository/MeetingRepositoryTest.kt
  • src/test/kotlin/org/com/belog/meeting/service/MeetingDatePollReminderIntegrationTest.kt
  • src/test/kotlin/org/com/belog/meeting/service/MeetingDatePollServiceConcurrencyTest.kt
  • src/test/kotlin/org/com/belog/meeting/service/MeetingDatePollServiceTest.kt
  • src/test/kotlin/org/com/belog/meeting/service/MeetingServiceTest.kt

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.



📝 Walkthrough

Walkthrough

일정 조율 시작·응답·리마인드·확정 시 알림과 Outbox 저장 흐름을 추가했습니다. 미응답자 리마인드 API를 추가하고, 만남 삭제 응답을 본문 없는 형식으로 변경했습니다.

Changes

일정 조율 알림

Layer / File(s) Summary
알림 명령과 모임 알림
src/main/kotlin/org/com/belog/meeting/service/MeetingNotificationCommands.kt, src/main/kotlin/org/com/belog/meeting/service/MeetingService.kt, src/test/kotlin/org/com/belog/meeting/service/MeetingServiceTest.kt
알림 명령 생성 로직을 추가했습니다. 일정 조율 모임 생성 시 참여자에게 시작 알림을 요청하고, 일정이 실제 확정된 경우 방장을 제외한 활성 참여자에게 확정 알림을 요청합니다. 관련 서비스 테스트를 추가했습니다.
응답 알림과 미응답자 리마인드
src/main/kotlin/org/com/belog/meeting/repository/MeetingParticipantRepository.kt, src/main/kotlin/org/com/belog/meeting/service/MeetingDatePollService.kt, src/test/kotlin/org/com/belog/meeting/repository/MeetingRepositoryTest.kt, src/test/kotlin/org/com/belog/meeting/service/MeetingDatePoll*Test.kt
미응답 활성 참가자 조회를 추가했습니다. 투표 응답 시 조건에 따라 방장에게 알림을 요청하고, 방장의 리마인드 요청 시 대상자에게 알림을 요청합니다. 서비스 및 통합 테스트에서 대상 선정과 반복 요청 시 저장 건수를 확인합니다.
리마인드 API와 빈 응답 계약
src/main/kotlin/org/com/belog/meeting/code/MeetingErrorCode.kt, src/main/kotlin/org/com/belog/meeting/code/MeetingSuccessCode.kt, src/main/kotlin/org/com/belog/meeting/controller/MeetingScheduleController.kt, src/main/kotlin/org/com/belog/meeting/controller/swagger/MeetingScheduleSwagger.kt, src/main/kotlin/org/com/belog/meeting/controller/MeetingController.kt, src/main/kotlin/org/com/belog/meeting/controller/swagger/MeetingSwagger.kt, src/test/kotlin/org/com/belog/meeting/controller/*Test.kt
미응답자 리마인드 POST와 성공·오류 응답 계약을 추가했습니다. 리마인드 및 만남 삭제 API의 204 응답 문서에 본문 스키마가 없는지 테스트합니다. 만남 삭제 컨트롤러와 Swagger 응답 타입을 ResponseEntity<Void>로 변경했습니다.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant MeetingScheduleController
  participant MeetingDatePollService
  participant MeetingParticipantRepository
  participant NotificationService
  MeetingScheduleController->>MeetingDatePollService: 미응답자 리마인드 요청
  MeetingDatePollService->>MeetingParticipantRepository: 미응답 활성 참가자 조회
  MeetingParticipantRepository-->>MeetingDatePollService: 미응답 참가자 목록
  MeetingDatePollService->>NotificationService: 리마인드 알림 명령 저장
Loading

Merge Risk: ⚪ Minimal · up to 2bbd6

The notification and reminder changes are mergeable after normal checks; no actionable merge-blocking issue remains.

🚥 Pre-merge checks | ✅ 2 | ❌ 2 | ❓ 1

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check Warning #142는 일정 조율 알림과 리마인드 API를 요구합니다. 이 PR은 별도로 만남 삭제 API의 MeetingController.deleteMeeting 및 MeetingSwagger.deleteMeeting 반환 타입을 변경하고, 해당 204 문서를 검증하는 테스트를 추가했습니다. 이 변경은 일정 조율 알림 구현과 직접 연결되지 않습니다. PR 설… 만남 삭제 API의 반환 타입 변경과 관련 OpenAPI 테스트를 이 PR에서 제거하십시오. 해당 수정이 필요하면 별도 이슈와 PR로 분리하십시오.
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 72 functions across 17 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check Inconclusive 직접 연결된 #142의 핵심 구현은 근거가 있습니다. MeetingService는 POLL 생성과 실제 날짜 확정 시 알림을 저장하고, MeetingDatePollService는 응답 알림과 리마인드를 저장합니다. 조회 쿼리는 생성자, 응답 기록 보유자, 탈퇴 멤버를 제외합니다. 통합·서비스·컨트롤러·데이터 계층 테스트도 추가되었습니다. `MEE… ./gradlew build를 실행하고 성공 결과를 확인하십시오. 실패하면 실패한 컴파일 또는 테스트를 수정한 뒤 다시 실행하십시오.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check Passed PR 설명은 템플릿의 필수 섹션을 모두 포함합니다. 변경 목적, 작업 내용, 테스트 결과, API 변경, DB 및 설정 변경, 참고 사항, 체크리스트를 구체적으로 작성했습니다. ./gradlew build를 실행하지 않은 사유도 명시했습니다.
Title check Passed 제목 [Feat] 일정 조율 알림 구현은 네 가지 일정 조율 알림과 리마인드 API를 추가하는 주요 변경을 간결하게 설명합니다.

Full details: Linked Issues check

Explanation

직접 연결된 #142의 핵심 구현은 근거가 있습니다. MeetingService는 POLL 생성과 실제 날짜 확정 시 알림을 저장하고, MeetingDatePollService는 응답 알림과 리마인드를 저장합니다. 조회 쿼리는 생성자, 응답 기록 보유자, 탈퇴 멤버를 제외합니다. 통합·서비스·컨트롤러·데이터 계층 테스트도 추가되었습니다. MEETING_PROPOSED와 쿨다운 제외는 #142의 범위와 일치합니다. 그러나 #142의 완료 조건인 ./gradlew build는 실행되지 않아 전체 코딩 요구사항의 통과를 확인할 수 없습니다.


Full details: Out of Scope Changes check

Explanation

#142는 일정 조율 알림과 리마인드 API를 요구합니다. 이 PR은 별도로 만남 삭제 API의 MeetingController.deleteMeeting 및 MeetingSwagger.deleteMeeting 반환 타입을 변경하고, 해당 204 문서를 검증하는 테스트를 추가했습니다. 이 변경은 일정 조율 알림 구현과 직접 연결되지 않습니다. PR 설명도 기존 만남 삭제 API의 실제 동작은 변경하지 않는다고 명시하므로, 관련 없는 API 문서 수정에 해당합니다.



  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@jyvnee jyvnee self-assigned this Oct 9, 2026
@jyvnee jyvnee added the ✨ feature New feature or request label Oct 9, 2026
@jyvnee
jyvnee merged commit 5e035e0 into develop Oct 9, 2026
2 checks passed
@jyvnee
jyvnee deleted the feat/#142/date-poll-notification branch October 9, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 일정 조율 알림 구현

1 participant