fix(home-following): 최근 대화를 팔로우 크리에이터로 제한한다

This commit is contained in:
2026-08-19 12:23:33 +09:00
parent b18e40d4fc
commit b1be87b46f
9 changed files with 570 additions and 20 deletions

View File

@@ -0,0 +1,141 @@
# 홈 팔로잉 최근 대화 팔로우 필터 PRD
## 문서 정보
| 항목 | 내용 |
|---|---|
| 문서 상태 | 구현 기준 확정 |
| 작성일 | 2026-08-19 |
| 최종 수정일 | 2026-08-19 |
| 대상 제품 | 메인 홈 팔로잉 탭 API의 최근 대화 섹션 |
| 작성자·결정권자 | Codex 작성, 사용자 승인 |
| 선행 PRD | `docs/20260625_메인_홈_팔로잉_탭_API/prd.md` |
| 관련 API Contract | 별도 문서 없음. 기존 `GET /api/v2/home/following` 계약을 유지한다. |
| 관련 구현 계획 | `docs/20260819_홈_팔로잉_최근대화_팔로우필터/plan-task.md` |
| 관련 review | 없음 |
### 선행 문서와의 관계
- 이 문서는 선행 PRD의 Feature D와 "최근 대화는 팔로잉 여부와 무관하다"는 Edge Case만 대체한다.
- 선행 PRD에서 정의한 나머지 팔로잉 탭 요구사항과 완료 기록은 변경하지 않는다.
## 1. Overview
로그인 사용자가 메인 홈 팔로잉 탭을 조회할 때 `recentChats`에는 현재 팔로우 중인 크리에이터와의 DM 및 AI 채팅만 노출한다. 기존 응답 스키마, 최신순 정렬, 최대 10개, 메시지 미리보기 정책은 유지한다.
## 2. Problem Statement
- `HomeFollowingFacade`는 현재 `ChatRoomListService.getRooms(member, filter = "ALL", cursor = null, limit = 10)`을 호출한다.
- 이 호출은 사용자의 모든 DM 및 AI 채팅방을 조회하므로, 팔로우하지 않은 크리에이터와의 대화도 팔로잉 탭에 노출된다.
- 팔로잉 탭의 최근 대화 섹션이 탭의 목적과 다른 대상을 보여준다.
문제를 해결했다는 판단은 팔로잉 탭 응답에서 활성 팔로우 대상의 DM 및 AI 채팅만 최신순 최대 10개로 반환되고, 일반 채팅 목록 API의 결과는 바뀌지 않는 것으로 한다.
## 3. Goals
- 팔로잉 탭의 `recentChats`를 현재 활성 팔로우 관계가 있는 크리에이터의 대화로 제한한다.
- DM과 AI 채팅을 모두 포함한다.
- 팔로우 필터를 저장소 조회에 적용한 뒤 최대 10개를 선택해, 더 최신인 미팔로우 대화 때문에 결과 수가 줄지 않게 한다.
- 기존 공개 API 스키마, 정렬, 미리보기, 최대 개수 정책을 유지한다.
- 일반 채팅 목록 API의 기존 전체 조회 동작을 유지한다.
## 4. Non-Goals
- `GET /api/v2/home/following` 또는 `GET /api/v2/chat/rooms`의 request/response 스키마를 변경하지 않는다.
- 채팅방 생성, 메시지 전송, 읽음 처리, cursor 형식과 메시지 미리보기 정책을 변경하지 않는다.
- 팔로우/언팔로우 처리 자체와 알림 설정 정책을 변경하지 않는다.
- 차단 관계, 크리에이터 계정 활성 상태 등 이번 요청에 포함되지 않은 추가 노출 조건을 도입하지 않는다.
- DB schema, index, dependency를 추가하지 않는다.
- 선행 홈 팔로잉 PRD의 최근 대화 이외 섹션을 변경하지 않는다.
## 5. Target Users and Permissions
| 사용자 | 기대 결과 |
|---|---|
| 로그인 회원 | 팔로우 중인 크리에이터와의 최근 DM 및 AI 채팅을 확인한다. |
| 비로그인 사용자 | 기존과 동일하게 로그인 필요 상태와 빈 `recentChats`를 받는다. |
| 앱 클라이언트 | 변경 없는 응답 스키마로 최근 대화 섹션을 표시한다. |
- 인증 회원의 `Member.id`를 팔로우 관계와 채팅 참가자 조회 기준으로 사용한다.
- 비로그인 요청에서는 기존과 동일하게 채팅 조회를 실행하지 않는다.
## 6. 핵심 사용자 흐름
1. 사용자가 `GET /api/v2/home/following`을 호출한다.
2. 서버는 로그인 회원의 홈 팔로잉 데이터와 최근 대화를 조회한다.
3. 최근 대화 조회는 현재 `CreatorFollowing.isActive = true`인 크리에이터의 채팅만 선택한다.
4. DM과 AI 결과를 기존 정렬 규칙으로 병합하고 최대 10개를 반환한다.
5. 앱은 기존 `recentChats` 응답 필드로 채팅방 진입 화면을 구성한다.
## 7. 기능 요구사항
| ID | 상태 | 요구사항 | 수용 기준 | Goal 연결 |
|---|---|---|---|---|
| `HFC-001` | 확정 | 팔로잉 탭의 최근 대화는 요청 회원이 현재 활성 팔로우 중인 크리에이터의 채팅만 포함한다. | 활성 팔로우 대상은 포함되고 팔로우 row가 없거나 `isActive = false`인 대상은 제외된다. | `P1-T1`, `P1-GATE` |
| `HFC-002` | 확정 | DM 채팅의 크리에이터 식별자는 `UserCreatorChatParticipant`의 상대 회원 `opponent.member.id`를 사용한다. | 상대 회원과 요청 회원의 활성 `CreatorFollowing`이 있을 때만 DM 방이 반환된다. | `P1-T1` |
| `HFC-003` | 확정 | AI 채팅의 크리에이터 식별자는 `ChatCharacter.creatorMember.id`를 사용한다. | AI 캐릭터의 `creatorMember`와 요청 회원의 활성 `CreatorFollowing`이 있을 때만 AI 방이 반환된다. | `P1-T1` |
| `HFC-004` | 확정 | 팔로우 필터는 저장소 조회에서 pagination보다 먼저 적용한다. | 최신 미팔로우 대화가 10개 이상이어도 그보다 오래된 팔로우 대화를 최대 10개까지 조회할 수 있다. | `P1-T1` |
| `HFC-005` | 확정 | DM과 AI 결과는 기존 `lastMessageAt`, `chatType`, `roomId` 내림차순으로 병합하고 최대 10개를 반환한다. | 기존 정렬·limit·`ChatRoomListItemResponse` 변환 테스트가 통과한다. | `P1-T1` |
| `HFC-006` | 확정 | 일반 채팅 목록은 기존처럼 팔로잉 여부와 무관하게 조회한다. | `GET /api/v2/chat/rooms`가 팔로우 필터를 요청하지 않고 기존 서비스 테스트가 통과한다. | `P1-T1`, `P1-GATE` |
| `HFC-007` | 확정 | 비로그인 홈 팔로잉 요청 동작은 유지한다. | `isLoginRequired = true`, 빈 `recentChats`, 채팅 서비스 미호출이 유지된다. | `P1-GATE` |
| `HFC-008` | 확정 | 홈 팔로잉의 크리에이터 목록 최대 20개와 관계없이 모든 활성 팔로우 관계를 최근 대화 필터에 사용한다. | `followingCreators` 응답 목록을 후처리 필터로 재사용하지 않고 DB의 활성 팔로우 관계를 직접 판정한다. | `P1-T1` |
### Edge Cases
- 활성 팔로우 관계가 없으면 `recentChats`는 빈 배열이다.
- 과거에 팔로우했더라도 현재 `CreatorFollowing.isActive = false`이면 해당 대화를 제외한다.
- 활성 팔로우 관계가 있는 AI와 DM 대화가 함께 있으면 두 유형 모두 기존 최신순 정렬에 포함한다.
- 방, 참가자 또는 메시지가 비활성인 경우 기존 채팅 목록 조회 조건대로 제외한다.
- 메시지가 없는 방은 기존 채팅 목록 정책대로 최근 대화에 포함하지 않는다.
## 8. API 계약
### Endpoint
- Method/Path: `GET /api/v2/home/following`
- request parameter: 변경 없음
- 인증 처리: 변경 없음
- response wrapper: 변경 없음
- `recentChats`: 기존 `List<ChatRoomListItemResponse>` 유지
- `roomId`, `chatType`, `targetName`, `targetImageUrl`, `lastMessage`, `lastMessageAt`: 필드와 의미 변경 없음
`GET /api/v2/chat/rooms`의 공개 계약과 전체 조회 의미도 변경하지 않는다.
## 9. 데이터·보안·성능 요구사항
- 팔로우 판정은 `creator_following.member_id`, `creator_following.creator_id`, `creator_following.is_active = true`를 사용한다.
- `creator_following`의 기존 `(member_id, creator_id)` unique constraint를 활용하고 신규 index를 추가하지 않는다.
- 팔로우 여부는 응답 생성 시점의 DB 상태를 기준으로 한다.
- 회원·채팅·팔로우 식별자와 메시지 본문을 새 log로 남기지 않는다.
- 팔로우 필터는 DB query에 포함해 미팔로우 결과를 메모리에서 제거하거나 전체 대화를 로드하지 않는다.
- 신규 dependency와 DB migration을 추가하지 않는다.
## 10. 성공 기준
- [x] 활성 팔로우 중인 일반 크리에이터와의 DM이 `recentChats`에 포함된다. (`HFC-001`, `HFC-002`)
- [x] 활성 팔로우 중인 AI 캐릭터와의 AI 채팅이 `recentChats`에 포함된다. (`HFC-001`, `HFC-003`)
- [x] 팔로우하지 않았거나 현재 비활성 팔로우인 크리에이터의 대화는 제외된다. (`HFC-001`)
- [x] 필터 적용 후 최신순 최대 10개와 기존 응답 필드가 유지된다. (`HFC-004`, `HFC-005`)
- [x] 일반 채팅 목록과 비로그인 홈 팔로잉 응답이 회귀하지 않는다. (`HFC-006`, `HFC-007`)
- [x] 공개 API schema, DB schema와 dependency 변경이 없다.
## 11. Open Questions
없음.
인터뷰 종료 시 최종 모호성은 `0.07`이며, 명확성 점수는 Goal `1.00`, Scope `1.00`, Constraints `1.00`, Success `0.75`, Context `0.90`이다.
## 12. 요구사항 추적표
| 요구사항 범위 | 계획 Phase | Goal | 자동 검증 |
|---|---:|---|---|
| `HFC-001~005`, `HFC-008` | 1 | `P1-T1` | `ChatRoomListServiceTest`, `HomeFollowingFacadeTest`, `HomeFollowingEndToEndTest` |
| `HFC-006~007` | 1 | `P1-GATE` | `ChatRoomListControllerTest`, `HomeFollowingFacadeTest`, `HomeFollowingEndToEndTest` |
## 13. Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·Goal |
|---|---|---|---|---|---|
| 2026-08-19 | `DEC-001` | 확정 | 최근 대화는 DM과 AI 채팅을 모두 포함한다. | 사용자는 AI 채팅 가능 캐릭터도 크리에이터라고 확정했다. | `HFC-001~003`, `P1-T1` |
| 2026-08-19 | `DEC-002` | 확정 | 일반 채팅 목록은 유지하고 홈 팔로잉 조회에서만 활성 팔로우 필터를 사용한다. | 변경 범위를 홈 팔로잉 탭으로 제한하고 기존 공개 API 회귀를 방지한다. | `HFC-006`, `P1-T1` |
| 2026-08-19 | `DEC-003` | 확정 | 기존 완료 문서는 유지하고 이 PRD가 최근 대화 규칙만 대체한다. | 완료 이력을 보존하면서 새 변경의 범위와 검증을 독립적으로 추적한다. | 문서 전체 |