본문으로 건너뛰기

Phase 99: Verification

빌드 검증

nx build dha-sleep-api

빌드가 성공해야 구현 완료로 간주한다.


기능 테스트

테스트 1: SCHEDULED 보고

방법: conversation.push_planned webhook을 트리거하여 push 생성

기대 로그:

[PushDeliveryReporterService] Push delivery reported { pushId: '...', status: 'SCHEDULED' }

기대 HTTP 호출:

POST {agentzApiUrl}/push-delivery/report
Body: { pushId, status: 'SCHEDULED', pushType, sessionId, userId, scheduledAt }

테스트 2: COMPLETED 보고

방법: Push 예약 시간이 도래하여 Bull 작업이 실행되고 발송 성공

기대 로그:

[PushDeliveryReporterService] Push delivery reported { pushId: '...', status: 'COMPLETED' }

기대 HTTP 호출:

POST {agentzApiUrl}/push-delivery/report
Body: { pushId, status: 'COMPLETED', pushType, sessionId, userId, channel: 'fcm'|'telegram'|'kakaotalk', scheduledAt }

테스트 3: CANCELLED 보고

사전 조건: cancelIfUserInitiatesFirst=true인 push가 스케줄되고, 사용자가 30분 이내에 메시지 전송

기대 로그:

[PushDeliveryReporterService] Push delivery reported { pushId: '...', status: 'CANCELLED' }

테스트 4: FAILED 보고

사전 조건: FCM token 없음 + Telegram/KakaoTalk 미연결 → 모든 채널 실패 (첫 번째 시도에서 즉시 실패)

참고: 기존 코드에서 첫 실패 시 DB status→FAILED 후 throw하므로, Bull retry attempt 2에서는 "already processed" 로 스킵된다. 따라서 FAILED 보고는 첫 번째 실패 시점에서 즉시 발생한다.

기대 로그:

[PushDeliveryReporterService] Push delivery reported { pushId: '...', status: 'FAILED' }

기대 HTTP 호출:

POST {agentzApiUrl}/push-delivery/report
Body: { pushId, status: 'FAILED', error: '...', channel: 'fcm', pushType, sessionId, userId, scheduledAt }

테스트 5: Best-effort (Platform API 다운)

방법: agentz-studio API 중단 상태에서 push 발송

기대 동작:

  1. Push 정상 발송됨 (COMPLETED)
  2. Reporter 로그: Push delivery report error (non-fatal)
  3. Push 처리 로직에 영향 없음

테스트 6: 비활성화 상태

방법: AGENTZ_ENABLED=false 환경변수로 서비스 시작

기대 동작:

  1. Push 정상 발송됨
  2. Reporter 로그: Push delivery reporting disabled or not configured
  3. Platform API 호출 없음

사용자 시나리오

Happy Path: Push 정상 발송

1. Platform webhook: conversation.push_planned (type: EVENING_CHECKIN, scheduledAt: 21:00)
2. PushSchedulerService: ScheduledPush 생성
3. Reporter: POST /push-delivery/report (SCHEDULED)
4. [21:00] Bull job 실행
5. NotificationRouter: Telegram 성공
6. Reporter: POST /push-delivery/report (COMPLETED, channel: telegram)

Edge Case: User Activity 취소

1. Platform webhook: conversation.push_planned (cancelIfUserInitiatesFirst: true)
2. Reporter: POST /push-delivery/report (SCHEDULED)
3. [20:45] 사용자가 메시지 전송
4. [21:00] Bull job 실행 → isUserRecentlyActive=true
5. ScheduledPush status → CANCELLED
6. Reporter: POST /push-delivery/report (CANCELLED)

Error Path: 모든 채널 실패

1. Platform webhook: conversation.push_planned
2. Reporter: POST /push-delivery/report (SCHEDULED)
3. [21:00] Bull job 실행 (attempt 1)
4. status → TRIGGERED
5. NotificationRouter: Telegram 실패 → KakaoTalk 실패 → FCM 실패
6. status → FAILED + Reporter: POST /push-delivery/report (FAILED, channel, error)
7. throw → Bull retry 트리거
8. [attempt 2] push 조회 → status=FAILED → "already processed" → return (성공)
→ Bull은 job 성공으로 간주, retry 종료

참고: 기존 코드의 retry 동작 한계. 첫 실패에서 DB status→FAILED 후 retry에서는 status 체크에 걸려 스킵된다. 이는 기존 코드의 문제이며 이 플랜의 범위 밖.

Error Path: Reporter 실패 (non-fatal)

1. [21:00] Bull job 실행 → push 발송 성공 (COMPLETED)
2. Reporter: POST /push-delivery/report → timeout (5s)
3. Log: "Push delivery report error (non-fatal)"
4. Push 처리 결과: 정상 완료 (보고 실패가 push에 영향 없음)
5. Platform: 이 push는 보고 누락 → Sweep에서 OVERDUE로 감지됨

체크리스트

  • PushDeliveryReporterService 생성
  • PushJobProcessor에 COMPLETED 보고 hook 추가
  • PushJobProcessor에 CANCELLED 보고 hook 추가
  • PushJobProcessor에 FAILED 보고 hook 추가 (handleFailedJob)
  • PushSchedulerService에 SCHEDULED 보고 hook 추가
  • PushModule에 서비스 등록
  • 빌드 성공 확인
  • Best-effort 패턴 확인 (보고 실패 시 push 정상 동작)

변경 파일 총 목록

#파일유형
1apps/dha-sleep-api/src/app/push/push-delivery-reporter.service.tsNEW
2apps/dha-sleep-api/src/app/push/push-job.processor.tsEDIT
3apps/dha-sleep-api/src/app/push/push-scheduler.service.tsEDIT
4apps/dha-sleep-api/src/app/push/push.module.tsEDIT
총계1 NEW + 3 EDIT

Platform 연동 확인 (agentz-studio Plan 120 기준)

Platform 기대Client 제공매칭
pushId (externalPushId)ScheduledPush.id (UUID)
status (PushDeliveryStatus enum)SCHEDULED/COMPLETED/FAILED/CANCELLED
timestamp (ISO8601)new Date().toISOString()
pushType (string)ScheduledPush.type
sessionId (for tracking correlation)ScheduledPush.createdFromSessionId
userId (for tracking correlation)ScheduledPush.userId
channel (delivery channel)routeResult.channel
error (failure reason)error.message
retryCountjob.attemptsMade
scheduledAt (original schedule)ScheduledPush.scheduledAt

Natural key correlation: Platform sweep은 sessionId + userId + pushType로 correlation한다. Client가 보고하는 이 3개 필드는 모두 webhook의 data.sessionId, data.userId, data.pushPlan.type에서 유래하므로 정확히 매칭된다.