Files
sodalive-ios/docs/20260704_크리에이터_채널_후원_탭/prd.md

12 KiB

PRD: 크리에이터 채널 후원 탭

1. Overview

크리에이터 채널 공통 shell의 후원 탭에서 채널 후원 랭킹과 후원 내역 목록을 제공한다. 상단 title bar, header, sticky tab-bar는 기존 크리에이터 채널 홈/라이브/오디오/시리즈 구현을 재사용하고, tab-bar 아래 콘텐츠만 후원 탭 전용 API 응답으로 구성한다.

API는 GET /api/v2/creator-channels/{creatorId}/donations를 사용한다. creatorId는 path variable이며, query parameter는 page, size를 사용한다. 기본값은 page=0, size=20이다.

Figma 참조:

  • 전체 화면: 290:9093, 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
  • 후원 랭킹 섹션: 290:9097, 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: 290:9009, 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-9009&m=dev

2. Problem

  • 현재 CreatorChannelTab.donation은 placeholder로 표시되어 실제 후원 랭킹과 후원 내역을 확인할 수 없다.
  • 기존 홈 탭에는 채널 후원 요약 섹션과 후원하기 액션이 있지만, 후원 탭 전용 API, 랭킹 섹션, 스크롤 페이징이 없다.
  • 후원 후 홈 탭 후원 섹션은 갱신되지만, 후원 탭이 구현되면 후원 탭 목록도 함께 갱신되어야 한다.

3. Goals

  • CreatorChannelTab.donation 선택 시 후원 탭 API를 호출하고 응답 데이터로 화면을 구성한다.
  • 기본 query는 page=0, size=20이다.
  • donationCount는 sort-bar 좌측에 전체 {donationCount} 형식으로 표시한다.
  • sort-bar에는 정렬 UI를 표시하지 않는다.
  • rankings는 Figma 후원 랭킹 섹션(290:9097)처럼 후원 랭킹 카드 안에 프로필 그리드로 표시한다.
  • 후원 랭킹 섹션의 전체보기를 터치하면 기존 UserProfileDonationAllView로 이동한다.
  • donations는 기존 홈 탭 후원 카드의 시각 규칙을 재사용해 세로 목록으로 표시한다.
  • 목록 하단 도달 시 hasNext == true이면 page + 1을 조회해 append한다.
  • rankings.isEmpty && donationCount == 0이면 Figma empty view(290:9009)를 표시한다.
  • Empty view 안의 후원하기 버튼은 홈 탭 후원하기 버튼과 동일한 액션을 사용한다.
  • donationCount == 0 empty 상태에서는 하단 고정 후원하기 버튼을 숨긴다.
  • donationCount != 0 || !rankings.isEmpty이면 홈 탭 plus button 위치에 icon-only 후원하기 버튼을 표시한다.
  • 후원 성공 후 후원 탭 데이터와 홈 탭 데이터가 갱신되어야 한다.

4. Non-Goals

  • 크리에이터 채널 공통 shell, header, sticky tab-bar 동작을 다시 설계하지 않는다.
  • 채널 후원 mutation API나 LiveRoomDonationDialogView를 새로 만들지 않는다.
  • 기존 ChannelDonationAllView를 확장하지 않는다.
  • 랭킹 전체보기 전용 화면/API를 새로 만들지 않고 기존 UserProfileDonationAllView를 재사용한다.
  • 서버 응답에 없는 비밀 후원 여부를 클라이언트에서 추론하지 않는다.
  • Figma localhost asset URL을 앱 코드에 직접 사용하지 않는다.
  • Pods/**, generated/**, build/**는 수정하지 않는다.

5. Core Requirements

5.1 API

  • Method: GET
  • Path: /api/v2/creator-channels/{creatorId}/donations
  • Path parameter:
    • creatorId
  • Query parameters:
    • page: 기본값 0
    • size: 기본값 20
  • 응답 래퍼는 기존 패턴대로 ApiResponse<CreatorChannelDonationTabResponse>로 디코딩한다.
data class CreatorChannelDonationTabResponse(
    val donationCount: Int,
    val rankings: List<MemberDonationRankingResponse>,
    val donations: List<CreatorChannelDonationResponse>,
    val page: Int,
    val size: Int,
    val hasNext: Boolean
)

data class MemberDonationRankingResponse(
    val userId: Long,
    val nickname: String,
    val profileImage: String,
    val donationCan: Int
)

data class CreatorChannelDonationResponse(
    val nickname: String,
    val profileImageUrl: String,
    val can: Int,
    val message: String,
    val createdAtUtc: String
)
  • Kotlin Long은 기존 V2 모델 관례대로 Swift Int로 선언한다.
  • CreatorChannelDonationResponse는 홈 탭 모델과 동일한 필드이므로 중복 타입 생성을 피하고 기존 타입을 재사용한다.

5.2 Sort-bar

  • 후원 탭 sort-bar는 정렬이 없고 전체 개수만 포함한다.
  • 좌측에 전체donationCount를 표시한다.
  • 우측 정렬 버튼, 정렬 아이콘, 정렬 context popup, BottomSheet는 표시하지 않는다.
  • 기존 CreatorChannelSortBar가 정렬 버튼을 필수로 노출하므로, 후원 탭에서는 별도 CreatorChannelDonationCountBar를 만들거나 CreatorChannelSortBar를 선택적으로 정렬 숨김 처리할 수 있다.

5.3 Ranking section

  • rankings가 비어 있으면 후원 랭킹 섹션은 숨긴다.
  • rankings가 있으면 Figma 290:9097665:19056 기준의 카드로 표시한다.
  • 후원 랭킹은 최대 8명만 표시하고, 한 줄에 4명씩 표시한다.
  • 카드 배경은 Color.gray900, corner radius는 14pt, 내부 padding과 item 간격은 14pt 기준을 사용한다.
  • title은 후원 랭킹을 표시한다.
  • item은 75pt 원형 프로필, 중앙 정렬 닉네임, 프로필 하단에 rank number overlay를 표시한다.
  • 랭킹 번호는 응답 배열 순서 기준으로 index + 1을 표시한다.
  • 닉네임은 1줄 말줄임 처리한다.
  • profileImageDownsampledKFImage로 표시하고, 비어 있거나 실패하면 기존 프로필 placeholder 관례를 따른다.
  • Figma의 전체보기 버튼을 표시한다.
  • 전체보기 터치 시 기존 AppStep.userProfileDonationAll(userId:)로 이동해 UserProfileDonationAllView를 연다.
  • userId에는 현재 크리에이터 채널의 creatorId를 전달한다.

5.4 Donation list

  • donations는 세로 목록으로 표시한다.
  • 각 item은 기존 홈 탭 CreatorChannelDonationSection 카드와 동일한 정보 구조를 사용한다.
    • profileImageUrl
    • nickname
    • createdAtUtc 상대 시간
    • can
    • message
  • 후원 금액 pill은 기존 can icon과 {can}캔 형식을 사용한다.
  • 카드 상단 배경색은 홈 탭 후원 카드의 can 구간 색상을 재사용한다.
    • 1...50: #E2E2E2
    • 51...100: #73EE01
    • 101...500: #00EAFF
    • 501...: #FF4C3C
  • can >= 501인 후원 item은 상단 영역에서 nickname을 white, 상대 시간을 gray100으로 표시한다.
  • 그 외 후원 can 구간의 nickname과 상대 시간 글자색은 기존 표시 색상을 유지한다.
  • message가 빈 문자열이면 기존 홈 탭과 동일하게 {can}캔을 후원하였습니다를 표시한다.
  • 서버 응답에 비밀 후원 여부가 없으므로, 클라이언트는 서버가 내려준 nickname, profileImageUrl, message를 그대로 표시한다.

5.5 Pagination

  • 첫 진입 시 page=0, size=20으로 조회한다.
  • 다음 페이지 로딩 중 중복 호출을 막는다.
  • 마지막 item이 화면에 나타났고 hasNext == true이면 page + 1을 조회한다.
  • 다음 페이지 성공 시 기존 donations 뒤에 append한다.
  • 첫 페이지 재조회 시 기존 목록을 교체한다.
  • 빈 페이지가 내려오고 hasNext == true인 경우 무한 루프를 피하기 위해 한 번의 다음 페이지 요청만 수행하고 이후 서버 응답 기준으로 상태를 갱신한다.

5.6 Donation action

  • 후원 탭의 모든 후원하기 button tap은 홈 탭의 후원하기 버튼 tap action과 동일하게 동작한다.
  • 기존 LiveRoomDonationDialogViewChannelDonationViewModel.postChannelDonation 흐름을 재사용한다.
  • Dialog 설정은 홈 탭 채널 후원과 동일하게 사용한다.
    • isAudioContentDonation: false
    • messageLimit: 100
    • I18n.MemberChannel.secretDonationLabel
    • I18n.MemberChannel.secretDonationMinimumCanMessage
    • shouldPrefixSecretInMessagePlaceholder: false
  • 후원 성공 시:
    • CreatorChannelViewModel.fetchHome(creatorId:)로 홈 탭 데이터를 갱신한다.
    • 후원 탭 ViewModel의 첫 페이지를 다시 조회해 랭킹과 목록을 갱신한다.

5.7 Empty and floating donation button

  • rankings.isEmpty && donationCount == 0이면 Figma 290:9009 기준의 empty view를 표시한다.
  • Empty 문구는 아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!를 표시한다.
  • Empty view에는 ic_new_donation 아이콘과 후원하기 텍스트가 있는 Color.soda400 capsule button을 표시한다.
  • 단, 내 채널인 경우 Empty view 내부 후원하기 버튼을 표시하지 않는다.
  • Empty view의 후원하기 버튼 tap은 홈 탭 후원하기 버튼 tap action과 동일하게 동작한다.
  • Empty 상태에서는 하단 고정 후원하기 버튼을 표시하지 않는다.
  • donationCount != 0 || !rankings.isEmpty이면 홈 탭 plus button 위치에 후원하기 floating button을 표시한다.
  • 단, 내 채널인 경우 후원하기 floating button을 표시하지 않는다.
  • 후원하기 floating button은 Color.soda400 배경과 ic_new_donation 아이콘만 표시하고 텍스트는 표시하지 않는다.

6. Success Criteria

  • 후원 탭 진입 시 GET /api/v2/creator-channels/{creatorId}/donations?page=0&size=20가 호출된다.
  • sort-bar에는 전체 {donationCount}만 표시되고 정렬 버튼/아이콘/popup은 표시되지 않는다.
  • rankings가 있으면 후원 랭킹 카드에 프로필 그리드와 rank number가 표시된다.
  • 후원 랭킹 카드의 전체보기를 터치하면 UserProfileDonationAllView로 이동한다.
  • donations 목록은 기존 홈 탭 후원 카드와 같은 후원 금액별 상단 컬러, can pill, 상대 시간, 메시지 표시 규칙을 따른다.
  • 목록 하단 도달 시 hasNext == true이면 다음 페이지가 append된다.
  • rankings.isEmpty && donationCount == 0이면 Figma 290:9009 empty view가 표시되고, 하단 고정 후원하기 버튼은 숨겨진다.
  • 내 채널인 경우 Empty view의 후원하기 버튼과 우측하단 icon-only 후원하기 버튼이 모두 표시되지 않는다.
  • Empty view의 후원하기 버튼을 누르면 홈 탭과 동일한 후원 dialog가 열린다.
  • donationCount != 0 || !rankings.isEmpty이면 홈 탭 plus button 위치에 ic_new_donation icon-only 후원하기 버튼이 표시된다.
  • Icon-only 후원하기 버튼을 누르면 홈 탭과 동일한 후원 dialog가 열린다.
  • 후원 성공 후 홈 탭 후원 섹션과 후원 탭 랭킹/목록이 갱신된다.
  • 서버 응답에 없는 secret 여부는 클라이언트에서 임의로 만들지 않는다.

7. Technical Constraints

  • 기능 변경은 SodaLive/Sources/V2/CreatorChannel/** 하위에서 해결한다.
  • 후원 탭 전용 View, ViewModel, Repository, API, 모델은 SodaLive/Sources/V2/CreatorChannel/Donation/** 아래에 둔다.
  • CreatorChannelDonationResponseHome/Models/CreatorChannelHomeResponse.swift의 기존 타입을 재사용한다.
  • 후원 item UI는 기존 홈 탭 구현과 중복을 줄이기 위해 공용 컴포넌트로 추출하거나, 홈/후원 탭 양쪽에서 같은 하위 컴포넌트를 사용하도록 최소 범위로 조정한다.
  • 신규 문구가 필요하면 SodaLive/Sources/I18n/I18n.swift에 ko/en/ja를 추가한다.
  • 프로젝트 설정 변경은 신규 Swift 파일 target 등록이 필요한 경우에만 수행한다.

8. Open Questions

  • 해당 없음

9. Verification Notes

  • 2026-07-04: Figma get_design_contextget_screenshot으로 290:9093, 290:9097을 확인했다.
  • 2026-07-04: CreatorChannelView, CreatorChannelDonationSection, CreatorChannelSortBar, ChannelDonationViewModel, 기존 후원 전체 화면 코드를 확인해 재사용 경계를 정했다.
  • 2026-07-04: Figma get_design_contextget_screenshot으로 empty view 290:9009를 확인했다.
  • 2026-07-04: UserProfileDonationAllViewAppStep.userProfileDonationAll(userId:) 기존 라우팅을 확인해 랭킹 전체보기 재사용 경계를 정했다.