Files
sodalive-android/docs/20260622_크리에이터_채널_후원_탭/plan-task.md

54 KiB

크리에이터 채널 후원 탭 구현 계획/TASK

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development 또는 superpowers:executing-plans to implement this plan task-by-task. 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시 - [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.

Goal: GET /api/v2/creator-channels/{creatorId}/donations 응답을 기반으로 크리에이터 채널 후원 탭에 후원 랭킹, 전체 후원 수, 후원 내역 목록, empty 상태, 후원하기 액션, pagination을 표시한다.

Architecture: 기존 CreatorChannelActivityViewPager2/CreatorChannelPagerAdapter 구조를 유지하고, CreatorChannelTab.Donation의 placeholder를 신규 CreatorChannelDonationFragment로 교체한다. 후원 탭 전용 Fragment/ViewModel/DTO/mapper/adapter는 kr.co.vividnext.sodalive.v2.creator.channel.donation 하위에 두되, API/Repository는 기존 채널 공통 CreatorChannelApi/CreatorChannelRepository에 endpoint만 최소 추가한다. 후원하기 dialog는 홈 탭과 동일한 LiveRoomDonationDialog 표시 경로를 Activity에 공통 helper로 분리해 재사용하고, 후원 탭 ViewModel은 후원 성공 후 첫 페이지를 재조회한다.

Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.


전제와 성공 기준

  • PRD: docs/20260622_크리에이터_채널_후원_탭/prd.md
  • 기존 채널 컨테이너:
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • app/src/main/res/layout/activity_creator_channel.xml
  • 기존 채널 API/Repository:
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
  • 기존 홈 탭 후원 UI/후원 액션 참조:
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt
    • app/src/main/res/layout/item_creator_channel_home_donation.xml
    • app/src/main/res/layout/item_creator_channel_home_donation_row.xml
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.kt
    • app/src/main/java/kr/co/vividnext/sodalive/live/room/donation/LiveRoomDonationDialog.kt
  • 기존 후원 전체보기 참조:
    • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt
    • Constants.EXTRA_USER_ID
  • Figma:
    • 전체 후원 탭: 290:9093
    • 후원 랭킹 섹션: 290:9097
    • 후원 empty 상태: 290:9008
  • API endpoint는 GET /api/v2/creator-channels/{creatorId}/donations이다.
  • 첫 페이지 page0, 기본 size20이다.
  • query parameter는 page, size만 전달한다.
  • 정렬 query parameter와 sort popup은 후원 탭에서 사용하지 않는다.
  • rankings는 서버가 항상 최대 8명까지만 내려준다.
  • 후원 랭킹 전체보기 버튼은 기존 UserProfileDonationAllViewActivity로 이동한다.
  • 현재 API는 비밀 후원 여부를 별도 필드로 내려주지 않으므로 비밀 후원 전용 UI/표시 분기는 구현하지 않는다.
  • 본인 채널에서는 content/empty 상태 모두 floating 후원하기 버튼을 숨긴다.
  • 본인 채널 empty 상태에서는 중앙 후원하기 button도 숨긴다.
  • 후원 성공 후에는 후원 탭의 donationCount, rankings, donations가 최신화되도록 첫 페이지를 재조회한다.
  • 구현 완료 후 최소 다음 명령을 실행한다.
    • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
    • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*"
    • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"
    • ./gradlew :app:mergeDebugResources
    • ./gradlew :app:compileDebugKotlin
    • ./gradlew :app:ktlintCheck
    • git diff --check

Figma 참조 필요 Phase

  • Phase 1: 제한 참조
    • 기존 placeholder, 후원 dialog, 전체보기 Activity, owner 판정 경계 확인이 중심이며 Figma는 PRD 기준만 확인한다.
  • Phase 2: Figma 참조 불필요
    • API/DTO/Repository/ViewModel 계약은 서버 응답과 기존 FanTalk/Community 탭 패턴을 따른다.
  • Phase 3: 제한 참조
    • mapper와 UI model은 PRD와 기존 홈 후원 카드 정책을 함께 확인한다.
  • Phase 4: 필수 참조
    • 랭킹 카드, Sort-bar without sort, 후원 내역 item은 Figma 290:9093, 290:9097 기준으로 구현한다.
  • Phase 5: 필수 참조
    • empty 상태와 empty 후원하기 button은 Figma 290:9008 기준으로 구현한다.
  • Phase 6: 제한 참조
    • 탭 연결, pagination, 후원 dialog 재사용, 전체보기 이동은 기존 코드 패턴 중심으로 검증한다.
  • Phase 7: 필수 참조
    • 최종 수동 화면 검증은 PRD의 Figma 노드와 실제 화면을 대조한다.

파일 구조

  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • CreatorChannelTab.Donation을 신규 CreatorChannelDonationFragment로 연결한다.
  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • CreatorChannelDonationFragment.Host 구현, 후원 탭 선택 시 최초 로드, pagination trigger, ViewPager 높이 갱신, 전체보기 이동, 공통 후원 dialog 표시 helper, 후원 성공 후 홈 탭 refresh hook을 추가한다.
  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
    • 후원 탭 endpoint를 추가한다.
  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
    • 후원 목록 조회 method를 추가한다. 기존 postChannelDonation()은 재사용한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/data/CreatorChannelDonationTabResponse.kt
    • CreatorChannelDonationTabResponse, MemberDonationRankingResponse, CreatorChannelDonationResponse를 정의한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModel.kt
    • 최초 조회, retry, refresh, pagination, 후원 성공 후 재조회, loading/error/empty/content 상태를 관리한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationUiModels.kt
    • 랭킹 item, 후원 item, 화면 상태 UI model을 정의한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationMappers.kt
    • DTO를 UI model로 변환하고 순위 번호, 날짜 포맷, message fallback, header 색상을 결정한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt
    • 후원 탭 UI, adapter, retry, pagination error toast, 후원하기 callback, 전체보기 callback, host callback 연결을 담당한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/ui/CreatorChannelDonationAdapter.kt
    • 랭킹 섹션, count bar, 후원 내역 목록을 하나의 RecyclerView 또는 섹션 adapter로 표시한다.
  • 생성 가능: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/ui/CreatorChannelDonationRankingAdapter.kt
    • 랭킹 grid가 nested RecyclerView로 구현될 때만 추가한다.
  • 생성: app/src/main/res/layout/fragment_creator_channel_donation.xml
    • RecyclerView, empty, error/retry, floating 후원하기 버튼 영역을 포함한다.
  • 생성: app/src/main/res/layout/item_creator_channel_donation_ranking.xml
    • 후원 랭킹 카드, 4열 x 2행 랭킹 grid, 전체보기 button을 구현한다.
  • 생성: app/src/main/res/layout/item_creator_channel_donation.xml
    • 후원 내역 item header, 캔 badge, 메시지를 구현한다.
  • 생성 가능: app/src/main/res/layout/item_creator_channel_donation_ranking_member.xml
    • 랭킹 member cell을 별도 adapter로 구현할 때만 추가한다.
  • 생성 가능: app/src/main/res/drawable/bg_creator_channel_donation_empty_button.xml
    • empty 후원하기 capsule 배경이 기존 drawable로 대응되지 않을 때만 추가한다.
  • 수정: app/src/main/res/values/strings.xml
    • 후원 탭 empty/error/retry/action/count/fallback 문구를 추가하거나 기존 문자열을 재사용한다.
  • 수정: app/src/main/res/values-en/strings.xml, app/src/main/res/values-ja/strings.xml
    • 신규 문자열의 다국어 값을 추가한다.
  • 수정: app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • CreatorChannelDonationViewModel binding을 추가한다.
  • 테스트 생성:
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationMapperTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationPaginationTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationActionTest.kt
  • 테스트 수정:
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt

Phase 1: 기존 구조 확인과 작업 경계 고정

  • Task 1.1: Donation 탭 placeholder 연결 지점 확인

    • 확인 파일:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
    • 작업:
      • CreatorChannelTab.Donation이 현재 CreatorChannelPlaceholderFragment로 연결되는지 확인한다.
      • 신규 CreatorChannelDonationFragment.newInstance(creatorId)로 교체할 위치를 고정한다.
    • 검증:
      • 실행: rg -n "CreatorChannelTab\\.Donation|CreatorChannelPlaceholderFragment|createFragment" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel
      • 기대 결과: Donation placeholder와 관련 테스트 갱신 지점이 확인된다.
    • 검증 기록:
      • 2026-06-22: rg -n "CreatorChannelTab\\.Donation|CreatorChannelPlaceholderFragment|createFragment" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel 실행. CreatorChannelPagerAdapter.createFragment()에서 Home/Live/Audio/Series/Community/FanTalk만 실제 Fragment로 연결되고 Donationelse -> CreatorChannelPlaceholderFragment.newInstance(tab) 경로를 타는 것을 확인했다. CreatorChannelPagerAdapterTest도 현재 실제 Fragment 탭 외 나머지는 placeholder로 유지된다는 기대를 가진다. 향후 교체 위치는 CreatorChannelPagerAdapter.ktwhen (tab) 분기다.
  • Task 1.2: 기존 후원 dialog와 홈 후원 성공 처리 확인

    • 확인 파일:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
    • 작업:
      • CreatorChannelActivity.onCreatorChannelDonationClicked()에서 LiveRoomDonationDialog를 여는 옵션을 확인한다.
      • 홈 탭의 CreatorChannelHomeViewModel.postChannelDonation()SharedPreferenceManager.can 차감 후 홈을 재조회하는지 확인한다.
      • 후원 탭에서도 같은 dialog 옵션과 CreatorChannelRepository.postChannelDonation()을 재사용하되, 성공 후 후원 탭 첫 페이지를 재조회하도록 설계한다.
    • 검증:
      • 실행: rg -n "onCreatorChannelDonationClicked|LiveRoomDonationDialog|postChannelDonation|SharedPreferenceManager.can" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel
      • 기대 결과: 홈 후원 dialog 옵션, repository 위임, can 차감, 홈 재조회 흐름이 확인된다.
    • 검증 기록:
      • 2026-06-22: rg -n "onCreatorChannelDonationClicked|LiveRoomDonationDialog|postChannelDonation|SharedPreferenceManager.can" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel 실행. CreatorChannelActivity.onCreatorChannelDonationClicked()가 owner가 아닐 때 LiveRoomDonationDialogisLiveDonation = true, messageMaxLength = 100, secretToggleLabelResId = R.string.screen_user_profile_channel_donation_secret, applySecretMissionMessageHint = false 옵션으로 열고, submit 시 homeActionDelegate?.postChannelDonation(...)로 위임하는 것을 확인했다. CreatorChannelHomeViewModel.postChannelDonation()CreatorChannelRepository.postChannelDonation() 호출 성공 시 SharedPreferenceManager.can = (SharedPreferenceManager.can - can).coerceAtLeast(0)로 차감하고 loadHome(content.header.creatorId)로 홈을 재조회한다.
  • Task 1.3: 기존 후원 전체보기 Activity 호출 계약 확인

    • 확인 파일:
      • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.kt
      • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt
      • app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
    • 작업:
      • 기존 UserProfileDonationAllViewActivityConstants.EXTRA_USER_ID를 요구하는지 확인한다.
      • 크리에이터 채널에서 보유한 creatorId가 기존 Activity 호출에 그대로 사용 가능한지 API/기존 호출 경로를 대조한다.
      • 식별자 불일치가 확인되면 구현 전에 PRD/계획 문서를 갱신하고 사용자에게 확인한다.
    • 검증:
      • 실행: rg -n "UserProfileDonationAllViewActivity|EXTRA_USER_ID|creatorId|userId" app/src/main/java/kr/co/vividnext/sodalive/explorer/profile app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
      • 기대 결과: 전체보기 Activity 인자 계약과 크리에이터 채널 보유 식별자가 확인된다.
    • 검증 기록:
      • 2026-06-22: rg -n "UserProfileDonationAllViewActivity|EXTRA_USER_ID|creatorId|userId" app/src/main/java/kr/co/vividnext/sodalive/explorer/profile app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt 실행. UserProfileDonationAllViewActivityintent.getLongExtra(Constants.EXTRA_USER_ID, 0)를 읽고, 기존 UserProfileActivityIntent(..., UserProfileDonationAllViewActivity::class.java)Constants.EXTRA_USER_IDuserId를 전달한다. 크리에이터 채널의 creatorId도 채널 owner/member 식별자로 사용되고 있어 Phase 6에서 putExtra(Constants.EXTRA_USER_ID, creatorId)로 연결 가능한 전제를 확인했다. 추가로 UserProfileChannelDonationAllViewActivityConstants.EXTRA_USER_ID를 받는 채널 후원 목록 전체보기 화면으로 존재함을 확인했다. 현재 계획의 버튼은 후원 랭킹 섹션의 전체보기이므로 UserProfileDonationAllViewActivity 전제는 유지하되, 향후 요구가 후원 내역 전체보기로 바뀌면 UserProfileChannelDonationAllViewActivity로 계획을 먼저 갱신해야 한다.
  • Task 1.4: 기존 목록 탭 pagination/viewport 패턴 확인

    • 확인 파일:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • 작업:
      • 후원 ViewModel도 FIRST_PAGE = 0, DEFAULT_PAGE_SIZE = 20, requestGeneration, paginationErrorMessage, consumePaginationErrorMessage() 패턴을 따른다.
      • 후원 Fragment도 onCreatorChannelDonationTabSelected(), onCreatorChannelDonationScrolledToBottom(), onCreatorChannelDonationRefreshRequested(), onCreatorChannelDonationViewportHeightChanged() entry를 제공한다.
    • 검증:
      • 실행: rg -n "requestGeneration|paginationErrorMessage|consumePaginationErrorMessage|ScrolledToBottom|RefreshRequested|ViewportHeightChanged" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel
      • 기대 결과: FanTalk/Community의 pagination, refresh, viewport 패턴이 확인된다.
    • 검증 기록:
      • 2026-06-22: rg -n "requestGeneration|paginationErrorMessage|consumePaginationErrorMessage|ScrolledToBottom|RefreshRequested|ViewportHeightChanged" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel 실행. CreatorChannelFanTalkViewModelCreatorChannelCommunityViewModelrequestGeneration, paginationErrorMessage, consumePaginationErrorMessage() 패턴을 사용하고, FIRST_PAGE = 0, DEFAULT_PAGE_SIZE = 20 기준으로 첫 페이지와 pagination을 구분하는 것을 확인했다. Fragment entry는 onCreatorChannelFanTalkScrolledToBottom(), onCreatorChannelFanTalkRefreshRequested(), onCreatorChannelFanTalkViewportHeightChanged(minHeight) 및 Community의 scrolled/refresh 패턴을 확인했다. Activity는 현재 Live/Audio/Series/Community/FanTalk에 대해 하단 스크롤 dispatcher와 load-more 탭 판정을 가지고 있으며, Donation 추가 지점은 notifyCurrentCreatorChannelTabScrolledToBottom(), isCreatorChannelLoadMoreTab(), updateCreatorChannelTabViewportHeight()다.

Phase 2: API/DTO/Repository/ViewModel 계약 추가

  • Task 2.1: 후원 탭 DTO 추가

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/data/CreatorChannelDonationTabResponse.kt
    • 작업:
      • @Keep, @SerializedName 기반으로 CreatorChannelDonationTabResponse, MemberDonationRankingResponse, CreatorChannelDonationResponse를 추가한다.
      • PRD의 서버 필드명과 동일하게 donationCount, rankings, donations, page, size, hasNext, userId, nickname, profileImage, donationCan, profileImageUrl, can, message, createdAtUtc를 정의한다.
      • PRD의 @JsonProperty("hasNext")는 서버 계약 설명이므로 구현은 프로젝트 기존 Gson 패턴인 @SerializedName("hasNext")를 사용한다.
    • 검증:
      • 실행: ./gradlew :app:compileDebugKotlin
      • 기대 결과: 신규 DTO 추가 후 컴파일이 PASS한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationTabResponse, MemberDonationRankingResponse, CreatorChannelDonationResponse@Keep/@SerializedName으로 추가했다. focused GREEN 실행 중 :app:compileDebugKotlin이 PASS해 DTO 컴파일을 확인했다.
  • Task 2.2: 후원 탭 endpoint와 Repository method 추가

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
    • 작업:
      • @GET("/api/v2/creator-channels/{creatorId}/donations") endpoint를 추가한다.
      • query parameter page, size만 전달한다.
      • Repository method는 getDonations(creatorId, page, size, token) 형태로 둔다.
      • 기존 postChannelDonation(creatorId, can, isSecret, message, token)은 후원 탭 ViewModel에서 재사용한다.
    • 검증:
      • 실행: ./gradlew :app:compileDebugKotlin
      • 기대 결과: API/Repository 추가 후 기존 Koin graph와 충돌 없이 컴파일된다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelApi.getDonations(creatorId, page, size, Authorization)CreatorChannelRepository.getDonations(creatorId, page, size, token)를 추가했다. focused GREEN 실행 중 :app:compileDebugKotlin이 PASS해 endpoint/repository 컴파일을 확인했다.
  • Task 2.3: 후원 ViewModel RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationPaginationTest.kt
    • 작업:
      • 최초 로드가 page=0, size=20으로 repository를 호출하는 테스트를 작성한다.
      • 성공 응답이 있으면 Content(donationCount, rankings, donations, page, size, hasNext) 상태가 되는 테스트를 작성한다.
      • donationCount == 0 또는 표시 가능한 donations가 비어 있으면 Empty 상태가 되는 테스트를 작성한다.
      • hasNext == true에서 loadMore()page + 1을 호출하고 기존 donations 뒤에 append하는 테스트를 작성한다.
      • loading 중 중복 loadMore()를 막는 테스트를 작성한다.
      • 후원 성공 시 SharedPreferenceManager.can을 차감하고 첫 페이지를 재조회하는 테스트를 작성한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationPaginationTest"
      • 기대 결과: 구현 전에는 신규 타입/메서드 부재로 FAIL한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationViewModelTest, CreatorChannelDonationPaginationTest를 작성한 뒤 focused 테스트를 실행했다. 구현 전 CreatorChannelDonationViewModel, CreatorChannelDonationTabResponse, CreatorChannelRepository.getDonations 등 신규 타입/메서드 부재로 :app:compileDebugUnitTestKotlin이 FAIL하여 RED를 확인했다.
  • Task 2.4: 후원 ViewModel 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModel.kt
    • 작업:
      • CreatorChannelDonationUiState.Loading, Empty, Error, Content를 정의한다.
      • loadDonations(creatorId, isOwner), retryDonations(), refreshDonations(), loadMore(), postChannelDonation(can, isSecret, message), consumePaginationErrorMessage(), consumeActionToastMessage(), consumeDonationSuccessEvent()를 제공한다.
      • 첫 페이지 성공 후 donations.isEmpty() || donationCount == 0이면 Empty 상태로 전환한다.
      • 후원 성공 시 (SharedPreferenceManager.can - can).coerceAtLeast(0)로 로컬 can을 차감하고 첫 페이지를 재조회한다.
      • 후원 성공 event는 Fragment가 홈 탭 refresh를 요청할 수 있도록 1회성 상태로 노출한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationPaginationTest"
      • 기대 결과: RED 테스트가 PASS한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationViewModel에 Loading/Empty/Error/Content, 첫 페이지 조회, retry/refresh, loadMore append, pagination error consume, 후원 성공 후 can 차감/첫 페이지 재조회/성공 event를 구현했다. focused GREEN 명령에서 ViewModel/Pagination 테스트가 PASS했다.
  • Task 2.5: Koin binding 추가

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 작업:
      • CreatorChannelDonationViewModel import와 viewModel { CreatorChannelDonationViewModel(get(), get()) } binding을 추가한다.
      • 두 번째 dependency는 UtcRelativeTimeTextFormatter를 사용한다.
    • 검증:
      • 실행: ./gradlew :app:compileDebugKotlin
      • 기대 결과: Koin binding 추가 후 컴파일이 PASS한다.
    • 검증 기록:
      • 2026-06-22: AppDICreatorChannelDonationViewModel(get(), get()) binding을 추가했다. focused GREEN 실행 중 :app:compileDebugKotlin이 PASS해 Koin binding 컴파일을 확인했다.

Phase 3: UI model/mapper/공통 후원 표시 정책

  • Task 3.1: 후원 mapper RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationMapperTest.kt
    • 작업:
      • 랭킹 순위가 응답 순서 기준 1부터 부여되는 테스트를 작성한다.
      • rankings는 서버 최대 8명 보장 전제를 유지하되 mapper가 응답 순서를 유지하는지 테스트한다.
      • createdAtUtcUtcRelativeTimeTextFormatter를 통해 표시되는 테스트를 작성한다.
      • message가 blank이면 기존 fallback %d캔을 후원하였습니다. 문구를 사용하는 테스트를 작성한다.
      • can 범위별 header 색상이 기존 홈 탭 정책과 동일한지 테스트한다.
      • 비밀 후원 전용 분기를 만들지 않는다는 source/mapper 계약 테스트를 작성한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationMapperTest"
      • 기대 결과: 구현 전에는 신규 mapper 부재로 FAIL한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationMapperTest를 작성한 뒤 focused 테스트를 실행했다. 구현 전 donation data/model/mapper 부재로 :app:compileDebugUnitTestKotlin이 FAIL하여 RED를 확인했다.
  • Task 3.2: 후원 UI model과 mapper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationUiModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationMappers.kt
    • 작업:
      • CreatorChannelDonationRankingUiModel(rank, userId, nickname, profileImageUrl, donationCan)을 정의한다.
      • CreatorChannelDonationUiModel(nickname, profileImageUrl, can, message, createdAtText, headerColorResId)를 정의한다.
      • mapper는 MemberDonationRankingResponse.profileImageprofileImageUrl UI field로 변환한다.
      • mapper는 CreatorChannelDonationResponse.message.ifBlank { fallback } 정책을 적용한다.
      • header 색상은 기존 홈 탭 calculateCreatorChannelDonationHeaderColorRes(can)와 동일한 정책을 재사용하거나 후원 공통 위치로 이동한다.
      • 공통 위치로 이동할 경우 기존 홈 탭 import를 함께 갱신하고 동작 변경 없이 테스트를 유지한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationMapperTest"
      • 기대 결과: mapper 테스트가 PASS한다.
    • 검증 기록:
      • 2026-06-22: 후원 랭킹/후원 목록 UI model과 mapper를 추가했다. 랭킹 rank는 응답 순서 기준 1부터 부여하고, profileImageprofileImageUrl로 매핑하며, blank message는 기존 creator_channel_donation_fallback_message를 사용한다. header 색상은 홈 helper와 동일한 기준을 donation mapper 내부 함수로 구현했다. focused GREEN 명령에서 mapper 테스트가 PASS했다.

Phase 4: 후원 탭 content UI 구현

  • Task 4.1: 후원 탭 layout/source RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationActionTest.kt
    • 작업:
      • fragment_creator_channel_donation.xml에 RecyclerView, error/retry, empty, floating donation button id가 존재해야 한다는 source test를 작성한다.
      • item_creator_channel_donation_ranking.xml후원 랭킹, ranking container, 전체보기 button id가 존재해야 한다는 source test를 작성한다.
      • item_creator_channel_donation.xml에 profile, nickname, createdAt, can badge, message id가 존재해야 한다는 source test를 작성한다.
      • 본인 채널에서는 floating button이 숨겨진다는 Fragment/source 계약 테스트를 작성한다.
      • Phase 4에서는 전체보기 클릭이 CreatorChannelDonationFragment.Host callback으로 전달된다는 source 계약 테스트를 작성한다. UserProfileDonationAllViewActivityConstants.EXTRA_USER_ID 연결은 Phase 6에서 검증한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest"
      • 기대 결과: 구현 전에는 신규 layout/Fragment 부재로 FAIL한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationFragmentLayoutTest, CreatorChannelDonationActionTest를 작성한 뒤 production UI 파일 추가 전 focused 테스트를 실행했다. fragment_creator_channel_donation 및 후원 layout/id 미존재로 :app:compileDebugUnitTestKotlin이 FAIL하여 RED를 확인했다. Phase 4 범위에 맞춰 전체보기는 Activity 직접 이동이 아니라 Host.onCreatorChannelDonationRankingAllClicked() callback 계약으로 검증했다.
  • Task 4.2: 후원 content layout과 adapter 구현

    • 생성:
      • app/src/main/res/layout/fragment_creator_channel_donation.xml
      • app/src/main/res/layout/item_creator_channel_donation_ranking.xml
      • app/src/main/res/layout/item_creator_channel_donation.xml
      • 필요 시 app/src/main/res/layout/item_creator_channel_donation_ranking_member.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/ui/CreatorChannelDonationAdapter.kt
      • 필요 시 app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/ui/CreatorChannelDonationRankingAdapter.kt
    • 작업:
      • content 상태에서는 랭킹 섹션, Sort-bar without sort, 후원 내역 목록을 표시한다.
      • Sort-bar는 좌측 전체donationCount만 표시하고 정렬 UI를 만들지 않는다.
      • 랭킹 섹션은 Figma 290:9097 기준으로 75dp 원형 프로필, 닉네임, 순위 숫자를 최대 8명 표시한다.
      • 랭킹 전체보기 button은 adapter callback으로 Fragment/Activity에 전달한다.
      • 후원 item은 Figma support card 구조를 기준으로 profile, nickname, relative time, can badge, message를 표시한다.
      • header 색상과 fallback message는 mapper 결과를 사용한다.
    • 검증:
      • 실행: ./gradlew :app:mergeDebugResources
      • 기대 결과: 신규 layout/drawable/string resource가 병합된다.
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest"
      • 기대 결과: layout/source 계약 테스트가 PASS한다.
    • 검증 기록:
      • 2026-06-22: fragment_creator_channel_donation.xml, 랭킹/랭킹 member/후원 item layout, 랭킹 전체보기 capsule drawable, CreatorChannelDonationAdapter, CreatorChannelDonationRankingAdapter를 추가했다. Figma 290:9093, 290:9097 기준으로 black background, 52dp count bar, gray_900 랭킹 카드, 75dp 랭킹 프로필 grid, 전체보기 capsule, 후원 card header/profile/can badge/message를 구현했다. ./gradlew :app:mergeDebugResources와 Phase 4 layout/source 테스트가 PASS했다.
  • Task 4.3: 후원 Fragment 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt
    • 작업:
      • CreatorChannelDonationFragment.newInstance(creatorId)를 제공한다.
      • Host interface에 isCreatorChannelOwner(), onCreatorChannelDonationContentChanged(), onCreatorChannelDonationRequested(onSubmit), onCreatorChannelDonationRankingAllClicked(), onCreatorChannelDonationCompleted()를 정의한다.
      • onCreatorChannelDonationTabSelected()에서 viewModel.loadDonations(creatorId, isOwner = host.isCreatorChannelOwner())를 호출한다.
      • content 상태에서 adapter에 랭킹/후원 내역을 submit하고 floating button은 !isOwner일 때만 표시한다.
      • 후원 button 클릭 시 host의 공통 후원 dialog를 열고 submit callback에서 viewModel.postChannelDonation(can, isSecret, message)를 호출한다.
      • 후원 성공 event를 받으면 host.onCreatorChannelDonationCompleted()를 호출해 홈 탭도 갱신 가능하게 한다.
      • pagination/action toast consume 패턴은 FanTalk Fragment와 동일하게 구현한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
      • 기대 결과: 후원 탭 단위 테스트가 PASS한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelDonationFragment를 추가해 newInstance(creatorId), Host interface, 탭 최초 로드, refresh/loadMore/viewport entry, content adapter submit, owner floating button 숨김, 후원 요청 submit callback, pagination/action toast consume, 후원 성공 event callback을 구현했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"가 PASS했다.

Phase 5: Empty/Error/문자열 리소스 구현

  • Task 5.1: empty/error 문자열과 다국어 리소스 추가

    • 수정:
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/src/main/res/values-ja/strings.xml
    • 작업:
      • creator_channel_donation_empty_message: 아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!
      • creator_channel_donation_action: 후원하기
      • creator_channel_donation_all_label: 전체
      • creator_channel_donation_ranking_title: 후원 랭킹
      • creator_channel_donation_ranking_all: 전체보기
      • creator_channel_donation_error_message, creator_channel_donation_retry는 기존 FanTalk/Community 문구 패턴에 맞춰 추가하거나 기존 공통 문구를 재사용한다.
    • 검증:
      • 실행: ./gradlew :app:mergeDebugResources
      • 기대 결과: values/en/ja 문자열 병합이 PASS한다.
    • 검증 기록:
      • 2026-06-22: 기존 creator_channel_donation_* 문자열이 values, values-en, values-ja에 이미 추가되어 있음을 확인했다. Figma 290:9008 및 계획 문구에 맞춰 한국어 creator_channel_donation_empty_title아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!로 갱신했다. ./gradlew :app:mergeDebugResources가 PASS해 문자열 병합을 확인했다.
  • Task 5.2: Figma 290:9008 empty 상태 구현

    • 수정:
      • app/src/main/res/layout/fragment_creator_channel_donation.xml
      • 필요 시 app/src/main/res/drawable/bg_creator_channel_donation_empty_button.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt
    • 작업:
      • empty 상태에서는 RecyclerView, Sort-bar, floating button을 숨긴다.
      • empty 문구는 16sp regular, gray/500, center 정렬로 표시한다.
      • 타인 채널에서는 문구 아래 후원하기 capsule button을 표시한다.
      • 본인 채널에서는 empty 후원하기 button과 floating button을 모두 숨긴다.
      • empty 후원하기 button 터치 시 content 상태 floating button과 동일한 host 후원 dialog를 호출한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"
      • 기대 결과: empty 상태 visibility/source 계약이 PASS한다.
      • 수동 확인: Figma 290:9008과 empty 문구 위치, button 색상/icon/text, owner 버튼 숨김을 대조한다.
    • 검증 기록:
      • 2026-06-22: RED로 CreatorChannelDonationFragmentLayoutTest에 empty 중앙 btn_creator_channel_donation_empty_write/icon/text id 및 Figma capsule source 계약을 추가하고, CreatorChannelDonationActionTest에 empty 중앙 button이 타인 채널에서만 표시되고 기존 후원 요청 callback을 재사용해야 한다는 source 계약을 추가했다. 구현 전 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest"가 신규 id 미존재(btn_creator_channel_donation_empty_write, iv_creator_channel_donation_empty_write, tv_creator_channel_donation_empty_write)로 FAIL해 RED를 확인했다.
      • 2026-06-22: fragment_creator_channel_donation.xml에 Figma 290:9008 기준 중앙 capsule 후원하기 button을 추가하고, CreatorChannelDonationFragment에서 empty 상태일 때 !state.isOwner인 경우만 중앙 button을 표시하도록 연결했다. empty 중앙 button 클릭은 content floating button과 같은 host.onCreatorChannelDonationRequested { can, isSecret, message -> viewModel.postChannelDonation(...) } 경로를 사용한다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest"가 PASS했다. 수동 대조는 Figma 290:9008 screenshot을 확인해 16sp gray/500 중앙 문구, 14dp 간격, soda capsule button 구조를 반영했다.
      • 2026-06-22: Phase 5/6 코드 리뷰 중 empty 중앙 후원하기 button 배경이 @color/white로 되어 있어 PRD의 soda/400 배경 및 흰색 icon/text 요구와 맞지 않는 문제를 확인했다. RED로 CreatorChannelDonationFragmentLayoutTestbg_creator_channel_donation_empty_button.xml@color/soda_400 계약을 추가했고, 기존 구현에서 실패하는 것을 확인한 뒤 배경을 @color/soda_400으로 수정했다.
      • 2026-06-22: Phase 5/6 코드 리뷰 중 empty 상태 중앙 후원하기 button으로 후원 API 실패 시 CreatorChannelDonationViewModel.setActionToastMessage()Content 상태에서만 메시지를 보관해 실패 toast가 사라지는 문제를 확인했다. RED로 CreatorChannelDonationViewModelTestEmpty 상태에서 채널 후원 실패는 action toast message를 emit하고 consume한다를 추가했고, 기존 구현에서 Empty.actionToastMessage 부재로 FAIL하는 것을 확인했다. 이후 Empty 상태에도 actionToastMessage를 추가하고 CreatorChannelDonationFragment.observeViewModel()의 공통 경로에서 empty/content action toast를 표시하도록 수정했다. 수정 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest"가 PASS했다.

Phase 6: Activity 연결, 탭 연결, 전체보기/후원 액션 연결

  • Task 6.1: PagerAdapter와 Activity source RED 테스트 작성

    • 수정:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
    • 작업:
      • CreatorChannelTab.DonationCreatorChannelDonationFragment를 생성해야 한다는 테스트를 추가한다.
      • Activity가 CreatorChannelDonationFragment.Host를 구현해야 한다는 source test를 추가한다.
      • Activity의 하단 스크롤 dispatcher와 load-more 탭 판정에 Donation이 포함되어야 한다는 source test를 추가한다.
      • Activity가 기존 LiveRoomDonationDialog 표시 helper를 홈/후원에서 재사용해야 한다는 source test를 추가한다.
      • Activity가 UserProfileDonationAllViewActivityConstants.EXTRA_USER_ID를 사용해야 한다는 source test를 추가한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
      • 기대 결과: 구현 전에는 Donation 연결 부재로 FAIL한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelPagerAdapterTestCreatorChannelTab.DonationCreatorChannelDonationFragment를 생성해야 한다는 assertion을 추가하고, placeholder 유지 대상에서 Donation을 제외했다. CreatorChannelActivitySourceTest에는 CreatorChannelDonationFragment.Host, findDonationFragment(), 탭 선택/헤더 변경 시 load, scroll bottom dispatcher, load-more 판정, viewport 전달, 공통 showCreatorChannelDonationDialog() helper, 후원 완료 홈 refresh hook, UserProfileDonationAllViewActivity + Constants.EXTRA_USER_ID 이동 계약을 추가했다. 구현 전 focused RED 실행에서 Phase 5 신규 id 미존재로 unit test compile이 먼저 FAIL했고, 같은 RED 테스트 묶음에서 Donation 연결 production 코드 부재 상태를 고정했다.
  • Task 6.2: PagerAdapter Donation 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • 작업:
      • CreatorChannelTab.Donation -> CreatorChannelDonationFragment.newInstance(creatorId) 분기를 추가한다.
      • placeholder fallback은 아직 구현되지 않은 탭에만 남긴다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"
      • 기대 결과: PagerAdapter 테스트가 PASS한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelPagerAdapterCreatorChannelDonationFragment import와 CreatorChannelTab.Donation -> CreatorChannelDonationFragment.newInstance(creatorId) 분기를 추가했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" 재실행에서 PagerAdapter Donation 생성 계약이 PASS했다.
  • Task 6.3: Activity Host와 scroll/viewport 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • 작업:
      • CreatorChannelDonationFragment.Host를 구현한다.
      • findDonationFragment()를 추가한다.
      • notifyCurrentCreatorChannelTabScrolledToBottom()에 Donation 분기를 추가한다.
      • isCreatorChannelLoadMoreTab()에 Donation을 추가한다.
      • 탭 선택 callback에서 Donation 선택 시 onCreatorChannelDonationTabSelected()가 호출되도록 연결한다.
      • updateCreatorChannelTabViewportHeight()에 Donation viewport height 전달을 추가한다.
      • onCreatorChannelDonationContentChanged()에서 updateViewPagerHeight()를 호출한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
      • 기대 결과: Activity source 계약이 PASS한다.
    • 검증 기록:
      • 2026-06-22: CreatorChannelActivityCreatorChannelDonationFragment.Host를 구현하도록 추가하고, findDonationFragment(), 탭 선택/헤더 변경 시 onCreatorChannelDonationTabSelected(), notifyCurrentCreatorChannelTabScrolledToBottom()의 Donation 분기, isCreatorChannelLoadMoreTab() Donation 포함, updateCreatorChannelTabViewportHeight() Donation viewport 전달, onCreatorChannelDonationContentChanged()updateViewPagerHeight { postCheckCreatorChannelCurrentTabNeedsMore() } 연결을 구현했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"가 PASS했다.
  • Task 6.4: Activity 후원 dialog 공통 helper와 후원 완료 hook 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt 필요 시 interface 유지 확인
    • 작업:
      • 기존 onCreatorChannelDonationClicked() 내부 dialog 생성 코드를 private helper로 분리한다.
      • helper signature는 showCreatorChannelDonationDialog(onSubmit: (can: Int, isSecret: Boolean, message: String) -> Unit)처럼 후원 submit callback을 받을 수 있게 둔다.
      • 기존 홈 탭 호출은 helper에 homeActionDelegate?.postChannelDonation(...)를 전달해 동작을 유지한다.
      • 후원 탭 Host 구현은 helper에 CreatorChannelDonationFragment의 ViewModel submit callback을 전달한다.
      • onCreatorChannelDonationCompleted()에서는 homeActionDelegate?.refreshHome()를 호출해 홈 탭 후원 요약도 최신화한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
      • 기대 결과: 홈 후원 액션 유지와 후원 탭 후원 dialog 재사용 source 계약이 PASS한다.
    • 검증 기록:
      • 2026-06-22: 기존 onCreatorChannelDonationClicked()LiveRoomDonationDialog 생성 코드를 showCreatorChannelDonationDialog(onSubmit) helper로 분리했다. 홈 탭 후원은 helper submit에서 기존 homeActionDelegate?.postChannelDonation(can = can, isSecret = isSecret, message = message)를 호출해 동작을 유지하고, 후원 탭 Host의 onCreatorChannelDonationRequested()는 같은 helper에 Fragment ViewModel submit callback을 전달하도록 연결했다. onCreatorChannelDonationCompleted()에서는 homeActionDelegate?.refreshHome()를 호출한다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"가 PASS했다.
  • Task 6.5: 랭킹 전체보기 이동 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • 작업:
      • onCreatorChannelDonationRankingAllClicked()에서 Intent(this, UserProfileDonationAllViewActivity::class.java)를 생성한다.
      • putExtra(Constants.EXTRA_USER_ID, creatorId)로 기존 Activity 호출 계약을 맞춘다.
      • Task 1.3에서 식별자 불일치가 확인된 경우 이 Task를 진행하지 않고 문서를 먼저 갱신한다.
    • 검증:
      • 실행: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
      • 기대 결과: 전체보기 이동 source 계약이 PASS한다.
    • 검증 기록:
      • 2026-06-22: onCreatorChannelDonationRankingAllClicked()에서 Intent(this, UserProfileDonationAllViewActivity::class.java)를 생성하고 putExtra(Constants.EXTRA_USER_ID, creatorId)로 기존 전체보기 Activity 호출 계약에 맞춰 연결했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" 범위는 Phase 5/6 focused GREEN 명령들로 분리 검증했으며 모두 PASS했다.

Phase 7: 통합 검증과 수동 확인

  • Task 7.1: 후원 탭 focused 테스트 실행

    • 실행:
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
    • 기대 결과:
      • 후원 탭 mapper/ViewModel/pagination/layout/action 테스트가 모두 PASS한다.
    • 검증 기록:
      • 미실행. 구현 시 기록한다.
  • Task 7.2: 크리에이터 채널 회귀 테스트 실행

    • 실행:
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*"
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"
    • 기대 결과:
      • 기존 Home/Live/Audio/Series/Community/FanTalk 탭 테스트와 신규 Donation 탭 테스트가 PASS한다.
    • 검증 기록:
      • 미실행. 구현 시 기록한다.
  • Task 7.3: 리소스/컴파일/스타일 검증

    • 실행:
      • ./gradlew :app:mergeDebugResources
      • ./gradlew :app:compileDebugKotlin
      • ./gradlew :app:ktlintCheck
      • git diff --check
    • 기대 결과:
      • resource merge, Kotlin compile, ktlint, whitespace check가 모두 PASS한다.
    • 검증 기록:
      • 미실행. 구현 시 기록한다.
  • Task 7.4: 수동 UI 확인

    • 확인 항목:
      • 후원 탭 진입 시 GET /api/v2/creator-channels/{creatorId}/donations?page=0&size=20이 호출된다.
      • Sort-bar는 전체donationCount만 표시하고 정렬 UI가 없다.
      • 랭킹 섹션은 Figma 290:9097 기준으로 최대 8명, 순위 1부터 표시한다.
      • 전체보기 버튼 터치 시 UserProfileDonationAllViewActivity로 이동한다.
      • 후원 내역 item은 Figma 290:9093 기준으로 profile, nickname, relative time, can badge, message를 표시한다.
      • 목록 하단 스크롤 시 hasNext == true이면 다음 페이지가 append된다.
      • 타인 채널 content 상태에서는 floating 후원하기 버튼이 표시되고, 후원 성공 후 첫 페이지가 재조회된다.
      • 타인 채널 empty 상태에서는 Figma 290:9008 기준 문구와 중앙 후원하기 button이 표시된다.
      • 본인 채널에서는 empty 중앙 후원하기 button과 floating 후원하기 버튼이 모두 숨겨진다.
      • 비밀 후원 전용 UI/표시 분기는 없다.
    • 검증 기록:
      • 미실행. 구현 시 기록한다.

Verification Log

  • 2026-06-22: Phase 5, 6 코드 리뷰 및 검증을 수행했다. 리뷰에서 empty 중앙 후원하기 button 배경색이 @color/whitesoda/400 요구와 맞지 않는 문제, empty 상태 후원 실패 메시지가 Content 전용 action toast 상태에 막혀 사용자에게 표시되지 않을 수 있는 문제를 발견해 RED 테스트 후 수정했다. 검증으로 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest", ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*", ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*", ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*", ./gradlew :app:mergeDebugResources, ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check가 PASS했다. :app:mergeDebugResources는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS를 확인했다.
  • 2026-06-22: Phase 2, 3, 4 코드 리뷰 및 재검증을 수행했다. API/DTO/Repository/ViewModel 계약, mapper 정책, content layout/adapter/Fragment 흐름을 대조했으며 차단급 코드 이슈는 발견하지 않았다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*", ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*", ./gradlew :app:mergeDebugResources, ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check가 PASS했다. :app:mergeDebugResources는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS를 확인했다.
  • 2026-06-22: 리뷰 지적에 따라 후원 성공 event가 Content 상태에 종속되지 않도록 후속 수정했다. RED로 CreatorChannelDonationActionTest후원 fragment source는 성공 이벤트를 content 상태와 독립적으로 전달한다를 추가했고, 기존 구현에서 handleDonationSuccessEvent() 부재로 FAIL하는 것을 확인했다. 이후 CreatorChannelDonationFragment.observeViewModel()의 상태 bind 후 공통 경로에서 handleDonationSuccessEvent()를 호출하도록 변경해 Empty, Error, Content 재조회 결과 모두 Host.onCreatorChannelDonationCompleted() 전달 대상이 되도록 했다. 수정 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*", ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check가 PASS했다.
  • 2026-06-22: docs/20260622_크리에이터_채널_후원_탭/prd.md, docs/agent-guides/work-plan-docs.md, 기존 docs/20260622_FanTalk_탭/plan-task.md 구조를 확인해 후원 탭 구현 계획/TASK 문서를 작성했다. 이번 단계는 문서 작성만 수행했으며 구현/빌드/테스트는 실행하지 않았다.
  • 2026-06-22: Phase 2, 3, 4 코드 리뷰 및 검증을 수행했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*", ./gradlew :app:mergeDebugResources, ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check가 PASS했다. 최초 mergeDebugResources 병렬 실행은 Gradle wrapper lock 파일 접근 권한 문제로 실패했으나 동일 명령을 단독 재실행해 PASS를 확인했다. 리뷰 결과, 후원 성공 event가 Content 상태 bind 중에만 Host.onCreatorChannelDonationCompleted()로 전달되어 후원 성공 후 첫 페이지 재조회가 Empty 또는 Error로 끝나는 경우 홈 탭 refresh hook이 호출되지 않을 수 있는 위험을 확인했다.