81 KiB
크리에이터 채널 후원 탭 구현 계획/TASK
For agentic workers: REQUIRED SUB-SKILL: Use
superpowers:subagent-driven-development또는superpowers:executing-plansto implement this plan task-by-task. 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시- [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
Goal: GET /api/v2/creator-channels/{creatorId}/donations 응답을 기반으로 크리에이터 채널 후원 탭에 후원 랭킹, 전체 후원 수, 후원 내역 목록, empty 상태, 후원하기 액션, pagination을 표시한다.
Architecture: 기존 CreatorChannelActivity의 ViewPager2/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.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktapp/src/main/res/layout/activity_creator_channel.xml
- 기존 채널 API/Repository:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.ktapp/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.ktapp/src/main/res/layout/item_creator_channel_home_donation.xmlapp/src/main/res/layout/item_creator_channel_home_donation_row.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/live/room/donation/LiveRoomDonationDialog.kt
- 기존 후원 전체보기 참조:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.ktConstants.EXTRA_USER_ID
- Figma:
- 전체 후원 탭:
290:9093 - 후원 랭킹 섹션:
290:9097 - 후원 empty 상태:
290:9008 - 후원 empty UI 컨텐츠:
290:9009
- 전체 후원 탭:
- API endpoint는
GET /api/v2/creator-channels/{creatorId}/donations이다. - 첫 페이지
page는0, 기본size는20이다. - query parameter는
page,size만 전달한다. - 정렬 query parameter와 sort popup은 후원 탭에서 사용하지 않는다.
rankings는 서버가 항상 최대 8명까지만 내려준다.- 후원 랭킹
전체보기버튼은 기존UserProfileDonationAllViewActivity로 이동한다. - 현재 API는 비밀 후원 여부를 별도 필드로 내려주지 않으므로 비밀 후원 전용 UI/표시 분기는 구현하지 않는다.
- 사용자가 후원 메시지를 입력하지 않으면 서버는
message를 빈 문자열로 반환한다. - 사용자가 직접 입력한 후원
message는 번역하지 않고 원문을 유지한다. - 시스템 기본 후원 문구, CAN 단위, 상대 시간은 기존 한국어·영어·일본어 resource 경로를 재사용한다.
- 서버 기본 문구의 한국어 패턴을 Android에서 추정하는 보정 로직은 추가하지 않는다.
- 본인 채널에서는 content/empty 상태 모두 floating 후원하기 버튼을 숨긴다.
- 본인 채널 empty 상태에서는 중앙
후원하기button도 숨긴다. - empty UI는 별도
minHeight없이 표시한다. - 후원 랭킹 섹션이 표시되는 경우에도 채널 후원 내역이 없으면 랭킹 섹션 아래에 empty UI를 표시한다.
- 후원 성공 후에는 후원 탭의
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:ktlintCheckgit 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기준으로 구현한다.
- 랭킹 카드, Sort-bar without sort, 후원 내역 item은 Figma
- Phase 5: 필수 참조
- empty 상태와 empty
후원하기button은 Figma290:9008기준으로 구현한다.
- empty 상태와 empty
- Phase 6: 제한 참조
- 탭 연결, pagination, 후원 dialog 재사용, 전체보기 이동은 기존 코드 패턴 중심으로 검증한다.
- Phase 7: 필수 참조
- 최종 수동 화면 검증은 PRD의 Figma 노드와 실제 화면을 대조한다.
- Phase 8: Figma 참조 불필요
- 후원 item의 API
message의미 계약과 locale별 formatter/resource 출력만 검증한다.
- 후원 item의 API
파일 구조
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktCreatorChannelTab.Donation을 신규CreatorChannelDonationFragment로 연결한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktCreatorChannelDonationFragment.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()은 재사용한다.
- 후원 목록 조회 method를 추가한다. 기존
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/data/CreatorChannelDonationTabResponse.ktCreatorChannelDonationTabResponse,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로 대응되지 않을 때만 추가한다.
- empty
- 수정:
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.ktCreatorChannelDonationViewModelbinding을 추가한다.
- 테스트 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationMapperTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationPaginationTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.ktapp/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.ktapp/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.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.ktapp/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로 연결되고Donation은else -> CreatorChannelPlaceholderFragment.newInstance(tab)경로를 타는 것을 확인했다.CreatorChannelPagerAdapterTest도 현재 실제 Fragment 탭 외 나머지는 placeholder로 유지된다는 기대를 가진다. 향후 교체 위치는CreatorChannelPagerAdapter.kt의when (tab)분기다.
- 2026-06-22:
- 확인 파일:
-
Task 1.2: 기존 후원 dialog와 홈 후원 성공 처리 확인
- 확인 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.ktapp/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가 아닐 때LiveRoomDonationDialog를isLiveDonation = 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)로 홈을 재조회한다.
- 2026-06-22:
- 확인 파일:
-
Task 1.3: 기존 후원 전체보기 Activity 호출 계약 확인
- 확인 파일:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/donation/UserProfileDonationAllViewActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/common/Constants.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
- 작업:
- 기존
UserProfileDonationAllViewActivity가Constants.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실행.UserProfileDonationAllViewActivity는intent.getLongExtra(Constants.EXTRA_USER_ID, 0)를 읽고, 기존UserProfileActivity도Intent(..., UserProfileDonationAllViewActivity::class.java)에Constants.EXTRA_USER_ID로userId를 전달한다. 크리에이터 채널의creatorId도 채널 owner/member 식별자로 사용되고 있어 Phase 6에서putExtra(Constants.EXTRA_USER_ID, creatorId)로 연결 가능한 전제를 확인했다. 추가로UserProfileChannelDonationAllViewActivity도Constants.EXTRA_USER_ID를 받는 채널 후원 목록 전체보기 화면으로 존재함을 확인했다. 현재 계획의 버튼은후원 랭킹섹션의전체보기이므로UserProfileDonationAllViewActivity전제는 유지하되, 향후 요구가 후원 내역 전체보기로 바뀌면UserProfileChannelDonationAllViewActivity로 계획을 먼저 갱신해야 한다.
- 2026-06-22:
- 확인 파일:
-
Task 1.4: 기존 목록 탭 pagination/viewport 패턴 확인
- 확인 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModel.ktapp/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를 제공한다.
- 후원 ViewModel도
- 검증:
- 실행:
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실행.CreatorChannelFanTalkViewModel과CreatorChannelCommunityViewModel이requestGeneration,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()다.
- 2026-06-22:
- 확인 파일:
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 컴파일을 확인했다.
- 2026-06-22:
- 생성:
-
Task 2.2: 후원 탭 endpoint와 Repository method 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.ktapp/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 컴파일을 확인했다.
- 2026-06-22:
- 수정:
-
Task 2.3: 후원 ViewModel RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.ktapp/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를 확인했다.
- 2026-06-22:
- 생성:
-
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했다.
- 2026-06-22:
- 생성:
-
Task 2.5: Koin binding 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
CreatorChannelDonationViewModelimport와viewModel { CreatorChannelDonationViewModel(get(), get()) }binding을 추가한다.- 두 번째 dependency는
UtcRelativeTimeTextFormatter를 사용한다.
- 검증:
- 실행:
./gradlew :app:compileDebugKotlin - 기대 결과: Koin binding 추가 후 컴파일이 PASS한다.
- 실행:
- 검증 기록:
- 2026-06-22:
AppDI에CreatorChannelDonationViewModel(get(), get())binding을 추가했다. focused GREEN 실행 중:app:compileDebugKotlin이 PASS해 Koin binding 컴파일을 확인했다.
- 2026-06-22:
- 수정:
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가 응답 순서를 유지하는지 테스트한다.createdAtUtc가UtcRelativeTimeTextFormatter를 통해 표시되는 테스트를 작성한다.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를 확인했다.
- 2026-06-22:
- 생성:
-
Task 3.2: 후원 UI model과 mapper 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationUiModels.ktapp/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.profileImage를profileImageUrlUI 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부터 부여하고,
profileImage를profileImageUrl로 매핑하며, blank message는 기존creator_channel_donation_fallback_message를 사용한다. header 색상은 홈 helper와 동일한 기준을 donation mapper 내부 함수로 구현했다. focused GREEN 명령에서 mapper 테스트가 PASS했다.
- 2026-06-22: 후원 랭킹/후원 목록 UI model과 mapper를 추가했다. 랭킹 rank는 응답 순서 기준 1부터 부여하고,
- 생성:
Phase 4: 후원 탭 content UI 구현
-
Task 4.1: 후원 탭 layout/source RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.ktapp/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.Hostcallback으로 전달된다는 source 계약 테스트를 작성한다.UserProfileDonationAllViewActivity와Constants.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 계약으로 검증했다.
- 2026-06-22:
- 생성:
-
Task 4.2: 후원 content layout과 adapter 구현
- 생성:
app/src/main/res/layout/fragment_creator_channel_donation.xmlapp/src/main/res/layout/item_creator_channel_donation_ranking.xmlapp/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
supportcard 구조를 기준으로 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를 추가했다. Figma290: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했다.
- 2026-06-22:
- 생성:
-
Task 4.3: 후원 Fragment 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt
- 작업:
CreatorChannelDonationFragment.newInstance(creatorId)를 제공한다.Hostinterface에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),Hostinterface, 탭 최초 로드, 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했다.
- 2026-06-22:
- 생성:
Phase 5: Empty/Error/문자열 리소스 구현
-
Task 5.1: empty/error 문자열과 다국어 리소스 추가
- 수정:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/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에 이미 추가되어 있음을 확인했다. Figma290:9008및 계획 문구에 맞춰 한국어creator_channel_donation_empty_title을아직 후원이 없습니다.\n처음으로 크리에이터를 후원해 보세요!로 갱신했다../gradlew :app:mergeDebugResources가 PASS해 문자열 병합을 확인했다.
- 2026-06-22: 기존
- 수정:
-
Task 5.2: Figma
290:9008empty 상태 구현- 수정:
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에 Figma290: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했다. 수동 대조는 Figma290:9008screenshot을 확인해 16sp gray/500 중앙 문구, 14dp 간격, soda capsule button 구조를 반영했다. - 2026-06-22: Phase 5/6 코드 리뷰 중 empty 중앙
후원하기button 배경이@color/white로 되어 있어 PRD의soda/400배경 및 흰색 icon/text 요구와 맞지 않는 문제를 확인했다. RED로CreatorChannelDonationFragmentLayoutTest에bg_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로CreatorChannelDonationViewModelTest에Empty 상태에서 채널 후원 실패는 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했다.
- 2026-06-22: RED로
- 수정:
Phase 6: Activity 연결, 탭 연결, 전체보기/후원 액션 연결
-
Task 6.1: PagerAdapter와 Activity source RED 테스트 작성
- 수정:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 작업:
CreatorChannelTab.Donation이CreatorChannelDonationFragment를 생성해야 한다는 테스트를 추가한다.- Activity가
CreatorChannelDonationFragment.Host를 구현해야 한다는 source test를 추가한다. - Activity의 하단 스크롤 dispatcher와 load-more 탭 판정에 Donation이 포함되어야 한다는 source test를 추가한다.
- Activity가 기존
LiveRoomDonationDialog표시 helper를 홈/후원에서 재사용해야 한다는 source test를 추가한다. - Activity가
UserProfileDonationAllViewActivity와Constants.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:
CreatorChannelPagerAdapterTest에CreatorChannelTab.Donation이CreatorChannelDonationFragment를 생성해야 한다는 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 코드 부재 상태를 고정했다.
- 2026-06-22:
- 수정:
-
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:
CreatorChannelPagerAdapter에CreatorChannelDonationFragmentimport와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했다.
- 2026-06-22:
- 수정:
-
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:
CreatorChannelActivity가CreatorChannelDonationFragment.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했다.
- 2026-06-22:
- 수정:
-
Task 6.4: Activity 후원 dialog 공통 helper와 후원 완료 hook 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/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했다.
- 2026-06-22: 기존
- 수정:
-
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했다.
- 2026-06-22:
- 수정:
Phase 6 후속: Empty UI 랭킹 하단 배치
- Task 6.6: Empty UI 랭킹 하단 배치 후속 수정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/ui/CreatorChannelDonationAdapter.ktapp/src/main/res/layout/fragment_creator_channel_donation.xml- 필요 시
app/src/main/res/layout/item_creator_channel_donation_empty.xml app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationActionTest.kt
- 작업:
- Figma
290:9009기준 empty UI 컨텐츠를 랭킹 섹션 아래에 표시할 수 있도록 adapter item으로 분리한다. - 채널 후원 내역이 없으면
rankings존재 여부와 관계없이 empty UI를 표시한다. rankings가 비어 있지 않으면 랭킹 item 다음에 empty item을 배치한다.- empty UI와 후원 Fragment root에는 별도
minHeight를 적용하지 않는다. - empty 중앙
후원하기button은 기존과 동일하게 타인 채널에서만 표시하고 같은 후원 요청 경로를 사용한다.
- Figma
- 검증:
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest" - 기대 결과: 구현 전에는 empty item/view model ranking 보존/minHeight 제거 계약 때문에 FAIL하고, 구현 후 PASS한다.
- 실행:
- 검증 기록:
- 2026-06-22: Figma
290:9009design context와 screenshot을 확인해 empty UI 컨텐츠가 16sp gray/500 문구, 14dp 간격,soda/400capsule button으로 구성되며 별도minHeight가 없음을 확인했다. 사용자 후속 요구에 따라 PRD와 계획 문서에rankings존재 여부와 관계없이 채널 후원이 없으면 empty UI를 표시하고, 랭킹 섹션이 있으면 그 아래에 배치하도록 기록했다. - 2026-06-22: RED로
CreatorChannelDonationViewModelTest에 Empty 상태가rankings를 보존해야 한다는 assertion을 추가하고,CreatorChannelDonationFragmentLayoutTest/CreatorChannelDonationActionTest에item_creator_channel_donation_empty.xml, adapter empty item, Fragment rootminimumHeight제거 계약을 추가했다. 구현 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationActionTest"실행 결과item_creator_channel_donation_empty리소스와Empty.rankings부재로:app:compileDebugUnitTestKotlin이 FAIL해 RED를 확인했다. - 2026-06-22:
CreatorChannelDonationViewModel의Empty상태에 랭킹 UI 모델을 보존하고,CreatorChannelDonationFragment는 empty 상태에서donationAdapter.submitEmpty(state.rankings, state.isOwner)를 호출하도록 수정했다.fragment_creator_channel_donation.xml의 empty overlay는 제거하고,item_creator_channel_donation_empty.xml을 추가해RecyclerViewitem으로 empty UI를 표시하도록 했다.CreatorChannelDonationAdapter는 랭킹 item 뒤에 empty item을 추가하고 타인 채널에서만 중앙후원하기button을 표시한다. 수정 후 동일 focused 명령이 PASS했다.
- 2026-06-22: Figma
- 수정:
Phase 7: 통합 검증과 수동 확인
-
Task 7.1: 후원 탭 focused 테스트 실행
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
- 기대 결과:
- 후원 탭 mapper/ViewModel/pagination/layout/action 테스트가 모두 PASS한다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"실행 결과 PASS했다. ktlint unused import 정리 후 동일 명령을 재실행해 PASS를 재확인했다.
- 2026-06-22:
- 실행:
-
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한다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Donation*"최초 실행에서CreatorChannelActivitySourceTest가 empty button을 기존 Fragment layout에서 찾는 오래된 source 계약으로 FAIL했다. Empty UI가 adapter item으로 이동한 현재 구조에 맞춰 테스트가item_creator_channel_donation_empty.xml을 확인하도록 갱신한 뒤 같은 명령이 PASS했다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"실행 결과 PASS했다.
- 2026-06-22:
- 실행:
-
Task 7.3: 리소스/컴파일/스타일 검증
- 실행:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
- 기대 결과:
- resource merge, Kotlin compile, ktlint, whitespace check가 모두 PASS한다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:mergeDebugResources는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS했다. - 2026-06-22:
./gradlew :app:compileDebugKotlin실행 결과 PASS했다. - 2026-06-22:
./gradlew :app:ktlintCheck최초 실행에서CreatorChannelDonationViewModelTest의 unusedassertTrueimport로 FAIL했다. 해당 import를 제거한 뒤 동일 명령을 재실행해 PASS했다. - 2026-06-22:
git diff --check실행 결과 PASS했다.
- 2026-06-22:
- 실행:
-
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/표시 분기는 없다.
- 후원 탭 진입 시
- 검증 기록:
- 2026-06-22: 실제 기기/API 수동 확인은 실행하지 않았다. Figma
290:9009design context/screenshot과 layout/source/test 검증으로 empty UI의 랭킹 하단 배치,minHeight제거, 타인 채널 button 표시 계약을 확인했다.
- 2026-06-22: 실제 기기/API 수동 확인은 실행하지 않았다. Figma
- 확인 항목:
Phase 8: 후원 item 다국어 회귀 수정
Phase 상태: 완료. 2026-08-26 API 메시지 계약 확인, locale 회귀 테스트 추가, Phase 8 Gate 검증을 완료했다.
Phase 결과: 사용자가 입력하지 않은 시스템 기본 후원 문구, CAN 단위, 상대 시간이 한국어·영어·일본어로 표시되고 사용자 작성 메시지는 원문을 유지한다.
선행조건: GET /api/v2/creator-channels/{creatorId}/donations가 사용자 메시지 미입력 시 message: ""를 반환한다는 서버 계약과 확인 가능한 응답 증거가 제공되어야 한다.
Phase 완료 조건: P8-T1, P8-T2, P8-GATE 완료와 검증 기록 누적.
-
Task 8.1: 미입력 후원 메시지 API 계약 확인
Goal 실행
P8-T1: 시스템 기본 문구와 사용자 작성 메시지를 Android가 문구 패턴 추정 없이 구분할 수 있는 응답 계약을 확정한다.-
시작 조건: 서버 담당자가 후원 생성·조회 API의 메시지 미입력 정책을 확인할 수 있다.
-
완료 증거: 미입력 내역의
message: ""응답과 입력 내역의 원문message응답이 계약 문서 또는 비민감 샘플 응답으로 확인된다. -
범위 밖: Android에서 한국어 기본 문구 정규식·문구 목록 추가, 사용자 메시지 자동 번역, 서버 코드 변경.
-
TDD 예외 사유: Android 저장소 밖의 API 응답 의미 계약을 확인하는 외부 의존 Task다.
-
대체 검증 방법: 서버 계약 또는 비민감 샘플 응답에서 미입력/입력 각 1건의
message값을 대조한다. 실제 사용자 정보, token, URL은 기록하지 않는다. -
메시지 미입력 후원 내역이
message: ""로 조회되는지 확인한다. -
사용자 작성 메시지가 번역·기본 문구 치환 없이 원문으로 조회되는지 확인한다.
-
서버가 한국어 기본 문구를 채운 응답은 계약 위반임을 확인하고 Android 보정 없이 서버 계약 수정 대상으로 되돌린다.
-
검증 기록:
- 2026-08-26: 승인된 PRD의 API 요구사항과 Resolved Decisions를 계약 문서로 대조했다. 미입력 메시지는
message: "", 입력 메시지는 원문 반환으로 확정되어 있으며, 서버가 한국어 기본 문구를 채우는 응답은 계약 위반으로 처리하고 Android 보정 로직을 추가하지 않는 경계를 확인했다. 실제 사용자 정보, token, URL은 기록하지 않았다.
- 2026-08-26: 승인된 PRD의 API 요구사항과 Resolved Decisions를 계약 문서로 대조했다. 미입력 메시지는
-
-
Task 8.2: 후원 item locale 표시 회귀 검증
Goal 실행
P8-T2: 기존 Android resource·formatter 경로가 빈 기본 문구, CAN 단위, 상대 시간을 한국어·영어·일본어로 표시하고 사용자 메시지는 보존함을 자동 검증한다.- 시작 조건:
P8-T1완료. - 완료 증거: locale별 mapper/resource 회귀 테스트와 Phase 8 Gate 명령이 PASS하고 실제 결과가 이 Task 아래에 기록된다.
- 범위 밖: 신규 번역 framework, donation 전용 상대 시간 formatter, 사용자 메시지 번역, layout 크기·간격·constraint 검증.
- TDD 예외 사유: 빈
messagefallback, CAN resource, 공통 상대 시간 formatter가 이미 구현되어 있어 기존 동작을 억지로 실패시키지 않는 특성화·회귀 테스트 Task다.
Files:
- Modify:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationMapperTest.kt - Modify:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragmentLayoutTest.kt - Modify:
app/src/test/java/kr/co/vividnext/sodalive/common/RelativeTimeFormatterTest.kt - Confirm only:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/model/CreatorChannelDonationMappers.kt - Confirm only:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelDonationCardBinder.kt - Confirm only:
app/src/main/res/values/strings.xml - Confirm only:
app/src/main/res/values-en/strings.xml - Confirm only:
app/src/main/res/values-ja/strings.xml
Interfaces:
-
Consumes:
CreatorChannelDonationResponse.message,CreatorChannelDonationResponse.can,CreatorChannelDonationResponse.createdAtUtc,AndroidUtcRelativeTimeTextFormatter. -
Produces: locale별
CreatorChannelDonationUiModel.message,createdAtText,creator_channel_donation_can_format표시 계약. -
한국어·영어·일본어 context에서 빈
message가 각 locale의creator_channel_donation_fallback_message로 매핑되는 테스트를 추가한다. -
비어 있지 않은 동일 사용자
message가 세 locale에서 원문 그대로 유지되는 테스트를 추가한다. -
creator_channel_donation_can_format이 한국어%1$s캔, 영어%1$s cans, 일본어%1$sCAN으로 포맷되는 테스트를 추가한다. -
상대 시간이 세 locale의 기존
character_comment_time_*resource 값으로 표시되는 테스트를 추가한다. -
기존 production 경로로 모든 테스트가 통과하면 production 코드를 변경하지 않는다. 실패가 확인되면 실패 원인에 직접 대응하는 최소 Task를 이 Phase에 먼저 추가한다.
-
검증 기록:
- 2026-08-26:
CreatorChannelDonationMapperTest에 빈message == ""의 한국어·영어·일본어 fallback과 동일 사용자 메시지 원문 보존 테스트를 추가했다.CreatorChannelDonationFragmentLayoutTest에는 실제 공통 card binder를 통한1,000캔,1,000 cans,1,000CAN표시 테스트를 추가했고,RelativeTimeFormatterTest에는 실제 2분 경과 값이 locale별character_comment_time_minutesresource로 표시되는 테스트를 추가했다. 기존 동작을 고정하는 특성화 테스트이므로 계획의 TDD 예외를 적용했으며 production 코드는 변경하지 않았다. 세 테스트 클래스 focused 실행은 18개 테스트가 모두 PASS했다.
- 2026-08-26:
- 시작 조건:
Phase 8 Gate
Goal 실행 P8-GATE: 서버의 빈 message 계약과 Android의 locale별 표시·사용자 메시지 보존을 최종 판정한다.
- 시작 조건:
P8-T1,P8-T2완료. - 완료 증거: 아래 명령이 모두 PASS하고 Task 8.1의 API 계약 증거와 함께 검증 기록에 누적된다.
- 범위 밖:
app/src/androidTest,connectedDebugAndroidTest, 기기·에뮬레이터 조작, 스크린샷·시각 QA.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationMapperTest"
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.RelativeTimeFormatterTest"
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"
./gradlew :app:mergeDebugResources
./gradlew :app:compileDebugKotlin
./gradlew :app:ktlintCheck
git diff --check
Expected: 한국어·영어·일본어의 기본 후원 문구, CAN 단위, 상대 시간 테스트와 사용자 메시지 원문 보존 테스트가 PASS하고, production 코드에 한국어 기본 문구 식별 로직이나 신규 번역 dependency가 추가되지 않는다.
Gate 결과: 2026-08-26 완료. 위 7개 명령이 모두 PASS했고 production 코드는 변경하지 않았다.
-
Task 8.3: 후원 item runtime locale 적용 회귀 수정
Goal 실행
P8-T3: 후원 item의 기본 문구와 상대 시간이 ViewModel 생성 시점의 application context가 아니라 실제 item view context의 locale로 표시되도록 수정한다.-
수정 대상:
CreatorChannelDonationUiModels.kt,CreatorChannelDonationMappers.kt,CreatorChannelDonationAdapter.kt, 관련 unit/source test. -
범위 밖: 후원 문구 내용 변경, 신규 locale 추가, 사용자 작성 메시지 번역, visual QA, 기기·에뮬레이터 확인.
-
완료 증거: RED 테스트가 기존 구조에서 실패하고, 최소 production 수정 후 focused 테스트와 Phase 8 Gate가 PASS한다.
-
후원 item adapter가 빈
messagefallback을 item view context로 포맷하도록 테스트를 추가한다. -
후원 item adapter가
createdAtUtc를 item view context로 상대 시간 포맷하도록 테스트를 추가한다. -
사용자 작성
message는 기존처럼 원문을 유지한다. -
production 수정은 raw API 값 보존과 bind 시점 locale 포맷에 필요한 최소 범위로 제한한다.
-
검증 기록:
- 2026-08-26: RED로
CreatorChannelDonationFragmentLayoutTest에 adapter가 item view locale context로 빈messagefallback과 2분 전 상대 시간을 표시해야 한다는 테스트를 추가했고, 기존 구현에서ComparisonFailure로 FAIL하는 것을 확인했다. 원인은CreatorChannelDonationViewModel/mapper 단계에서 application context 기반 문자열을 미리 저장해 화면 locale context가 적용될 기회가 없는 구조였다. GREEN으로 mapper는 APImessage와createdAtUtc원문을 UI model에 보존하고,CreatorChannelDonationAdapter가 bind 시점의root.context로creator_channel_donation_fallback_message와formatUtcRelativeTimeText()를 적용하도록 수정했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*"실행 결과 PASS했다.
- 2026-08-26: RED로
-
Verification Log
- 2026-08-26: Phase 8 Gate로 mapper focused 테스트, 공통 상대 시간 focused 테스트, 후원 탭 전체 테스트, resource merge, Kotlin compile, ktlint,
git diff --check를 순서대로 실행해 모두 PASS했다. 별도로 세 변경 테스트 클래스를--rerun-tasks로 실행해 18개 테스트가 실제 수행되는 것을 확인했다. 명세 준수 재리뷰와 코드 품질 리뷰도 APPROVED였으며, 사용자 요청에 따라 worktree, 기기·에뮬레이터, screenshot, visual QA는 실행하지 않았다. 전체 미완료 체크 감사 결과 Phase 8 누락은 없고, Phase 7의 실제 기기/API 수동 확인 Task 7.4만 기존 미완료 상태로 남아 있다. - 2026-06-22: 후원 랭킹 item 순위 text 하단이 보이지 않는 원인을 재확인했다. rank
TextView는 profile1:1컨테이너 안에서0.63지점부터 아래로 그려져야 하는데, profile 컨테이너와 root item의 child drawing이 bounds 안으로 clipping되어 컨테이너 밖으로 내려오는 하단 영역이 보이지 않는 구조였다. 따라서 앞선 padding 접근은 제거하고, root와 profile 컨테이너에android:clipChildren="false"를 적용해 rank text가 profile 하단 밖으로 overflow되어도 보이도록 수정했다. 순위 text 상단 위치는 사용자 확인 기준대로0.63guideline에 맞췄다. RED로CreatorChannelDonationFragmentLayoutTest가 기존 XML에서 FAIL하는 것을 확인했고, 수정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.*",./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,./gradlew :app:mergeDebugResources,git diff --check가 PASS했다.:app:mergeDebugResources는 sandbox에서 Gradle wrapper lock 파일 접근 권한 문제로 1회 실패했고, 외부 권한으로 동일 명령을 재실행해 PASS를 확인했다. - 2026-06-22: 사용자 추가 확인에 따라 후원 랭킹 item 순위 text 하단 clipping과 위치를 재조정한다. 기존
0.68guideline은 눈으로 봤을 때 Figma보다 낮게 보이므로0.63으로 올리고,@font/pattaya_regular+ shadow가includeFontPadding=false상태에서 하단 여유 없이 그려져 잘릴 수 있어 rank text에 bottom padding을 추가한다. 구현 전 RED로CreatorChannelDonationFragmentLayoutTest가 기존 XML에서 FAIL하는 것을 확인했다. - 2026-06-22: 후원 랭킹 item 순위 표시 위치를 Figma
290:9097기준으로 수정했다.get_design_context와 screenshot에서75dpprofile 기준 rank texttop=51dp를 확인했고, profile 높이의 약68%지점을 기준으로 rank text가 시작되도록 profile 영역을1:1nestedConstraintLayout으로 분리한 뒤 horizontal guideline0.68에 rank top을 연결했다. 기존 bottom constraint +translationY방식은 제거했다. RED로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"가 기존 XML에서 FAIL하는 것을 확인했고, 수정 후 동일 focused 테스트가 PASS했다. 회귀 검증으로./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: 사용자 추가 확인에 따라 후원 랭킹 item 순위 표시 위치를 Figma
290:9097기준으로 재검토했다. Figma에서 순위 text는75dpprofile 기준top=51dp지점에 시작하므로 profile 높이의 약68%지점을 기준으로 overlay되도록 후속 수정한다. 구현 전 RED 테스트로 기존 bottom constraint +translationY방식이 Figma 위치 계약과 다름을 고정한다. - 2026-06-22: 사용자 피드백에 따라 후원 랭킹 섹션과 우측 하단 채널 후원 floating button을 Figma 기준으로 후속 수정했다. Figma
290:9097,290:9093design context/screenshot을 재확인했고, 랭킹 grid는 고정75dp가 아니라 가용 폭에서14dp간격 3개를 제외한 뒤 4등분되도록GridLayoutManager+CreatorChannelDonationRankingItemDecoration으로 수정했다. 랭킹 member profile은 column 폭에 맞춰1:1비율로 측정되도록 바꾸고, 순위 표시는@font/pattaya_regular,28sp, profile 하단 overlay 기준으로 조정했다. 우측 하단 후원 floating button은fragment_creator_channel_donation.xml에서 제거하고activity_creator_channel.xmloverlay로 옮겨66dp크기,14dpend/bottom margin,38dpicon, Activity 우하단 고정 위치로 표시되게 했다.CreatorChannelDonationFragment는 content/owner 상태에 따라 Activity에 floating button visibility를 전달하고, Activity button click은 현재 Donation Fragment의onCreatorChannelDonationFloatingButtonClicked()를 호출한다. - 2026-06-22: 후속 수정 RED로
CreatorChannelDonationFragmentLayoutTest에 responsive ranking grid/item 계약을 추가하고,CreatorChannelActivitySourceTest에 Activity overlay floating button 계약을 추가했다. 기존 구현에서 랭킹 grid 폭/간격/순위 폰트 및 Fragment 내부 floating button 구조 때문에 FAIL하는 것을 확인한 뒤 수정했다. 검증으로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest",./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를 확인했다. 참고로donation.*와*Donation*테스트 필터를 한 Gradle 명령에 합쳐 실행하면SharedPreferenceManager.can전역 상태를 사용하는 테스트들이 겹쳐 기존 ViewModel 테스트가 흔들릴 수 있어, 계획 문서의 명령처럼 분리 실행했다. - 2026-06-22: 사용자 확인 사항에 따라 후속 수정 범위를 추가했다. 후원 랭킹 섹션은 Figma
290:9097기준으로 grid gap14dp, 4열 responsive profile cell, rank overlay28sp/profile 하단 위치를 맞춘다.402dp화면에서는 profile cell이75dp가 되지만 고정값으로 두지 않고 가용 폭에 따라 계산되도록 한다. 우측 하단 채널 후원 floating button은 Figma290:9093및 홈 탭 owner FAB 패턴처럼66dp크기,14dpend/bottom margin,38dpicon, Activity overlay 고정 위치로 옮기고, 후원 탭 content/empty 상태와 owner 여부에 따라 표시만 제어한다. 구현 전 RED 테스트로 layout/source 계약을 먼저 고정한다. - 2026-06-22: Phase 5, 6 코드 리뷰 및 검증을 수행했다. 리뷰에서 empty 중앙
후원하기button 배경색이@color/white라soda/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이 호출되지 않을 수 있는 위험을 확인했다. - 2026-07-08: 사용자 후속 요청에 따라 후원 내역 item의
red_400header 구간에서 닉네임은white, 시간은gray_100으로 표시되도록CreatorChannelDonationAdapterbinding을 수정했다. 그 외 후원 can 구간은 RecyclerView 재사용 시에도 기존 색상인 닉네임black, 시간gray_700을 유지하도록else에서 명시적으로 되돌린다. RED로CreatorChannelDonationFragmentLayoutTest에 adapter 색상 계약을 추가해 기존 구현에서 FAIL하는 것을 확인했고, 수정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest"가 PASS했다. - 2026-07-08: 사용자 후속 요청에 따라 크리에이터 채널 홈 탭 후원 섹션도 동일하게
red_400header 구간에서 닉네임white, 시간gray_100을 적용했다. 그 외 후원 can 구간은 기존 색상인 닉네임black, 시간gray_700을 유지한다. RED로CreatorChannelActivitySourceTest에 홈 후원 섹션 adapter 색상 계약을 추가해 기존 구현에서 FAIL하는 것을 확인했고, 수정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"가 PASS했다. - 2026-07-08: 후원 탭 item과 홈 탭 후원 섹션 색상 변경 통합 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,git diff --check가 PASS했다../gradlew :app:ktlintCheck는 이번 변경 파일이 아닌HomeMainFragmentLoginGuardSourceTest.kt의 기존 line length/argument wrapping 위반으로 실패해 별도 관찰사항으로 둔다. - 2026-07-08: 후원 탭 item과 홈 탭 후원 섹션의 카드 내부 UI/색상 정책 중복을 줄이기 위해 추천 방향대로 공통 후원 카드 바인더를 도입하는 리팩토링을 진행한다. 섹션/adapter 전체는 스크롤 방향과 컨테이너 구조가 달라 유지하고, 카드 내부 profile/nickname/time/can/message/header/text color binding만 공통화한다. XML 단일화는 홈 width/message maxLines/icon 차이로 인한 UI 회귀 위험이 있어 이번 리팩토링 범위에서 제외한다.
- 2026-07-08: 공통 후원 카드 바인더 리팩토링을 완료했다.
CreatorChannelDonationCardBinder를 추가해 header 배경, profile, nickname, time, can, message,red_400/기본 텍스트 색상 정책을 한 곳에서 처리하고, 홈 탭 후원 섹션과 후원 탭 item adapter는 각자 layout/scroll/icon/message line 정책을 유지한 채 해당 바인더를 호출하도록 변경했다. RED로CreatorChannelActivitySourceTest,CreatorChannelDonationFragmentLayoutTest에 공통 바인더 계약을 추가해 기존 구현에서 FAIL하는 것을 확인했고, 수정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.donation.CreatorChannelDonationFragmentLayoutTest",./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했다. - 2026-08-26: 후원 item 문자열 흐름과 기존 문서를 대조했다. 빈
message는creator_channel_donation_fallback_message, CAN 단위는creator_channel_donation_can_format,createdAtUtc는AndroidUtcRelativeTimeTextFormatter를 사용하고, 비어 있지 않은message는 API 원문을 표시한다. 인터뷰에서 시스템 기본 문구와 CAN 단위·상대 시간을 한국어·영어·일본어로 처리하고 사용자 메시지는 원문 유지하며, 미입력message는 서버가 빈 문자열로 반환하는 계약을 확정했다. 이번 실행은 PRD와 계획/TASK 문서만 갱신했으며 코드 변경·Gradle 빌드·기능 테스트는 수행하지 않았다../gradlew tasks --all은 sandbox의 Gradle wrapper lock 접근 제한으로 1회 실패한 뒤 승인된 권한으로 재실행해BUILD SUCCESSFUL을 확인했다.