diff --git a/docs/20260709_FanTalk_상세_답글쓰기/prd.md b/docs/20260709_FanTalk_상세_답글쓰기/prd.md new file mode 100644 index 00000000..923ff05c --- /dev/null +++ b/docs/20260709_FanTalk_상세_답글쓰기/prd.md @@ -0,0 +1,196 @@ +# PRD: FanTalk 상세 / 답글쓰기 + +## 1. Overview +크리에이터 채널 `FanTalk` 탭에서 본인 채널의 FanTalk item을 터치하면 FanTalk 상세 화면으로 진입하고, 기존 FanTalk 답글 쓰기 API로 크리에이터 답글을 작성할 수 있게 한다. + +--- + +## 2. Problem +- 현재 v2 `FanTalk` 탭은 목록 item에 표시되는 FanTalk 원문과 최신 크리에이터 답글을 보여주지만, item 터치로 상세 화면에 진입하는 흐름이 없다. +- 본인 채널의 크리에이터는 FanTalk 원문을 상세히 보고 같은 화면에서 답글을 작성해야 한다. +- FanTalk 상세 UI는 커뮤니티 답글 화면과 동일해야 하므로, 기존 구현과 Figma 기준을 문서로 고정해야 한다. +- 답글 입력값 유무에 따른 send button 활성/비활성 상태가 명확히 정의되어야 한다. +- 구현 전에 V2 패키지 하위에서 재사용 가능한 위젯/화면 요소 후보를 확인해 중복 구현을 줄여야 한다. + +--- + +## 3. Goals +- `FanTalk` 탭의 FanTalk item 터치 시 FanTalk 상세 화면으로 진입하는 요구사항을 정의한다. +- FanTalk 상세 진입은 본인 채널(`currentHeader?.isOwner == true`)에서만 허용한다. +- FanTalk 상세 UI는 `CreatorChannelCommunityReplyActivity`와 동일한 구조를 따른다. +- Figma 답글 없음 `669:40462`, 답글 있음 `669:40468` 기준으로 FanTalk 상세/답글쓰기 UI 요구사항을 정의한다. +- FanTalk는 별도의 상세 조회 API가 없으므로 FanTalk item 터치 시 목록의 data model을 통째로 상세 화면에 전달한다. +- FanTalk 상세에서는 크리에이터 답글을 최대 1개만 표시/관리한다. +- 답글 작성은 기존 FanTalk 답글 쓰기 API를 그대로 재사용한다. + - 기존 경로: `ExplorerRepository.writeCheers(parentCheersId = fanTalkId, creatorId = creatorId, content = content, token = token)` + - v2 wrapper 추가 시 `CreatorChannelRepository.writeFanTalkReply(fanTalkId, creatorId, content, token)`처럼 레거시 API를 감싸는 최소 wrapper만 둔다. +- 기존 답글이 있는 상태에서 신규 답글을 쓰려고 하면 안내 모달을 표시하고, 확인 시 신규 작성 API가 아니라 답글 수정 API로 기존 답글을 대체한다. +- 입력값이 `trim()` 기준 비어 있으면 send button을 비활성화한다. +- 입력값이 `trim()` 기준 비어 있지 않으면 send button을 활성화한다. +- 재사용 가능한 V2 위젯/화면 요소 후보를 계획 문서에 기록한다. + +--- + +## 4. Non-Goals +- 레거시 FanTalk 화면 파일을 직접 수정하지 않는다. +- 서버 API schema나 endpoint를 새로 정의하지 않는다. +- 별도 FanTalk 상세 조회 API를 새로 가정하지 않는다. +- FanTalk 원글 작성 화면은 이번 범위에서 수정하지 않는다. +- FanTalk 원글 수정/삭제 UX를 새로 설계하지 않는다. +- 일반 사용자가 타인 채널에서 FanTalk 상세에 진입하는 흐름은 이번 범위에서 제공하지 않는다. +- 커뮤니티 상세/답글 화면의 기존 동작을 변경하지 않는다. +- Figma에 없는 새로운 reaction row, title text, marketing성 안내 문구를 추가하지 않는다. + +--- + +## 5. Target Users +- 본인 채널의 FanTalk에 답글을 작성하려는 크리에이터. +- `kr.co.vividnext.sodalive.v2` 하위 크리에이터 채널 FanTalk 기능을 구현/유지보수하는 Android 개발자. + +--- + +## 6. User Stories +- 크리에이터는 내 채널의 FanTalk item을 터치해 상세 화면으로 들어가고 싶다. +- 크리에이터는 FanTalk 상세에서 팬이 남긴 원문, 작성자 프로필, 닉네임, 작성 시간을 확인하고 싶다. +- 크리에이터는 이미 작성된 답글이 있으면 FanTalk 상세에서 확인하고 싶다. +- 크리에이터는 답글이 없는 FanTalk 상세에서도 바로 답글을 입력하고 싶다. +- 크리에이터는 답글 input field에 글을 입력하면 send button이 활성화되기를 기대한다. +- 크리에이터는 답글 작성 성공 후 FanTalk 목록과 홈 요약이 최신 상태로 갱신되기를 기대한다. +- 일반 사용자는 타인 채널에서 FanTalk item을 터치해 상세 화면으로 들어가지 못해야 한다. + +--- + +## 7. Core Features + +### F1. FanTalk 상세 진입 +본인 채널의 FanTalk item 터치 시 상세 화면으로 이동한다. + +#### Requirements +- `CreatorChannelFanTalkAdapter`의 item root 또는 FanTalk 본문 영역 터치를 상세 진입점으로 사용한다. +- 상세 진입은 `CreatorChannelActivity`의 `currentHeader?.isOwner == true`인 경우에만 허용한다. +- 본인 채널이 아니면 item 터치 callback을 연결하지 않거나, Activity에서 안전하게 무시한다. +- 상세 화면에는 목록에서 사용 중인 FanTalk data model을 통째로 전달한다. +- data model 전달 방식은 `Parcelable`을 우선 사용하고, 기존 model에 직접 적용하기 어렵다면 FanTalk 상세 전용 payload model을 만들어 원본 item 데이터를 한 번에 담아 전달한다. +- 개별 primitive extra로 `fanTalkId`, `writerNickname`, `content` 등을 나누어 전달하지 않는다. +- `fanTalkId <= 0L` 또는 `creatorId <= 0L`이면 상세 화면을 열지 않는다. +- 로그인 토큰이 없으면 기존 크리에이터 채널 로그인 이동 정책을 따른다. + +#### Edge Cases +- 목록 refresh 중 stale item을 터치해 유효하지 않은 ID가 전달되면 아무 동작도 하지 않는다. +- 본인 채널이 아닌 상태에서 callback이 호출되더라도 상세 화면을 열지 않는다. + +### F2. FanTalk 상세 UI +Figma와 커뮤니티 답글 화면 기준으로 FanTalk 상세 화면을 표시한다. + +#### Requirements +- UI 구조는 `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/detail/reply/CreatorChannelCommunityReplyActivity.kt`와 동일하게 한다. +- layout 기준은 `activity_creator_channel_community_reply.xml`을 따른다. +- 상단 title-bar에는 뒤로가기 icon만 표시하고 별도 title text는 표시하지 않는다. +- 상단에는 부모 FanTalk 원문을 표시한다. + - 프로필 이미지 42dp + - 닉네임 + - 작성 시간 + - 본문 + - 본인 채널 관리용 더보기 button +- 답글이 없으면(Figma `669:40462`) 부모 FanTalk만 표시하고 답글 card 영역은 비운다. +- 답글이 있으면(Figma `669:40468`) 부모 FanTalk 아래에 크리에이터 답글 card 1개만 표시한다. +- 답글 card는 `gray_900` 배경, round corner, profile 20dp, 닉네임, 시간, 본문, 더보기 button 구조를 따른다. +- 하단 input bar는 화면 하단에 고정하고 키보드 표시 시 키보드 위로 함께 올라오게 한다. + +#### Edge Cases +- 전달받은 답글 목록이 비어 있으면 empty message를 추가하지 않는다. +- 답글은 1개만 가능하므로 별도 pagination/API를 사용하지 않는다. +- 서버 응답 data model에 답글 list가 들어 있더라도 상세에서는 첫 번째 답글만 기존 답글로 취급한다. + +### F3. FanTalk 답글 작성 +기존 FanTalk 답글 쓰기 API를 재사용한다. + +#### Requirements +- 답글 input placeholder는 기존 커뮤니티 답글 화면과 동일한 문자열을 우선 재사용한다. +- 입력값 판단은 `trim()` 기준으로 한다. +- 입력값이 없으면 send button은 disabled 상태다. + - button bg: `gray_900` + - button icon: `ic_new_arrow_up_gray` +- 입력값이 있으면 send button은 enabled 상태다. + - button bg: `soda_400` + - button icon: `ic_new_arrow_up_white` +- 답글이 없는 상태에서 send를 누르면 기존 FanTalk 등록 API에 `parentCheersId = fanTalkId`를 전달해 호출한다. +- 답글이 있는 상태에서 send를 누르면 기존 답글 대체 안내 모달을 먼저 표시한다. +- 기존 답글 대체 안내 모달은 기존 답글이 신규 답글로 대체된다는 내용을 포함한다. + - title: `답글 수정` + - desc: `기존 답글이 신규 답글로 대체됩니다.\n수정하시겠어요?` + - cancel button: `취소` + - confirm button: `확인` +- 기존 답글 대체 안내 모달에서 확인을 누르면 신규 작성 API가 아니라 답글 수정 API를 호출한다. +- 답글 수정 API는 기존 답글의 `fanTalkId`를 대상으로 `ExplorerRepository.modifyCheers(PutModifyCheersRequest(cheersId = replyFanTalkId, content = ...), token)`를 호출하는 v2 wrapper를 사용한다. +- 전송 content는 앞뒤 공백을 제거한 값을 사용한다. +- 작성 중에는 중복 전송을 막는다. +- 작성 또는 수정 성공 시 입력값을 비우고 상세 화면/목록/홈 요약 갱신을 요청한다. +- 작성 또는 수정 성공 시 키보드를 내리고 input focus를 해제한다. +- 작성 실패 시 입력값을 유지하고 기존 toast/error 표시 정책을 따른다. + +#### Edge Cases +- 공백/줄바꿈만 입력하면 API를 호출하지 않는다. +- 기존 답글이 있는 상태에서 대체 안내 모달을 취소하면 API를 호출하지 않고 입력 상태를 유지한다. +- 서버 실패 응답의 message가 있으면 해당 message를 우선 표시한다. +- 답글 작성 성공 후 목록 refresh 결과가 늦게 도착해도 현재 상세 상태를 stale 데이터로 덮지 않는다. + +### F4. FanTalk 답글 수정/삭제 메뉴 +본인 채널 상세에서 답글 관리 메뉴를 제공한다. + +#### Requirements +- Figma `669:40468`처럼 답글 card 우측 더보기 버튼을 표시한다. +- 메뉴 문구는 `수정하기`, `삭제하기`를 사용한다. +- popup UI는 커뮤니티 답글 화면의 `CreatorChannelCommunityMorePopup` 패턴을 우선 검토한다. +- 삭제 확인은 v2 공통 모달 `V2ModalDialog`를 사용한다. +- 기존 FanTalk 수정/삭제 API wrapper가 없으면 `ExplorerRepository.modifyCheers(PutModifyCheersRequest(...))`를 감싸는 v2 wrapper를 추가한다. +- 수정 UX는 커뮤니티 답글 화면과 동일하게 하단 input field 수정 모드를 우선한다. +- 하단 input field에서 기존 답글을 대체하는 흐름도 같은 답글 수정 API를 사용한다. + +#### Edge Cases +- 기존 답글이 없으면 답글 card 더보기 button을 표시하지 않는다. + +--- + +## 8. UX / UI Expectations +- FanTalk 상세는 커뮤니티 답글 화면과 시각적으로 동일해야 한다. +- 본인 채널에서만 item 터치 affordance가 느껴져야 한다. +- 하단 input bar는 화면 하단에 안정적으로 고정되어야 한다. +- send button 상태는 입력값 변화에 즉시 반응해야 한다. +- 답글 작성 성공 후 사용자는 목록으로 돌아가지 않아도 작성된 답글 상태를 확인할 수 있어야 한다. +- Figma `669:40462`/`669:40468`의 검은 배경, gray divider, gray_900 input/card, soda_400 활성 button 색상을 유지한다. + +--- + +## 9. Technical Constraints +- Android XML View/ViewBinding 기반으로 구현한다. +- 신규 Activity/ViewModel 및 하위 코드는 `kr.co.vividnext.sodalive.v2.creator.channel.fantalk` 하위에 작성한다. +- 레거시 FanTalk 코드는 직접 수정하지 않고 기존 repository/API 흐름만 호출한다. +- 신규 API endpoint를 추가하지 않는다. +- API 호출은 기존 RxJava3, Koin, `ApiResponse` 패턴을 따른다. +- 상대 시간 표시는 기존 `UtcRelativeTimeTextFormatter`를 재사용한다. +- 삭제 확인 dialog는 v2 패키지 내부이므로 `V2ModalDialog`를 사용한다. +- 기존 답글 대체 안내도 v2 패키지 내부이므로 `V2ModalDialog`를 사용한다. +- 신규 문자열은 `values`, `values-en`, `values-ja`에 추가하거나 기존 문자열을 재사용한다. +- 커뮤니티 답글 화면 리소스를 직접 재사용할 경우 resource id/name과 ViewBinding 충돌을 피한다. + +--- + +## 10. Metrics +- 별도 분석 이벤트 추가는 이번 범위에서 정의하지 않는다. +- 기능 성공 기준은 본인 채널에서 FanTalk item 터치로 상세에 진입하고, 답글이 없으면 기존 답글 쓰기 API로 답글 작성이 성공하며, 답글이 있으면 안내 모달 확인 후 수정 API로 기존 답글이 대체되는 것이다. +- 접근 제한 성공 기준은 본인 채널이 아닌 경우 FanTalk 상세에 진입할 수 없는 것이다. +- UI 완료 기준은 Figma `669:40462`, `669:40468` 및 `CreatorChannelCommunityReplyActivity`의 주요 layout 구조, input bar, send button 상태가 반영되는 것이다. +- 자동 검증 기준은 owner gate, data model 통째 전달, send button enabled/disabled 상태, 기존 `writeCheers(parentCheersId = fanTalkId)` 호출 계약, 기존 답글 대체 모달 및 `modifyCheers` 호출 계약, 작성/수정 성공 후 refresh event를 ViewModel/source 테스트로 확인하는 것이다. + +--- + +## 11. Open Questions & Decisions +- 결정: 별도 FanTalk 상세 조회 API가 프롬프트에 없으므로 신규 조회 endpoint를 가정하지 않는다. 최초 상세 데이터는 목록 item의 UI model을 intent payload로 전달한다. +- 결정: FanTalk item 터치로 상세 이동 시 data model을 통째로 전달한다. 개별 primitive extra 분해 전달은 사용하지 않는다. +- 결정: 답글 작성은 기존 `writeCheers` API를 `parentCheersId = fanTalkId`로 호출하는 방식으로 정의한다. +- 결정: FanTalk 상세의 답글은 1개만 가능하므로 pagination/API가 필요 없다. +- 결정: 기존 답글이 있는 상태에서 신규 답글을 쓰려 하면 안내 모달을 표시하고, 확인 시 답글 수정 API로 기존 답글을 신규 답글 내용으로 대체한다. +- 결정: send button icon은 `ic_new_arrow_up_white`, `ic_new_arrow_up_gray`를 사용한다. +- 결정: FanTalk 상세 진입은 `currentHeader?.isOwner == true`일 때만 허용한다. +- 남은 확인: 없음.