Files
sodalive-android/docs/20260622_FanTalk_탭/prd.md

22 KiB
Raw Blame History

PRD: 크리에이터 채널 FanTalk 탭

1. Overview

크리에이터 채널의 FanTalk 탭에서 팬이 남긴 응원글과 크리에이터 답글을 조회하고, 작성자/채널 소유 여부에 따라 신고 또는 더보기 메뉴를 제공한다.


2. Problem

  • 크리에이터 채널에는 FanTalk 탭이 존재하지만, 탭 전용 API 계약과 Figma 기반 UI 요구사항이 별도 문서로 정의되어야 한다.
  • 사용자는 크리에이터 채널 안에서 전체 FanTalk 수와 팬 응원글, 크리에이터 답글을 한 화면에서 확인할 수 있어야 한다.
  • FanTalk 목록은 길어질 수 있으므로 hasNext == true일 때 다음 페이지를 이어서 조회해야 한다.
  • 내가 쓴 글 또는 내 채널의 글에는 수정/삭제 등 소유자용 액션을 열 수 있는 더보기 버튼이 필요하다.
  • 그 외 유저가 작성한 글에는 더보기 버튼 대신 신고 버튼만 표시되어야 한다.
  • 신고 기능은 기존 크리에이터 페이지(UserProfileActivity)의 FanTalk 신고 흐름과 동일하게 처리되어야 한다.

3. Goals

  • Figma 전체 화면 290:9139 기준으로 크리에이터 채널 FanTalk 탭 UI 요구사항을 정의한다.
  • API endpoint GET /api/v2/creator-channels/{creatorId}/fan-talks를 기준으로 최초 조회와 pagination 요구사항을 정의한다.
  • 최초 조회 query parameter 기본값은 page=0, size=20으로 둔다.
  • Sort-bar에는 정렬 UI를 표시하지 않고 전체 label과 fanTalkCount만 표시한다.
  • FanTalk item에는 작성자 프로필 이미지, 닉네임, 작성 시간, 본문을 표시한다.
  • createdAtUtc는 크리에이터 채널 v2 공통 날짜/시간 포맷을 따른다.
  • FanTalk item에 크리에이터 답글이 있으면 원글 아래에 크리에이터 답글을 최대 1개 표시한다.
  • 내가 쓴 글일 때 원글 우측 액션은 ic_new_more 더보기 버튼으로 표시하고, popup에는 수정하기, 삭제하기를 표시한다.
  • 내 채널에서 타인이 작성한 글일 때 원글 우측 액션은 ic_new_more 더보기 버튼으로 표시하고, popup에는 삭제하기만 표시한다.
  • 내가 쓴 글도 아니고 내 채널도 아닌 경우 원글 우측 액션은 신고 버튼으로 표시한다.
  • 더보기 popup의 수정하기는 이번 범위에서 표시만 하고 실제 수정 화면/API는 연결하지 않는다.
  • 더보기 popup의 삭제하기는 기존 FanTalk 삭제 API 흐름에 연결한다.
  • 신고 버튼 터치 시 기존 UserProfileFantalkAllViewActivity의 FanTalk 신고 기능과 동일하게 CheersReportDialog를 열고 ReportType.CHEERS 신고를 처리한다.
  • 우측 하단 floating 글쓰기 버튼은 Figma처럼 표시하되, 터치 액션은 후속 작업 범위로 남긴다.
  • FanTalk가 없는 경우 Figma empty 화면 290:9000 기준으로 empty 문구와 응원 남기기 button을 표시하되, button 터치 액션은 후속 작업 범위로 남긴다.
  • 응답의 hasNexttrue이면 현재 page + 1 페이지를 추가 로딩한다.

4. Non-Goals

  • 크리에이터 채널 상단 header, title bar, 공통 main tab-bar 구조 자체를 재설계하지 않는다.
  • , 라이브, 오디오, 시리즈, 화보, 커뮤니티, 후원 탭의 상세 구현은 이번 범위에서 제외한다.
  • FanTalk 글쓰기 페이지 구현과 floating 글쓰기 버튼의 실제 화면 연결은 이번 범위에서 제외한다.
  • empty 상태의 응원 남기기 button 실제 화면 연결은 이번 범위에서 제외한다.
  • FanTalk 상세 화면이 별도로 필요하더라도 이번 범위에서는 정의하지 않는다.
  • API schema를 임의 변경하거나 서버 응답 필드명을 클라이언트에서 새로 정의하지 않는다.
  • Sort-bar에 정렬 label, 정렬 icon, 정렬 popup을 추가하지 않는다.
  • 더보기 메뉴의 수정하기 실제 수정 화면/API 연결은 이번 범위에서 제외한다.
  • 크리에이터 답글 작성, 수정, 삭제 기능은 이번 범위에서 제외한다.
  • Figma asset을 localhost URL 그대로 앱 코드에 직접 의존하지 않는다.
  • 레거시 FanTalk 구현 파일을 직접 수정하지 않는다. 필요한 경우 기존 신고 dialog/repository 흐름을 호출하거나 신규 wrapper/adapter를 추가해 사용한다.

5. Target Users

  • 크리에이터 채널에서 팬 응원글을 읽는 앱 사용자.
  • 특정 크리에이터에게 남겨진 팬 반응과 크리에이터 답글을 확인하려는 사용자.
  • 본인이 작성한 FanTalk 글을 관리하려는 사용자.
  • 본인 채널의 FanTalk 글을 관리하려는 크리에이터.
  • 부적절한 FanTalk 글을 신고하려는 사용자.
  • kr.co.vividnext.sodalive.v2 하위 크리에이터 채널 탭을 구현/유지보수하는 Android 개발자.

6. User Stories

  • 사용자는 크리에이터 채널의 FanTalk 탭에서 전체 FanTalk 수를 확인하고 싶다.
  • 사용자는 팬이 남긴 응원글의 작성자, 작성 시간, 본문을 확인하고 싶다.
  • 사용자는 FanTalk 원글에 달린 크리에이터 답글을 원글 아래에서 확인하고 싶다.
  • 사용자는 내가 작성하지 않은 FanTalk 글을 부적절하다고 판단하면 신고하고 싶다.
  • 사용자는 내가 작성한 FanTalk 글에서 더보기 메뉴를 열고 수정/삭제 액션을 확인하고 싶다.
  • 사용자는 내가 작성한 FanTalk 글을 삭제하고 싶다.
  • 크리에이터는 내 채널에 타인이 작성한 FanTalk 글에서 더보기 메뉴를 열고 삭제 액션을 확인하고 싶다.
  • 사용자는 목록 하단까지 스크롤하면 다음 페이지가 자연스럽게 이어서 로딩되길 기대한다.
  • 사용자는 우측 하단 글쓰기 버튼 또는 empty 상태의 응원 남기기 button을 보고 FanTalk 작성 진입점이 있음을 인지하고 싶다.

7. Core Features

Creator Channel FanTalk Tab API

FanTalk 탭 진입과 추가 로딩 시 크리에이터별 FanTalk 목록 데이터를 조회한다.

Requirements

  • API endpoint는 GET /api/v2/creator-channels/{creatorId}/fan-talks이다.
  • creatorId는 path variable로 전달한다.
  • Query parameters는 page, size를 사용한다.
  • 최초 조회 기본값은 page=0, size=20이다.
  • 정렬 query parameter는 사용하지 않는다.
  • hasNext == true일 때 다음 페이지 요청은 현재 응답의 page + 1 값을 사용한다.
  • 중복 pagination 요청이 발생하지 않도록 loading 중 추가 요청을 막아야 한다.
  • 다음 페이지 성공 시 기존 fanTalks 뒤에 append한다.

Response Contract

data class CreatorChannelFanTalkTabResponse(
    val fanTalkCount: Int,
    val fanTalks: List<CreatorChannelFanTalkResponse>,
    val page: Int,
    val size: Int,
    @JsonProperty("hasNext")
    val hasNext: Boolean
)

data class CreatorChannelFanTalkResponse(
    val fanTalkId: Long,
    val writerId: Long,
    val writerNickname: String,
    val writerProfileImageUrl: String,
    val content: String,
    val createdAtUtc: String,
    val creatorReplies: List<CreatorChannelFanTalkReplyResponse>
)

data class CreatorChannelFanTalkReplyResponse(
    val fanTalkId: Long,
    val writerId: Long,
    val writerNickname: String,
    val writerProfileImageUrl: String,
    val content: String,
    val createdAtUtc: String
)

Edge Cases

  • 최초 조회 실패 시 기존 크리에이터 채널 탭의 error/retry 패턴을 따른다.
  • 다음 페이지 로딩 실패 시 기존 목록은 유지하고 기존 pagination 실패 표시 정책을 따른다.
  • 다음 페이지 응답의 fanTalks가 비어 있어도 hasNext 값 기준으로 이후 로딩 가능 여부를 갱신한다.
  • 서버 응답의 page, size가 요청 상태와 다를 경우 서버 응답 값을 기준으로 ViewModel page 상태를 동기화한다.
  • writerProfileImageUrl이 비어 있거나 이미지 로딩에 실패하면 기존 프로필 이미지 placeholder 정책을 따른다.
  • creatorReplies가 비어 있으면 답글 영역과 답글 연결선을 표시하지 않는다.
  • creatorReplies가 2개 이상이어도 응답 순서 기준 첫 번째 1개만 표시한다.

Sort Bar without Sort

Sort-bar는 전체 FanTalk 수만 표시한다.

Requirements

  • Figma 전체 화면 기준 Sort-bar는 290:9139sort-bar이다.
  • 좌측에는 전체fanTalkCount를 표시한다.
  • 우측 정렬 label, 정렬 icon, 정렬 popup 진입 영역은 표시하지 않는다.
  • FanTalk 탭에서는 정렬 상태를 보관하거나 API query로 전달하지 않는다.

Edge Cases

  • fanTalkCount == 0이고 전체 empty 상태이면 Figma empty 화면 290:9000을 우선하며 Sort-bar를 표시하지 않는다.
  • 다국어 label 길이가 길어져도 전체 개수와 겹치지 않아야 한다.

FanTalk List Item

FanTalk 목록은 Figma의 feed item 구조를 기준으로 원글과 크리에이터 답글을 표시한다.

Requirements

  • Figma 전체 화면 기준 FanTalk item은 290:9139feed-listfeed 구조를 따른다.
  • 각 원글에는 writerProfileImageUrl, writerNickname, createdAtUtc, content를 표시한다.
  • createdAtUtc는 크리에이터 채널 v2 공통 날짜/시간 포맷을 따른다.
  • 원글 본문은 Figma처럼 16sp regular text로 표시하고 긴 내용은 자연스럽게 여러 줄로 확장한다.
  • creatorReplies가 있으면 원글 아래에 회색 배경의 답글 card를 표시한다.
  • 답글 card에는 답글 작성자 프로필 이미지, 닉네임, 작성 시간, 본문을 표시한다.
  • 답글 card의 작성자는 API 응답의 writerNickname, writerProfileImageUrl을 사용한다.
  • 크리에이터 답글은 현재 1개만 추가되는 서버 정책을 기준으로 최대 1개만 표시한다.
  • creatorReplies가 여러 개 내려오더라도 응답 순서 기준 첫 번째 1개만 표시하고 나머지는 표시하지 않는다.
  • 원글과 답글 사이에는 Figma처럼 연결선을 표시한다.

Edge Cases

  • 원글 content가 비어 있으면 작성자/작성 시간/우측 액션은 유지하고 본문 텍스트 영역은 빈 문자열로 표시한다.
  • 답글 content가 비어 있으면 답글 card는 표시하되 본문 텍스트 영역은 빈 문자열로 표시한다.
  • 닉네임이 긴 경우 프로필 영역에서 말줄임 처리한다.
  • createdAtUtc 파싱 실패 시 기존 날짜 표시 fallback 정책을 따른다.

Item Right Action

FanTalk 원글 우측 액션은 사용자 권한에 따라 더보기 또는 신고로 분기한다.

Requirements

  • 현재 로그인 사용자의 id와 writerId가 같으면 내가 쓴 글로 판단한다.
  • 현재 크리에이터 채널이 내 채널이면 내 채널로 판단한다.
  • 내가 쓴 글이거나 내 채널에서 타인이 작성한 글인 경우 우측 액션은 더보기 버튼으로 표시한다.
  • 더보기 버튼 icon은 ic_new_more를 사용한다.
  • 더보기 버튼을 누르면 Figma popup 메뉴처럼 권한별 메뉴를 표시한다.
  • 내가 쓴 글의 popup 메뉴 label은 수정하기, 삭제하기이다.
  • 내 채널에서 타인이 작성한 글의 popup 메뉴 label은 삭제하기만 표시한다.
  • 내가 쓴 글이면서 내 채널의 글인 경우 내가 쓴 글 기준으로 수정하기, 삭제하기를 표시한다.
  • 수정하기는 표시만 하고 이번 범위에서는 실제 수정 화면/API를 연결하지 않는다.
  • 삭제하기는 기존 FanTalk 삭제 API 흐름에 연결한다.
  • 기존 삭제 API 흐름은 레거시 FanTalk 삭제와 동일하게 PutModifyCheersRequest(cheersId = fanTalkId, isActive = false)ExplorerRepository.modifyCheers() / PUT /explorer/profile/cheers로 요청하는 방식이다.
  • 삭제 전에는 기존 FanTalk 삭제와 동일하게 SodaDialog 확인 dialog를 표시한다.
  • 삭제 성공 후에는 목록을 새로고침하거나 해당 item을 제거해 삭제 결과가 화면에 반영되어야 한다.
  • 내가 쓴 글도 아니고 내 채널도 아닌 경우 우측 액션은 신고 버튼으로 표시한다.
  • 신고 버튼은 Figma 기준 14sp medium gray text button으로 표시한다.

Edge Cases

  • 로그인 사용자 id를 확인할 수 없는 경우 내가 쓴 글로 판단하지 않는다.
  • 내 채널 여부를 확인할 수 없는 경우 내 채널로 판단하지 않는다.
  • 수정하기 터치 시에는 API를 호출하지 않고 수정 화면으로 이동하지 않는다.
  • 삭제 확인 dialog에서 취소하면 삭제 API를 호출하지 않는다.
  • 삭제 API 실패 시 기존 FanTalk 삭제 흐름과 동일한 toast/error 정책을 따른다.
  • 더보기 popup은 화면 우측 또는 하단을 벗어나지 않아야 한다.
  • 더보기 popup이 열린 상태에서 다른 item의 액션을 누르면 기존 popup을 닫고 새 액션을 처리한다.

FanTalk Report

신고 버튼 터치 시 기존 FanTalk 신고 기능과 동일한 신고 dialog와 신고 API 계약을 사용한다.

Requirements

  • 신고 버튼 터치 시 기존 UserProfileFantalkAllViewActivity.showCheersReportPopup() 흐름과 동일하게 CheersReportDialog를 표시한다.
  • 신고 사유를 선택하지 않고 신고하면 screen_user_profile_fantalk_report_reason_required 문구를 사용한다.
  • 신고 요청은 기존 FanTalk 신고와 동일하게 ReportRequest(type = ReportType.CHEERS, reason = reason, cheersId = fanTalkId) 계약을 사용한다.
  • 신고 성공 toast는 기존 신고 흐름의 응답 message 또는 character_comment_report_submitted 문구를 따른다.
  • 신고 API 호출은 기존 ReportRepository.report() 흐름을 재사용한다.

Edge Cases

  • 신고 dialog를 취소하면 API를 호출하지 않는다.
  • 신고 API 실패 시 기존 FanTalk 신고 흐름과 동일한 toast/error 정책을 따른다.
  • 이미 신고한 글에 대한 서버 오류 또는 중복 신고 메시지는 서버 응답 message를 우선 표시한다.

Floating Write Button

우측 하단 floating 글쓰기 버튼은 표시만 하고 실제 연결은 후속 작업으로 남긴다.

Requirements

  • Figma 전체 화면 기준 우측 하단 button-floating을 표시한다.
  • 버튼은 목록 위에 floating 형태로 배치한다.
  • 버튼 icon과 색상은 Figma와 대응되는 기존 프로젝트 asset/token을 우선 사용한다.
  • 버튼 터치 이벤트는 후속 FanTalk 글쓰기 페이지 구현 후 연결한다.
  • 이번 범위에서는 버튼 터치 시 실제 글쓰기 화면을 열지 않고 별도 API도 호출하지 않는다.

Edge Cases

  • 목록 하단 item, 시스템 navigation bar, keyboard와 버튼이 겹치지 않아야 한다.
  • 빈 목록 상태에서는 Figma empty 화면 290:9000을 기준으로 우측 하단 floating 글쓰기 버튼 대신 중앙 응원 남기기 capsule button을 표시한다.

Pagination

FanTalk 목록은 스크롤 하단 접근 시 다음 페이지를 로딩한다.

Requirements

  • CreatorChannelFanTalkTabResponse.hasNext == true일 때만 다음 페이지를 요청한다.
  • 다음 페이지는 마지막 성공 응답의 page + 1로 요청한다.
  • 다음 페이지 요청에는 size=20을 유지한다.
  • 다음 페이지 로딩 중에는 추가 page 요청을 중복으로 보내지 않는다.
  • 다음 페이지 성공 시 기존 fanTalks 뒤에 append한다.

Edge Cases

  • 빠른 스크롤로 load-more trigger가 반복 발생해도 page가 중복 append되지 않아야 한다.
  • Fragment/View 재생성 후 현재 목록과 page 상태는 ViewModel 상태 보존 정책에 따라 유지되어야 한다.
  • 마지막 페이지 응답 이후 hasNext == false이면 이후 load-more trigger를 무시한다.

Empty State

FanTalk가 없으면 목록 대신 empty 상태를 표시한다.

Requirements

  • fanTalkCount == 0 또는 표시 가능한 fanTalks가 없는 전체 empty 상태이면 empty 상태를 표시한다.
  • empty 상태에서는 FanTalk 목록을 표시하지 않는다.
  • empty 상태에서는 Figma empty 화면 290:9000 기준으로 Sort-bar를 표시하지 않는다.
  • empty 문구는 Figma node 290:9001 기준으로 16sp regular, line-height 1.45, gray/500(#959595), center 정렬로 표시한다.
  • empty 문구 한국어는 아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!이다.
  • empty 문구 영어는 No cheers yet.\nBe the first to cheer for the creator!이다.
  • empty 문구 일본어는 まだ応援がありません。\n最初にクリエイターを応援してみましょう이다.
  • empty 문구는 한국어/영어/일본어 다국어 문자열 리소스로 관리한다.
  • empty 상태에서는 Figma node 290:9001처럼 문구 아래에 14dp 간격으로 응원 남기기 capsule button을 표시한다.
  • empty 상태의 응원 남기기 button은 gray/800(#343434) 배경, 100dp 수준 radius, 20dp icon, icon-text 6dp gap, 16sp medium white text를 기준으로 한다.
  • empty 상태의 응원 남기기 button 터치 액션은 후속 FanTalk 글쓰기 페이지 구현 후 연결한다.
  • 이번 범위에서는 empty 상태의 응원 남기기 button 터치 시 실제 글쓰기 화면을 열지 않고 별도 API도 호출하지 않는다.

Edge Cases

  • API 최초 조회 실패 상태는 empty 상태로 취급하지 않고 기존 error/retry 패턴을 따른다.
  • fanTalkCount > 0이지만 첫 페이지 fanTalks가 비어 있고 표시 가능한 item이 없으면 empty 상태를 표시하되, 전체 개수는 서버 응답의 fanTalkCount 값을 유지한다.

8. UX / UI Expectations

  • 전체 화면은 기존 크리에이터 채널 컨테이너의 black background, sticky tab-bar, title-bar 동작을 유지한다.
  • FanTalk main tab은 선택 상태로 표시하고, 선택 underline과 텍스트 색상은 기존 tab-bar 정책을 따른다.
  • Sort-bar 높이와 배치는 Figma 290:9139를 기준으로 하되 정렬 UI는 제거한다.
  • FanTalk item은 black background 위에 white/gray text hierarchy를 유지한다.
  • 답글 card는 Figma처럼 gray/900 계열 배경과 radius를 적용해 원글과 구분한다.
  • 원글 작성자 프로필 이미지는 42dp 원형, 답글 작성자 프로필 이미지는 20dp 원형 수준을 기준으로 한다.
  • item 간 divider 또는 spacing은 Figma 구조를 기준으로 하되 기존 RecyclerView item 간격 정책과 충돌하지 않게 맞춘다.
  • 우측 액션 영역은 더보기 icon 또는 신고 text 중 하나만 표시한다.
  • popup 메뉴는 Figma처럼 원글 우측 액션 근처에 표시하고 수정하기, 삭제하기 항목을 세로로 배치한다.
  • floating 글쓰기 버튼은 우측 하단 14dp margin 수준으로 표시하고 목록 스크롤과 독립적으로 유지한다.
  • empty 상태는 오디오/시리즈/커뮤니티 탭과 같은 상단 가시 영역 배치 패턴을 따른다. 별도 minHeight로 부모 높이를 늘리지 않고, tab content 상단 기준 48dp padding 아래에 empty 문구와 응원 남기기 capsule button을 세로로 배치한다.

9. Technical Constraints

  • Android Gradle 프로젝트의 :app 모듈에서 구현한다.
  • 신규 Fragment, ViewModel, adapter, mapper, DTO 등은 kr.co.vividnext.sodalive.v2 하위에 작성한다.
  • 레거시 파일은 직접 수정하지 않고, 기존 기능이 필요하면 기존 class를 호출하거나 신규 wrapper/adapter를 추가한다.
  • 기존 CreatorChannelActivityViewPager2/CreatorChannelPagerAdapter 구조를 유지한다.
  • API/Repository는 기존 크리에이터 채널 공통 data 계층 패턴을 우선 따른다.
  • 신고 dialog/API는 기존 CheersReportDialog, ReportRepository, ReportType.CHEERS 계약을 재사용한다.
  • fanTalkId는 신고 요청의 cheersId로 전달한다.
  • FanTalk 삭제는 기존 ExplorerRepository.modifyCheers()PutModifyCheersRequest(cheersId = fanTalkId, isActive = false) 계약을 재사용한다.
  • 정렬 관련 ContentSort, CreatorChannelSortPopup은 FanTalk 탭에서 사용하지 않는다.
  • 이미지 로딩, 날짜 포맷, toast/error/retry, pagination trigger는 기존 크리에이터 채널 탭 구현 패턴을 따른다.
  • 문자열은 다국어 리소스로 관리하고 hardcoded user-facing text를 최소화한다.
  • Figma localhost asset URL을 앱 코드에 직접 사용하지 않는다.

10. Metrics

  • FanTalk 탭 최초 조회 성공률.
  • FanTalk 탭 최초 조회 API latency.
  • FanTalk pagination 성공률과 중복 요청 발생 여부.
  • 신고 버튼 노출 조건 정확도.
  • 더보기 버튼 노출 조건 정확도.
  • 삭제 action 성공률과 삭제 후 목록 반영 정확도.
  • FanTalk 신고 성공률.
  • FanTalk 탭 empty/error 상태 노출 빈도.
  • floating 글쓰기 버튼 클릭 수는 후속 글쓰기 페이지 연결 시 측정한다.

11. Open Questions

  • 현재 확정 대기 중인 항목 없음.

12. Resolved Decisions

  • 더보기 popup에서 수정하기는 표시만 하고 실제 수정 화면/API는 연결하지 않는다.
  • 더보기 popup에서 삭제하기는 기존 FanTalk 삭제 API 흐름에 연결한다.
  • 내 채널에서 타인이 작성한 FanTalk 글의 더보기 popup에는 삭제하기만 표시한다.
  • createdAtUtc는 크리에이터 채널 v2 공통 날짜/시간 포맷을 따른다.
  • empty 문구는 한국어/영어/일본어 문자열 리소스로 관리한다.
  • floating 글쓰기 버튼과 empty 상태의 응원 남기기 button은 이번 범위에서 표시만 하고 터치 액션은 후속 범위에서 연결한다.
  • creatorReplies는 최대 1개만 표시한다.