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

21 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 문구는 화면 중앙에 16sp regular, gray/500 계열, center 정렬로 표시한다.
  • empty 문구 한국어는 아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!이다.
  • empty 문구 영어는 No cheers yet.\nBe the first to cheer for the creator!이다.
  • empty 문구 일본어는 まだ応援がありません。\n最初にクリエイターを応援してみましょう이다.
  • empty 문구는 한국어/영어/일본어 다국어 문자열 리소스로 관리한다.
  • empty 상태에서는 Figma 290:9000처럼 문구 아래에 응원 남기기 capsule button을 표시한다.
  • empty 상태의 응원 남기기 button은 gray/800 배경, 100dp 수준 radius, 20dp icon, 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 상태는 Figma 290:9000처럼 tab-bar 아래 black background 중앙에 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개만 표시한다.