21 KiB
21 KiB
PRD: 크리에이터 채널 후원 탭
1. Overview
크리에이터 채널의 후원 탭에서 후원 랭킹, 전체 후원 개수, 후원 내역 목록을 조회하고, 우측 하단 후원하기 버튼으로 홈 탭과 동일한 채널 후원 플로우를 제공한다.
2. Problem
- 크리에이터 채널에는
후원탭이 존재하지만 현재 상세 탭 구현은 placeholder 상태다. - 사용자는 크리에이터 채널 안에서 누가 많이 후원했는지와 최근 후원 내역을 한 화면에서 확인할 수 있어야 한다.
- 후원 내역은 길어질 수 있으므로
hasNext == true일 때 다음 페이지를 이어서 조회해야 한다. - 후원 탭에서 후원하기를 완료하면 방금 반영된 후원 내역과 랭킹이 화면에 갱신되어야 한다.
- 홈 탭에 이미 연결된 채널 후원 플로우와 동일한 UX/API 동작을 재사용해야 한다.
3. Goals
- Figma 전체 화면
290:9093기준으로 크리에이터 채널후원탭 UI 요구사항을 정의한다. - Figma 후원 랭킹 섹션
290:9097기준으로 랭킹 카드 요구사항을 정의한다. - API endpoint
GET /api/v2/creator-channels/{creatorId}/donations를 기준으로 최초 조회와 pagination 요구사항을 정의한다. - 최초 조회 query parameter 기본값은
page=0,size=20으로 둔다. - Sort-bar에는 정렬 UI를 표시하지 않고
전체label과donationCount만 표시한다. - 후원 랭킹 섹션에는 API
rankings를 응답 순서 기준으로 최대 8명까지 표시한다. - 후원 내역 item에는 후원자 프로필 이미지, 닉네임, 작성 시간, 후원 캔 수량, 메시지를 표시한다.
createdAtUtc는 크리에이터 채널 v2 공통 상대 시간 포맷을 따른다.- 후원 내역 item의 header 색상과 캔 수량 badge는 기존 홈 탭 후원 카드 정책을 우선 재사용한다.
- 랭킹 섹션의
전체보기버튼은 기존UserProfileDonationAllViewActivity로 이동한다. - 우측 하단 floating 후원하기 버튼은 홈 탭 후원하기 버튼 터치 액션과 동일하게
LiveRoomDonationDialog기반 채널 후원 플로우를 호출한다. - 후원 성공 후에는 후원 탭의 첫 페이지를 다시 조회해
donationCount,rankings,donations가 갱신되어야 한다. - 응답의
hasNext가true이면 현재page + 1페이지를 추가 로딩한다.
4. Non-Goals
- 크리에이터 채널 상단 header, title bar, 공통 tab-bar 구조 자체를 재설계하지 않는다.
홈,라이브,오디오,시리즈,커뮤니티,팬Talk탭의 동작은 이번 범위에서 변경하지 않는다.- 후원 결제/충전 dialog 내부 UX, validation, 충전 화면 진입 동작은 변경하지 않고 홈 탭 후원하기 액션을 재사용한다.
- 후원 랭킹 전체보기용 신규 화면, bottom sheet, 랭킹 확장 UI는 만들지 않는다.
- 후원 내역 item에서 프로필 이동, 신고, 삭제, 차단 등 추가 액션은 이번 범위에서 제외한다.
- 후원 내역의 비밀 후원 전용 표시 정책은 이번 범위에서 제외한다. 현재 API가 비밀 후원 여부를 별도 필드로 내려주지 않으므로 클라이언트에서 비밀 후원 UI를 분기하지 않는다.
- API schema를 임의 변경하거나 서버 응답 필드명을 클라이언트에서 새로 정의하지 않는다.
- Sort-bar에 정렬 label, 정렬 icon, 정렬 popup을 추가하지 않는다.
- Figma asset을 localhost URL 그대로 앱 코드에 직접 의존하지 않는다.
- 레거시 후원 구현 파일을 직접 수정하지 않는다. 필요한 경우 기존 후원 dialog/repository 흐름을 호출하거나 신규 wrapper/adapter를 추가해 사용한다.
5. Target Users
- 크리에이터 채널에서 후원 랭킹과 후원 내역을 확인하려는 앱 사용자.
- 특정 크리에이터에게 후원하고 후원 결과가 화면에 반영되길 기대하는 앱 사용자.
- 본인 채널의 후원 현황을 확인하려는 크리에이터.
kr.co.vividnext.sodalive.v2하위 크리에이터 채널 탭을 구현/유지보수하는 Android 개발자.
6. User Stories
- 사용자는 크리에이터 채널의
후원탭에서 전체 후원 수를 확인하고 싶다. - 사용자는 후원 랭킹 상위 사용자의 프로필, 닉네임, 순위를 빠르게 확인하고 싶다.
- 사용자는 각 후원 내역의 후원자, 후원 시간, 후원 캔 수량, 메시지를 확인하고 싶다.
- 사용자는 목록 하단까지 스크롤하면 다음 페이지가 자연스럽게 이어서 로딩되길 기대한다.
- 사용자는 후원 탭 우측 하단 버튼으로 바로 후원하고 싶다.
- 사용자는 후원 완료 직후 목록과 랭킹이 최신 상태로 갱신되길 기대한다.
7. Core Features
Creator Channel Donation Tab API
후원 탭 진입과 추가 로딩 시 크리에이터별 후원 랭킹과 후원 내역 데이터를 조회한다.
Requirements
- API endpoint는
GET /api/v2/creator-channels/{creatorId}/donations이다. creatorId는 path variable로 전달한다.- Query parameters는
page,size를 사용한다. - 최초 조회 기본값은
page=0,size=20이다. - 정렬 query parameter는 사용하지 않는다.
hasNext == true일 때 다음 페이지 요청은 현재 응답의page + 1값을 사용한다.- 중복 pagination 요청이 발생하지 않도록 loading 중 추가 요청을 막아야 한다.
- 다음 페이지 성공 시 기존
donations뒤에 append한다. - 후원 성공 후 갱신은 append가 아니라
page=0,size=20최초 조회를 다시 수행한다.
Response Contract
data class CreatorChannelDonationTabResponse(
val donationCount: Int,
val rankings: List<MemberDonationRankingResponse>,
val donations: List<CreatorChannelDonationResponse>,
val page: Int,
val size: Int,
@JsonProperty("hasNext")
val hasNext: Boolean
)
data class MemberDonationRankingResponse(
@JsonProperty("userId") val userId: Long,
@JsonProperty("nickname") val nickname: String,
@JsonProperty("profileImage") val profileImage: String,
@JsonProperty("donationCan") val donationCan: Int
)
data class CreatorChannelDonationResponse(
val nickname: String,
val profileImageUrl: String,
val can: Int,
val message: String,
val createdAtUtc: String
)
Edge Cases
creatorId <= 0이면 API를 호출하지 않고 기존 크리에이터 채널 탭의 종료/placeholder 정책을 따른다.- 최초 조회 실패 시 기존 크리에이터 채널 탭의 error/retry 패턴을 따른다.
- 다음 페이지 로딩 실패 시 기존 목록은 유지하고 기존 pagination 실패 표시 정책을 따른다.
- 다음 페이지 응답의
donations가 비어 있어도hasNext값 기준으로 이후 로딩 가능 여부를 갱신한다. - 서버 응답의
page,size가 요청 상태와 다를 경우 서버 응답 값을 기준으로 ViewModel page 상태를 동기화한다. profileImage또는profileImageUrl이 비어 있거나 이미지 로딩에 실패하면 기존 프로필 이미지 placeholder 정책을 따른다.message가 빈 문자열이면 기존 홈 탭 후원 카드와 동일하게%1$d캔을 후원하였습니다.형식의 fallback 문구를 표시한다.
Donation Ranking Section
후원 랭킹 섹션은 Figma 290:9097 기준의 카드 UI로 상위 후원자를 표시한다.
Requirements
- 섹션 title은
후원 랭킹이다. - 카드 배경은
gray/900, radius 14dp, 내부 padding 14dp를 기준으로 한다. - 랭킹 영역은 75dp 원형 프로필, 닉네임, 순위 숫자를 하나의 profile cell로 표시한다.
- profile cell은 Figma처럼 4열 x 2행 기준으로 최대 8개까지 표시한다.
- 순위 숫자는 응답 순서 기준으로 1부터 부여한다.
- 닉네임은 한 줄로 표시하고 긴 경우 말줄임 처리한다.
- 프로필 이미지는
MemberDonationRankingResponse.profileImage를 사용한다. donationCan은 현재 Figma 랭킹 cell에 직접 노출되지 않으므로 이번 범위에서는 UI model에 보관하되 화면에는 표시하지 않는다.- 랭킹 섹션 하단에는 Figma 기준
전체보기capsule button을 표시한다. - 서버는
rankings를 항상 최대 8명까지만 내려준다. 전체보기버튼을 터치하면 기존UserProfileDonationAllViewActivity로 이동한다.UserProfileDonationAllViewActivity는 기존 호출 계약처럼Constants.EXTRA_USER_ID를 전달해 실행한다.
Edge Cases
rankings가 비어 있으면 랭킹 grid는 표시하지 않는다.rankings가 1~7개이면 내려온 개수만 표시하고 빈 cell을 만들지 않는다.profileImage가 비어 있거나 이미지 로딩에 실패하면 기존 프로필 이미지 placeholder를 표시한다.- 동일
userId가 중복으로 내려오면 API 응답 순서를 유지해 그대로 표시한다. UserProfileDonationAllViewActivity진입에 필요한 id와 크리에이터 채널의creatorId가 다른 식별자인 경우, 구현 계획에서 기존 Activity 호출 계약에 맞는 식별자 확보 방식을 먼저 확정한다.
Sort Bar without Sort
Sort-bar는 전체 후원 수만 표시한다.
Requirements
- Figma 전체 화면 기준 Sort-bar는
290:9093내sort-bar이다. - 좌측에는
전체와donationCount를 표시한다. - 우측 정렬 label, 정렬 icon, 정렬 popup 진입 영역은 표시하지 않는다.
- 후원 탭에서는 정렬 상태를 보관하거나 API query로 전달하지 않는다.
Edge Cases
donationCount == 0이고 표시 가능한 후원 내역이 없으면 empty 상태 정책을 우선한다.- 다국어 label 길이가 길어져도 전체 개수와 겹치지 않아야 한다.
Donation List Item
후원 내역 목록은 Figma의 support item 구조를 기준으로 표시한다.
Requirements
- 각 item은
gray/900배경, radius 14dp 카드로 표시한다. - item header에는 후원자 프로필 이미지, 닉네임, 작성 시간, 후원 캔 수량 badge를 표시한다.
- header 배경색은 기존 홈 탭 후원 카드의
calculateCreatorChannelDonationHeaderColorRes(can)정책을 우선 재사용한다. - 후원 캔 수량은 can icon과
%d캔텍스트로 표시한다. createdAtUtc는 크리에이터 채널 v2 공통 상대 시간 포맷을 따른다.- 메시지는 header 아래에 16sp regular white text로 표시하고 긴 내용은 여러 줄로 확장한다.
message가 빈 문자열이면 기존 홈 탭 후원 카드 fallback 문구를 표시한다.- 현재 후원 탭 API는 비밀 후원 여부를 별도로 내려주지 않으므로 비밀 후원 전용 아이콘, 안내 문구, 익명 처리 분기를 구현하지 않는다.
Edge Cases
- 닉네임이 긴 경우 header의 텍스트 영역에서 말줄임 처리한다.
createdAtUtc파싱 실패 시 기존 날짜 표시 fallback 정책을 따른다.can <= 0값이 내려와도 앱에서 크래시가 발생하지 않아야 하며, 서버 응답 값을 그대로 표시한다.- 메시지가 매우 긴 경우 item 높이는 자연스럽게 확장하되 인접 item과 겹치지 않아야 한다.
Floating Donation Button
우측 하단 floating 후원하기 버튼은 홈 탭 후원하기 액션과 동일하게 동작한다.
Requirements
- Figma 전체 화면 기준 우측 하단
button-floating을 표시한다. - 버튼은 목록 위에 floating 형태로 배치한다.
- 버튼 icon과 색상은 Figma와 대응되는 기존 프로젝트 asset/token을 우선 사용한다.
- 타인 채널에서 버튼을 누르면 홈 탭의
onCreatorChannelDonationClicked()와 동일한 후원 dialog를 연다. - 후원 dialog는 기존 홈 탭과 동일하게
LiveRoomDonationDialog를 사용한다. - 후원 dialog 옵션과 동작은 홈 탭 후원하기 액션과 동일하게 유지한다.
- 후원 API는 기존
CreatorChannelRepository.postChannelDonation()흐름을 재사용한다. - 후원 성공 시
SharedPreferenceManager.can차감 정책은 홈 탭과 동일하게 적용한다. - 후원 성공 후 후원 탭은
GET /api/v2/creator-channels/{creatorId}/donations?page=0&size=20을 다시 호출해 화면을 갱신한다. - 같은 채널의 홈 탭 데이터가 이미 로드되어 있다면 홈 탭도 기존 delegate를 통해 갱신할 수 있다.
- 본인 채널에서는 floating 후원하기 버튼을 숨긴다.
Edge Cases
- 후원 dialog를 닫거나 취소하면 후원 API를 호출하지 않는다.
- 후원 API 실패 시 기존 홈 탭 후원 실패 toast/error 정책을 따른다.
- 후원 요청 중 중복 터치로 API가 중복 호출되지 않아야 한다.
- 후원 성공 후 재조회가 실패하면 기존 후원 성공 처리 자체는 유지하고, 후원 탭은 error/retry 또는 기존 목록 유지 정책 중 구현 계획에서 결정한다.
- floating button은 목록 하단 item, 시스템 navigation bar, keyboard와 겹치지 않아야 한다.
Pagination
후원 내역 목록은 스크롤 하단 접근 시 다음 페이지를 로딩한다.
Requirements
CreatorChannelDonationTabResponse.hasNext == true일 때만 다음 페이지를 요청한다.- 다음 페이지는 마지막 성공 응답의
page + 1로 요청한다. - 다음 페이지 요청에는
size=20을 유지한다. - 다음 페이지 로딩 중에는 추가 page 요청을 중복으로 보내지 않는다.
- 다음 페이지 성공 시 기존
donations뒤에 append한다. - 첫 페이지 새로고침 또는 후원 성공 갱신 시 기존 pagination 상태는 초기화한다.
Edge Cases
- 빠른 스크롤로 load-more trigger가 반복 발생해도 page가 중복 append되지 않아야 한다.
- Fragment/View 재생성 후 현재 목록과 page 상태는 ViewModel 상태 보존 정책에 따라 유지되어야 한다.
- 마지막 페이지 응답 이후
hasNext == false이면 이후 load-more trigger를 무시한다.
Empty State
후원 내역이 없으면 목록 대신 empty 상태를 표시한다.
Requirements
donationCount == 0또는 표시 가능한donations가 없는 전체 empty 상태이면 empty 상태를 표시한다.- empty 상태에서는 후원 내역 목록을 표시하지 않는다.
- empty 상태에서는 Sort-bar를 숨긴다.
- 후원 랭킹 섹션은
rankings가 비어 있으면 숨긴다. - empty 상태는 Figma
290:9008을 기준으로 표시한다. - empty 문구는
아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!이다. - empty 문구는 16sp regular,
gray/500, center 정렬로 표시한다. - 타인 채널 empty 상태에서는 문구 아래에
후원하기capsule button을 표시한다. - empty
후원하기button은 Figma처럼soda/400배경, gift icon, 16sp medium white text를 기준으로 한다. - empty
후원하기button 터치 시 floating 후원하기 버튼과 동일하게 홈 탭 후원하기 액션을 호출한다. - 본인 채널 empty 상태에서는 문구 아래의
후원하기button을 숨긴다. - 본인 채널 empty 상태에서는 floating 후원하기 버튼도 숨긴다.
Edge Cases
- API 최초 조회 실패 상태는 empty 상태로 취급하지 않고 기존 error/retry 패턴을 따른다.
donationCount > 0이지만 첫 페이지donations가 비어 있고 표시 가능한 item이 없으면 empty 상태를 표시하되, 전체 개수는 서버 응답의donationCount값을 유지한다.
8. UX / UI Expectations
- 후원 탭 선택 시 공통 header와 tab-bar는 기존 크리에이터 채널 컨테이너 구조를 유지한다.
- 후원 탭 컨텐츠는 Figma
290:9093처럼 black 배경 위에 구성한다. - Sort-bar는 52dp 높이 기준으로
전체와 개수만 표시한다. - 랭킹 카드와 후원 내역 카드는 좌우 14dp margin 기준으로 배치한다.
- 카드 간 vertical spacing은 Figma 기준을 우선하되 기존 크리에이터 채널 탭 spacing token과 충돌하면 기존 token을 우선한다.
- 모든 텍스트는 Pretendard 기반 기존 앱 폰트 스타일을 따른다.
- 후원 목록 스크롤 중 floating button이 주요 텍스트를 과도하게 가리지 않아야 한다.
- empty 상태의
후원하기button과 floating 후원하기 버튼은 같은 후원 액션을 호출하지만, 본인 채널에서는 둘 다 노출하지 않는다. - 이미지 로딩 중/실패 시 기존 프로필 placeholder를 사용해 레이아웃이 흔들리지 않아야 한다.
9. Technical Constraints
- 신규 후원 탭 전용 Fragment/ViewModel/DTO/mapper/adapter는
kr.co.vividnext.sodalive.v2.creator.channel.donation하위에 작성한다. - API/Repository endpoint 추가는 기존 채널 공통
CreatorChannelApi/CreatorChannelRepository에 최소 변경으로 추가한다. - 기존
CreatorChannelPagerAdapter의CreatorChannelTab.Donationplaceholder를 신규CreatorChannelDonationFragment로 교체한다. - 기존 홈 탭 후원 API 호출 흐름인
CreatorChannelRepository.postChannelDonation()을 재사용한다. - 기존 홈 탭 후원 item의 색상 계산, fallback message, 상대 시간 formatter는 가능한 범위에서 재사용한다.
- 랭킹
전체보기이동은 기존UserProfileDonationAllViewActivity를 재사용하고 신규 전체보기 화면을 만들지 않는다. - 기존
UserProfileDonationAllViewActivity는Constants.EXTRA_USER_ID를 요구하므로, 구현 계획에서 크리에이터 채널에서 전달할 식별자가 기존 Activity 계약과 맞는지 확인한다. - 레거시 후원 dialog 또는 repository 파일은 직접 수정하지 않는다.
- DTO annotation은 프로젝트 Retrofit/Gson 관례에 맞춰 구현 계획에서
@SerializedName사용 여부를 확인한다. PRD의@JsonProperty는 서버 계약 설명으로 기록한다. - 구현 전
plan-task.md를 작성하고, 해당 문서의 Phase/Task별 검증 기준에 따라 최소 테스트를 작성한다.
10. Metrics
- 후원 탭 최초 로딩 성공률.
- 후원 탭 pagination 성공률과 중복 요청 방지 여부.
- 후원하기 버튼 터치 후 dialog 노출률.
- 후원 성공 후 후원 탭 재조회 성공률.
- 후원 성공 후
donationCount,rankings,donations최신화 여부.
11. Resolved Decisions
- 랭킹 섹션의
전체보기버튼은 기존UserProfileDonationAllViewActivity로 이동한다. rankings는 서버가 항상 최대 8명만 내려준다.- 후원 내역 empty 상태는 Figma
290:9008을 기준으로 구현한다. - 본인 채널에서는 empty 상태의 하단
후원하기button과 floating 후원하기 버튼을 모두 숨긴다. - 현재 API는 비밀 후원 여부를 별도로 내려주지 않으므로 이번 범위에서는 비밀 후원 관련 UI/표시 분기를 구현하지 않는다.
12. References
- 전체 Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9093&m=dev
- 후원 랭킹 섹션 Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9097&m=dev
- 후원 empty 상태 Figma: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9008&m=dev
- 샘플 PRD:
docs/prd/sample-prd.md - 작업 문서 규칙:
docs/agent-guides/work-plan-docs.md - 기존 홈 탭 PRD:
docs/20260611_크리에이터_채널_홈_탭/prd.md - 기존 FanTalk 탭 PRD:
docs/20260622_FanTalk_탭/prd.md - 기존 후원 전체보기 Activity:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt
13. Verification Log
- 2026-06-22:
docs/prd/sample-prd.md와docs/agent-guides/work-plan-docs.md를 확인해 신규 문서 경로와 PRD 섹션 구성을 맞췄다. - 2026-06-22: Figma
290:9093,290:9097의 design context와 screenshot을 확인해 후원 랭킹 카드, Sort-bar, 후원 내역 item, floating button 구조를 PRD에 반영했다. - 2026-06-22: 기존 홈 탭 PRD와
CreatorChannelActivity.onCreatorChannelDonationClicked(),CreatorChannelHomeViewModel.postChannelDonation()흐름을 확인해 후원 탭 후원하기 액션 재사용 및 성공 후 갱신 요구를 PRD에 반영했다. - 2026-06-22: 사용자 확인사항을 반영해 랭킹
전체보기는 기존UserProfileDonationAllViewActivity이동으로 확정하고,rankings최대 8명 서버 보장, Figma290:9008empty 상태, 본인 채널 후원하기 버튼 숨김, 비밀 후원 표시 제외 정책을 PRD에 보강했다.