[BE-73] 변경 공고 알림 — 24시간 1회 제한 (FR-93)
작업 내용 (설계 의도)
근거 TDD: 20260808-지원관리-확장-tdd.md — “방안 11 변경 공고 알림의 24시간 제한”
변경 사항
- “변경 공고” 알림을 신설합니다 — 공고 내용이 변경됐다는 사실을 알립니다. FR-36이 제외하는 것은 “공고가 CLOSED로 전환됐다는 사실”이고, 내용 변경 통보는 그 범위 밖입니다(PRD 관계 정리).
- 24시간 1회 제한을 멱등 키로 표현합니다 —
JOB_POSTING:{id}:JOB_POSTING_CHANGED:{kstDayOrdinal(changedAt)}.DAILY_DIGEST가 이미 같은 패턴을 씁니다(IdempotencyKey.kt:37-38,NotificationDispatchDomainService.kt:91-103).- 롤링 24시간 판정 대신 KST 일자 서수를 쓰는 근거: 알림 배치가 09:00 하루 1회이므로(
application.yml:46) 같은 공고에 두 번 발송될 창이 애초에 없고, 롤링 판정은 매번 발송 이력 조회를 요구하며 기존 멱등 키 규칙을 벗어납니다. 명시적 키는 수동 트리거·재실행 시의 중복을 방어합니다.
- 롤링 24시간 판정 대신 KST 일자 서수를 쓰는 근거: 알림 배치가 09:00 하루 1회이므로(
- 대상 산출 —
JobPostingRepository.findAllChangedSince(baseTime)신설(대표 &&changed_at >= baseTime). 여기에 매칭 성립 && 미지원 필터를 application 레이어가 얹습니다(크로스 컨텍스트 조합). - 메시지는
ChangedJobPostingTarget.toMessageContent()가 만들고 300자 이내로 조립합니다 —notification_dispatches.message_summary가 VARCHAR(300)입니다(baseline...sql:319). - 피처 플래그
notification.job-posting-changed가 OFF면 대상 산출이 0건입니다. NotificationDispatchDomainService.planDispatches()시그니처에changedJobPostingTargets를 추가합니다. 기존 2종 대상 산출은 변경하지 않습니다.
롤백: 플래그 OFF → 대상 0건.
의존
- BE-68 (
NotificationType.JOB_POSTING_CHANGEDenum·플래그 시드)
다이어그램
처리 흐름
sequenceDiagram participant S as NotificationDispatchScheduler participant U as DispatchPendingNotificationsUseCase participant P as PostingDomainService participant M as MatchResultQueryDomainService participant A as ApplicationQueryDomainService participant D as NotificationDispatchDomainService S->>U: execute() U->>P: findAllChangedSince(baseTime) U->>M: findAllBy(ids) — 매칭 성립만 U->>A: findAllByJobPostingIn(ids) — 미지원만 U->>D: planDispatches(신규·고장·변경 대상) D->>D: 멱등 키 JOB_POSTING:{id}:JOB_POSTING_CHANGED:{일자서수} D->>D: 이미 발송이면 SKIPPED D-->>U: DispatchOutcome
클래스 의존
flowchart LR subgraph Application["application/notification"] UC[DispatchPendingNotificationsUseCase] Mapper[NotificationTargetMapper] end subgraph Domain["domain/notification"] DS[NotificationDispatchDomainService] Target[ChangedJobPostingTarget] Key[IdempotencyKey] Type[NotificationType] Flag[FeatureFlagGateway] end UC --> Mapper UC --> DS Mapper --> Target DS --> Key DS --> Flag Key --> Type
테스트 케이스
- 변경 감지된 매칭 공고에 변경 알림이 1건 발송된다
- 같은 공고가 하루에 2회 변경돼도 알림이 1건이다 (KST 일자 서수 멱등)
- 다음 날 다시 변경되면 알림이 1건 더 발송된다
- 매칭되지 않은 공고는 변경 알림 대상이 아니다
- 이미 지원한 공고는 변경 알림 대상이 아니다
- 비대표(중복) 공고는 변경 알림 대상이 아니다
- 변경되지 않은 공고는 대상이 아니다 (0건 경계)
- 메시지 요약이 300자를 넘지 않는다 (컬럼 상한)
- 피처 플래그 OFF면 대상이 0건이다
- 발송 실패 시
idempotency_key가 NULL로 남아 다음 배치가 재시도한다 - 기존 신규 공고·소스 고장 알림 동작이 변경되지 않는다 (회귀)
- 마감(CLOSED) 전환 자체로는 알림이 발송되지 않는다 (FR-36 유지)