[BE-10] 재연결 backfill (FR-10)

작업 내용 (설계 의도)

변경 사항

근거 TDD: 20260704-채팅시스템고도화-tdd.md (FR-10, WebSocket 연결 끊김 실패 경로). 2단계(Milestone 2).

WebSocket 재연결 후 끊긴 구간의 메시지를 REST로 채운다. 클라이언트는 지수 백오프 재시도, 3회 실패 시 REST 폴링으로 전환하고 재연결 성공 시 backfill 호출.

  • domain/message/repository/MessageCustomRepository.kt + Impl: findAfter(roomId, afterMessageId, pageSize): List<Message> 추가 (id > afterMessageId 오름차순).
  • domain/message/service/MessageDomainService.kt 또는 신규 MessageBackfillDomainService.kt: backfill(roomId, userId, afterMessageId) — 참여자·만료 가드 후 findAfter.
  • application/message/usecase/BackfillMessagesUseCase.kt (신규).
  • presentation/message/controller/MessageApiController.kt: GET /rooms/{roomId}/messages/backfill?afterMessageId= 추가 — wave4에서 본 티켓만 MessageApiController 수정.
  • at-least-once 전제: 중복은 클라이언트가 messageId로 dedup(멱등).
  • 롤백: 조회 전용 additive 엔드포인트. 제거해도 실시간·기존 조회 무영향.

의존

  • BE-05 (MessageCustomRepository 확장 — 같은 파일이므로 BE-05 완료 후 순차)
  • BE-03a (컨텍스트 방 무관하나 Room 확장 이후)

다이어그램

처리 흐름

sequenceDiagram
    participant C as Client
    participant Ctl as MessageApiController
    participant UC as BackfillMessagesUseCase
    participant DS as MessageDomainService
    C->>Ctl: GET /rooms/1/messages/backfill?afterMessageId=120
    Ctl->>UC: execute(roomId,userId,120)
    UC->>DS: backfill → findAfter(1,120,pageSize)
    DS-->>C: messages id>120

테스트 케이스

  • afterMessageId=120 요청 시 id 121 이상의 메시지만 오름차순으로 반환된다.
  • 끊긴 구간이 없으면(최신까지 읽음) 빈 목록을 반환한다.
  • 방 비참여자의 backfill 요청은 거부된다.
  • 만료된 게스트의 backfill 요청은 거부된다.
  • 동일 afterMessageId로 재요청해도 동일 결과를 반환한다 (멱등).