[FE-05] 채팅 REST 엔드포인트 + Query 훅 (unread/read/backfill/goods-chat)

작업 내용 (설계 의도)

변경 사항

근거: 20260704-채팅시스템고도화-design-fe-app.md “API 연동 표”·BE TDD “REST API 계약”.

채팅 관련 신규 REST 엔드포인트 함수와 TanStack Query 훅을 추가한다. 신규 파일 api/chat.ts·lib/useChat.ts. 기존 api/room.ts·lib/useRooms.ts는 재사용(방목록·메시지·전송은 기존 훅 유지). 컴포넌트는 api/를 직접 호출하지 않고 훅 경유.

  • api/chat.ts: getUnreadCounts() (GET /rooms/me/unread), markRead(roomId, lastReadMessageId) (POST /rooms/{roomId}/read), backfillMessages(roomId, afterMessageId) (GET /rooms/{roomId}/messages/backfill), startGoodsChat(productId) (POST /products/{productId}/chat), evictGuest(roomId, userId) (POST /rooms/{roomId}/guests/{userId}/evict).
  • lib/useChat.ts: useUnreadCounts, useMarkRead(성공 시 해당 방 unread 캐시 0 갱신), useBackfill, useStartGoodsChat, useEvictGuest.
  • 안읽은 수 낙관적 갱신: 실시간 수신 시 방별 +1(내가 보낸 것 제외)은 FE-06 소켓 훅에서 setQueryData로 처리하고, 이 티켓은 서버 SSOT 조회/재동기화 담당.

1·2단계 경계: getUnreadCounts/markRead는 1단계(FR-7/9 P0). backfillMessages(FR-10 재연결 backfill)·startGoodsChat(FR-18 goods)·evictGuest(FR-15 수동 방출)는 2단계 기능이며 각각 chat.realtime.enabled(backfill)·chat.goods.enabled(goods)·chat.community.enabled(evict) 플래그로 게이팅된다(플래그 정의는 FE-15 feature-flags.ts). 함수·훅은 1단계에 함께 정의하되 호출부(FE-06/FE-10/FE-14)가 플래그로 노출을 제어한다.

필드는 BE 계약과 일치(RoomUnreadResponse{roomId,unreadCount} 등). getBeClient() 경유(Authorization Bearer 자동).

의존

  • FE-02 (chat-types)

다이어그램

처리 흐름

sequenceDiagram
    participant Screen
    participant Hook as useMarkRead
    participant Api as api/chat.ts
    participant BE
    Screen->>Hook: markRead(roomId, msgId)
    Hook->>Api: POST /rooms/{id}/read
    Api->>BE: {lastReadMessageId}
    BE-->>Api: UnreadResponse
    Hook->>Hook: setQueryData(unread → 0)

테스트 케이스

  • useUnreadCountsRoomUnreadResponse[]를 방별 매핑으로 반환한다
  • useMarkRead 성공 후 해당 방의 unread 캐시가 0으로 갱신된다
  • markRead 실패 시 예외를 전파하되 화면을 막지 않는다(비차단)
  • useStartGoodsChatRoomResponse를 반환하고 기존 방이 있으면 같은 roomId를 받는다
  • backfillMessagesafterMessageId 이후 메시지만 반환한다(경계값)