119 KiB
크리에이터 채널 라이브 탭 구현 계획/TASK
For agentic workers: 각 단계는 체크박스(
- [ ])로 추적하고, 완료 즉시- [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
Goal: GET /api/v2/creator-channels/{creatorId}/live 응답을 기반으로 크리에이터 채널의 라이브 탭에 현재 라이브, 라이브 다시듣기 목록, 정렬, pagination, 본인 채널 전용 하단 라이브 시작하기 CTA를 표시한다.
Architecture: 기존 CreatorChannelActivity의 ViewPager2/CreatorChannelPagerAdapter 구조를 유지하고, CreatorChannelTab.Live의 placeholder를 신규 CreatorChannelLiveFragment로 교체한다. 라이브 탭 전용 Fragment/ViewModel/mapper/UI model/adapter/popup은 kr.co.vividnext.sodalive.v2.creator.channel.live 하위에 둔다. API/Repository는 홈 탭과 라이브 탭이 함께 쓰는 채널 공통 계층으로 보고 기존 CreatorChannelHomeApi/CreatorChannelHomeRepository를 CreatorChannelApi/CreatorChannelRepository로 rename한다. 홈 탭에서 이미 쓰는 CreatorChannelLiveResponse, CreatorChannelAudioContentResponse, 본인 판정, 라이브 시작 진입 흐름은 가능한 한 재사용한다.
Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
전제와 성공 기준
- PRD:
docs/20260617_크리에이터_채널_라이브_탭/prd.md - 기존 채널 컨테이너:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt - 기존 탭 adapter:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt - 기존 채널 API/DTO/Repository:
- rename 대상:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeApi.kt->app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt - rename 대상:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeRepository.kt->app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt - 유지/확장:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.kt
- rename 대상:
- 별도
CreatorChannelLiveApi,CreatorChannelLiveRepository는 생성하지 않는다. - Figma:
- 전체:
290:8945 - 전체 empty:
290:8959 - Sort-bar:
290:8949 - 현재 진행 중인 라이브:
290:8950 - 라이브 다시듣기 item:
290:8954,290:8956 - 정렬 컨텍스트 메뉴:
290:9041 - 본인 채널 하단 CTA:
665:19359,665:19371
- 전체:
- 첫 페이지
page는0이다. - 다음 페이지는 마지막 성공 응답의
page + 1로 요청한다. ContentSort기본값은LATEST이다.isOwned == true와isRented == true가 동시에 내려오면소장중을 우선 표시한다.seriesName은 라이브 다시듣기 item에 표시하지 않는다.isFirstContent,isOriginalSeries는 기존 오디오 item 정책과 동일하게 매핑한다.currentLive == null이고liveReplayContents.isEmpty()인 전체 empty 상태에서는 Sort-bar, 현재 라이브 카드, 라이브 다시듣기 리스트를 제거하고 중앙에크리에이터가 라이브를 준비 중입니다.\n기대해 주세요!문구를 표시한다.- 전체 empty 상태가 본인 채널에 표시되는 경우에도 empty 문구는 동일하게 표시하고, 하단
라이브 시작하기CTA는 본인 채널 정책에 따라 계속 표시한다. - 같은
creatorId로loadLive()가 재호출되면 Fragment/View 재생성 또는 탭 재바인딩으로 보고 기존 ViewModel 상태를 유지한다. 명시적 새로고침은 후속 필요 시 별도 refresh API로 분리한다. - 최초 조회 실패로
Error상태인 경우retryLive()를 통해 현재creatorId의 첫 페이지를 다시 요청한다. - 전체 error 상태에서는 안내 문구 아래 retry 버튼을 표시하고, retry 버튼은
CreatorChannelLiveViewModel.retryLive()를 호출한다. - drawable 리소스는 기존 파일을 재사용한다.
ic_new_sortic_new_shield_smallic_new_player_playic_new_create_live
- 구현 완료 후 최소 다음 명령을 실행한다.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheck
Figma 참조 필요 Phase
- Phase 1: 부분 참조
- 기존 코드 경계와 리소스 존재 여부 확인이 중심이며, Figma는 PRD 기준만 확인한다.
- Phase 2: Figma 참조 불필요
- API/DTO/Repository/ViewModel 상태 모델은 서버 계약과 기존 코드 패턴을 따른다.
- Phase 3: 부분 참조
- mapper와 presenter 정책은 PRD와 Figma item variant의 상태 표시를 함께 확인한다.
- Phase 4: 필수 참조
fragment_creator_channel_live.xml, sort-bar, current live card, replay item layout, 전체 empty 상태는 Figma290:8949,290:8950,290:8954,290:8956,290:8959를 기준으로 구현한다.
- Phase 5: 필수 참조
- 정렬 컨텍스트 메뉴는 Figma
290:9041기준으로 구현한다.
- 정렬 컨텍스트 메뉴는 Figma
- Phase 6: 필수 참조
- 본인 채널 하단 CTA는 Figma
665:19359,665:19371기준으로 구현한다.
- 본인 채널 하단 CTA는 Figma
- Phase 7: 부분 참조
- 탭 연결, 화면 이동, pagination 동작은 기존 코드 패턴 중심으로 검증한다.
- Phase 8: 필수 참조
- 최종 수동 화면 검증은 PRD의 모든 Figma 노드와 실제 화면을 대조한다.
파일 구조
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktCreatorChannelTab.Live를 신규CreatorChannelLiveFragment로 연결한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt- 라이브 탭 UI, adapter, sort menu, pagination, CTA click 연결을 담당한다.
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt- 라이브 탭 API 호출, 정렬 변경, pagination 상태, loading/error/content 상태를 관리한다.
- rename:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeApi.kt->app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt- 홈 API와 라이브 API endpoint를 함께 가진 채널 공통 API로 변경한다.
- rename:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeRepository.kt->app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt- 홈 API, 라이브 API, 팔로우, 대화, 후원, 신고 등 채널 공통 동작을 제공한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.ktCreatorChannelAudioContentResponse에 라이브 탭 전용 필드isAdult,isOwned,isRented를 서버 계약에 맞춰 추가한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/common/data/ContentSort.kt- v2 API 공용 정렬 enum으로
ContentSort를 둔다.
- v2 API 공용 정렬 enum으로
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/data/CreatorChannelLiveTabResponse.ktCreatorChannelLiveTabResponse를 크리에이터 채널 라이브 탭 응답 전용 모델로 둔다.
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.kt- sort option, replay item status, pagination 상태, CTA 노출 여부를 순수 UI model로 정의한다.
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveMappers.kt- DTO를 UI model로 변환하고 소장/대여/무료/가격/포인트/19금 상태를 결정한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveReplayAdapter.kt- 라이브 다시듣기 목록 RecyclerView adapter를 담당한다.
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveSortPopup.kt- Sort-bar anchor 기준 컨텍스트 메뉴 표시를 담당한다.
- 생성:
app/src/main/res/layout/fragment_creator_channel_live.xml- sort-bar, current live card container, replay RecyclerView, owner CTA를 포함한다.
- 생성:
app/src/main/res/layout/item_creator_channel_live_replay.xml- 라이브 다시듣기 item layout이다.
- 생성 후보:
app/src/main/res/layout/view_creator_channel_live_sort_menu.xml- 정렬 컨텍스트 메뉴를 XML로 분리할 때만 추가한다.
- 수정:
app/src/main/res/values/strings.xml- 정렬 label,
소장중,대여중,라이브 시작하기, empty/error 문구를 추가한다.
- 정렬 label,
- 수정:
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- 신규 ViewModel/API/Repository binding이 필요하면 추가한다.
- 테스트 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
Phase 1: 기존 구조 확인과 작업 경계 고정
-
Task 1.1: 크리에이터 채널 탭 구조와 본인 판정 경로 확인
- 확인:
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/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeMappers.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
- 작업:
CreatorChannelTab.Live가 현재 placeholder로 연결되는 지점을 확인한다.- 본인 여부(
isOwner)가 Activity와 탭 Fragment로 전달되는 기존 경로를 확인한다. - 라이브 시작 진입이 기존
CreatorChannelActivity의onOwnerFabLiveClicked()또는LiveRoomCreateActivity경로를 재사용할 수 있는지 확인한다.
- 검증:
- 라이브 탭 구현이 홈 탭 section adapter를 변경하지 않고 독립 Fragment로 추가 가능한지 기록한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelPagerAdapter는 현재CreatorChannelTab.Home만CreatorChannelHomeFragment.newInstance(creatorId)로 연결하고,CreatorChannelTab.Live를 포함한 나머지 탭은CreatorChannelPlaceholderFragment.newInstance(tab)로 처리함을 확인했다. 따라서 Phase 7에서 Live 탭만 신규 Fragment로 교체해도 홈 탭 section adapter 변경 없이 독립 추가 가능하다. - 2026-06-17: 본인 여부는
CreatorChannelHomeMappers.toUiContent()에서creator.creatorId == currentMemberId로 계산되어CreatorChannelHeaderUiModel.isOwner와 donation section에 전달되고,CreatorChannelActivity는 이 값을 owner FAB/상단 액션/DM/후원 분기 조건으로 사용함을 확인했다. - 2026-06-17: 라이브 시작 진입은
CreatorChannelActivity.onOwnerFabLiveClicked()가liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))를 호출하는 기존 경로를 사용하며, 생성 결과는homeActionDelegate?.refreshHome()후Constants.EXTRA_ROOM_ID,Constants.EXTRA_ROOM_CHANNEL_NAME에 따라CreatorChannelLiveCoordinator.enterLiveRoom()또는 생성 완료 toast로 처리됨을 확인했다.
- 2026-06-17:
- 확인:
-
Task 1.2: 기존 오디오 item 정책과 drawable/string 리소스 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeAudioContentCardView.ktapp/src/main/res/layout/item_creator_channel_home_audio_content.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.ktapp/src/main/res/drawable-mdpi/ic_new_sort.pngapp/src/main/res/drawable-mdpi/ic_new_shield_small.pngapp/src/main/res/drawable-mdpi/ic_new_player_play.pngapp/src/main/res/drawable-mdpi/ic_new_create_live.png
- 작업:
isFirstContent,isOriginalSeries, point/free/original tag의 기존 표시 정책을 정리한다.- 라이브 다시듣기 item에서
seriesName은 표시하지 않는다는 PRD 결정을 재확인한다.
- 검증:
- 위 4개 drawable 리소스가 존재함을 확인한다.
- 검증 기록:
- 2026-06-17: 기존 홈 오디오 item은
CreatorChannelHomeAudioContentCardView.bind()에서isOriginalSeries == true이면 original tag,isPointAvailable이면 point tag,isFirstContent이면 first tag,price <= 0이면 free tag를 표시한다. secondary text는 기존 홈 카드에서duration과seriesName을 조합하지만, 라이브 다시듣기 item에서는 PRD/계획 기준에 따라seriesName을 표시하지 않는 것으로 재확인했다. - 2026-06-17:
AudioContentTag의 top tag 순서는Original,First, bottom tag 순서는Point,Free로 정의되어 있음을 확인했다. - 2026-06-17:
test -f로app/src/main/res/drawable-mdpi/ic_new_sort.png,ic_new_shield_small.png,ic_new_player_play.png,ic_new_create_live.png가 모두 존재함을 확인했다.
- 2026-06-17: 기존 홈 오디오 item은
- 확인:
-
Task 1.3: 채널 공통 API/Repository rename
- rename:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeApi.kt->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/CreatorChannelHomeRepository.kt->app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModelTest.ktapp/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt- 기타
CreatorChannelHomeApi,CreatorChannelHomeRepositoryimport 참조 파일
- 작업:
- class/interface 이름을
CreatorChannelApi,CreatorChannelRepository로 변경한다. - 기존 홈 탭 동작은 변경하지 않는다.
- 별도
CreatorChannelLiveApi,CreatorChannelLiveRepository는 만들지 않는다.
- class/interface 이름을
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"./gradlew :app:compileDebugKotlin
- 기대 결과:
- rename 후 기존 홈 탭 ViewModel 테스트와 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-17: rename-only 변경으로
CreatorChannelHomeApi.kt/CreatorChannelHomeRepository.kt를 각각CreatorChannelApi.kt/CreatorChannelRepository.kt로 변경하고 interface/class/import/DI/test 참조를 갱신했다. 홈 endpointGET /api/v2/creator-channels/{creatorId}/home와 기존 repository method 동작은 변경하지 않았다. - 2026-06-17: 신규 동작 추가가 없는 rename-only 작업이므로 RED 테스트 신규 작성 대상에서 제외했다. 기존 회귀 검증은 아래 명령 실행 결과로 누적한다.
- 2026-06-17: 최초 병렬 검증 실행 중
:app:kspDebugKotlin이 KSP incremental cache(app/build/kspCaches/debug) 경합/손상으로 실패했다. 생성물 캐시만 삭제한 뒤 순차 재실행했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin순차 재실행 결과BUILD SUCCESSFUL로 통과했다. 기존 deprecation/annotation 경고는 출력되었으나 rename 변경과 무관하다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"순차 재실행 결과BUILD SUCCESSFUL로 통과했다.
- 2026-06-17: rename-only 변경으로
- rename:
Phase 2: API/DTO/Repository/ViewModel 계약 추가
-
Task 2.1: 라이브 탭 DTO와
ContentSort추가- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.kt
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/common/data/ContentSort.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt
- 작업:
CreatorChannelLiveTabResponse를 라이브 탭data/CreatorChannelLiveTabResponse.kt에@Keep,@SerializedName기반 data class로 추가한다.ContentSortenum에LATEST,POPULAR,OWNED,PRICE_HIGH,PRICE_LOW를 추가하고v2.common.data패키지에 둔다.- 기존
CreatorChannelAudioContentResponse에isAdult,isOwned,isRented필드를 추가할 때 홈 탭 API 하위 호환성이 깨지지 않는지 확인한다. - 하위 호환이 필요하면 라이브 탭 전용 DTO 분리 또는 nullable/default 정책을 테스트로 고정한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- DTO 추가 후 컴파일된다.
- 검증 기록:
- 2026-06-17:
CreatorChannelHomeModels.kt에CreatorChannelLiveTabResponse와ContentSort(LATEST, POPULAR, OWNED, PRICE_HIGH, PRICE_LOW)를 추가했다. 기존 홈 탭 테스트 fixture의 positional constructor 호환을 유지하기 위해CreatorChannelAudioContentResponse의 라이브 탭 전용isAdult,isOwned,isRented필드는 기본값이 있는 trailing field로 추가했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL로 통과했다.
- 2026-06-17:
- 수정:
-
Task 2.1.1: 라이브 탭 응답 모델 파일 경계 분리
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/common/data/ContentSort.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelHomeModels.ktdocs/20260617_크리에이터_채널_라이브_탭/prd.md
- 작업:
ContentSort는 v2 API 공용 정렬 enum이므로v2.common.data패키지로 분리한다.CreatorChannelLiveTabResponse는 크리에이터 채널 라이브 탭 응답 계약이므로v2.creator.channel.live.data패키지로 분리한다.- 홈 탭과 라이브 탭이 함께 쓰는
CreatorChannelLiveResponse,CreatorChannelAudioContentResponse는 기존 공용 DTO로 유지한다. - 별도
CreatorChannelLiveApi/CreatorChannelLiveRepository는 만들지 않고 기존 공통CreatorChannelApi/CreatorChannelRepository를 유지한다.
- 검증 명령:
rg -n "CreatorChannelLiveTabResponse|enum class ContentSort" app/src/main/java/kr/co/vividnext/sodalive/v2./gradlew :app:compileDebugKotlin
- 기대 결과:
ContentSort는v2/common/data/ContentSort.kt에 정의된다.CreatorChannelLiveTabResponse는v2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt에 정의된다.- 모델 파일 분리 후 Kotlin 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-17: 사용자 리뷰에 따라
CreatorChannelLiveTabResponse와ContentSort를CreatorChannelHomeModels.kt에서 라이브 탭 응답 전용 파일로 이동했다. 같은data패키지의 타입 이동이라 API/Repository/ViewModel의 패키지 import 계약은 유지된다. - 2026-06-17:
rg -n "CreatorChannelLiveTabResponse|enum class ContentSort" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data실행 결과, 타입 정의가 라이브 탭 응답 전용 파일에만 있고CreatorChannelApi.kt의 응답 타입 참조만 남아 있음을 확인했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin최초 실행은 sandbox가~/.gradlelock 파일 생성을 막아 실패했다. 동일 명령을 권한 승인 후 재실행해BUILD SUCCESSFUL in 24s로 통과했다. - 2026-06-17: 사용자 추가 지시에 따라
ContentSort는v2.common.data.ContentSort,CreatorChannelLiveTabResponse는v2.creator.channel.live.data.CreatorChannelLiveTabResponse로 이동하도록 계획과 PRD의 파일 경계를 갱신했다. - 2026-06-17:
rg -n "creator\\.channel\\.data\\.ContentSort|creator\\.channel\\.data\\.CreatorChannelLiveTabResponse|ContentSort|CreatorChannelLiveTabResponse" app/src/main/java app/src/test/java로 이전 패키지 import가 남지 않았고,ContentSort는v2.common.data,CreatorChannelLiveTabResponse는v2.creator.channel.live.data참조로 갱신됐음을 확인했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin최초 실행은 sandbox가~/.gradlelock 파일 생성을 막아 실패했다. 동일 명령을 권한 승인 후 재실행해BUILD SUCCESSFUL in 12s로 통과했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL in 32s로 통과했다.
- 2026-06-17: 사용자 리뷰에 따라
- 생성:
-
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}/live")endpoint를 추가한다.- query parameter
page,size,sort를 전달한다. sort는ContentSort.name또는 Retrofit enum 변환의 기존 정책을 확인해 서버 값이LATEST등 대문자로 나가도록 한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- API/Repository가 기존 Koin graph와 충돌 없이 컴파일된다.
- 검증 기록:
- 2026-06-17:
CreatorChannelApi에GET /api/v2/creator-channels/{creatorId}/liveendpoint를 추가하고page,size,sortquery와Authorizationheader를 전달하도록 했다.CreatorChannelRepository.getLive()는 별도 live repository를 만들지 않고 기존 공통 repository에서 API를 얇게 위임한다. - 2026-06-17:
sort는ContentSortenum query로 전달해LATEST등 enum name이 Retrofit query 값으로 사용되도록 했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL로 통과했다.
- 2026-06-17:
- 수정:
-
Task 2.3: ViewModel RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt
- 작업:
- 최초 로딩이
page=0,sort=LATEST로 호출되는지 검증한다. hasNext == true일 때page + 1로 다음 페이지를 요청하는지 검증한다.- loading 중 중복 load-more 요청이 막히는지 검증한다.
- 정렬 변경 시 목록과 page가 초기화되는지 검증한다.
- 선택 중인 정렬을 다시 선택하면 API를 재호출하지 않는지 검증한다.
- 최초 로딩이
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"
- 기대 결과:
- ViewModel 미구현 상태에서 RED 실패한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelLiveViewModelTest와CreatorChannelLivePaginationTest를 추가했다. 최초 로딩page=0/sort=LATEST,hasNext기반page + 1pagination, loading 중 중복 load-more 차단, 정렬 변경 시 page/list 초기화, 같은 정렬 재선택 no-op, 실패/empty 상태를 검증한다. - 2026-06-17: production 구현 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"실행 결과ContentSort,CreatorChannelLiveTabResponse,CreatorChannelLiveViewModel,CreatorChannelRepository.getLive미구현으로:app:compileDebugUnitTestKotlin FAILED가 발생해 RED를 확인했다. - 2026-06-17:
최초 로드 전 정렬 변경은 기본 최신순 최초 로드 계약을 바꾸지 않는다테스트를 추가하고 production 보정 전 실행한 결과,changeSort(POPULAR)가 최초 로드 기본값을 오염시켜 테스트가 실패함을 확인한 뒤 guard를 구현했다. - 2026-06-17: 리뷰 코멘트에 따라 같은
creatorId로loadLive()가 재호출되면 기존 목록/page/load-more 결과를 유지해야 한다는 테스트를 추가했다. production 보정 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"실행 결과 해당 테스트가 실패함을 확인했다.
- 2026-06-17:
- 생성:
-
Task 2.4:
CreatorChannelLiveViewModel구현- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
- initial loading, content, empty, error, pagination loading, pagination error 상태를 정의한다.
page=0,size기본값,sort=LATEST초기값을 적용한다.- 정렬 변경과 pagination 요청 중복 방지 로직을 구현한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"
- 기대 결과:
- ViewModel/pagination 테스트가 PASS한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelLiveViewModel을 추가하고Loading,Empty,Error,Content상태를 정의했다. 초기 로드는page=0,size=20,sort=LATEST로 요청하고, pagination은 마지막 성공 응답의page + 1로 append하며isLoadingMore로 중복 요청을 차단한다. - 2026-06-17: 정렬 변경은 기존 목록/page를 초기화해 첫 페이지를 다시 요청하고, 같은 정렬 재선택은 API를 재호출하지 않는다. 최초 로드 전 정렬 변경은 기본
LATEST최초 로드 계약을 바꾸지 않도록 no-op 처리했다. - 2026-06-17: 리뷰 게이트에서 정렬 변경 중 이전 첫 페이지/load-more 응답이 최신 상태를 덮어쓸 수 있다는 지적이 있어 RED 테스트를 추가했다. production 보정 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과이전 첫 페이지 응답은 이후 정렬 변경 결과를 덮어쓰지 않는다,이전 load-more 응답은 이후 정렬 변경 목록에 append되지 않는다2개 테스트가 실패함을 확인했다. - 2026-06-17:
requestGenerationguard를 추가해 오래된 첫 페이지/load-more 성공 또는 실패 응답을 무시하도록 보정했다. 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
AppDI에CreatorChannelLiveViewModel(get())binding을 추가했다. 기존CreatorChannelApi/CreatorChannelRepositorybinding은 재사용했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17: 같은
creatorId와 기존 상태가 있는loadLive()재호출은 no-op 하도록 보정해 Fragment/View 재생성 또는 탭 재바인딩 시 기존 정렬/page/list 상태를 유지한다. 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17: 리뷰 코멘트의 empty 정책을 반영해 PRD/계획 문서에 Figma
290:8959기준 전체 empty 상태를 기록했다.CreatorChannelLiveUiState.Empty는 Sort-bar/list를 제거하고 중앙 문구를 표시하기 위한 상태로 유지하며, 본인 채널 하단 CTA는 Phase 6의 본인 채널 정책대로 유지한다. - 2026-06-17: 리뷰 코멘트에 따라
Error상태에서 retry 버튼이 현재creatorId첫 페이지를 재요청해야 한다는 테스트를 추가했다. production 보정 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"실행 결과retryLive미구현으로:app:compileDebugUnitTestKotlin FAILED가 발생해 RED를 확인했다. - 2026-06-17: Android 샘플과 기존 retry 의미를 따라
loadLive()의 같은creatorId상태 보존 guard는 유지하고, 명시적retryLive()API를 추가했다. Phase 4 retry 버튼은retryLive()를 호출하면 된다. 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"및./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL로 통과했다.
- 2026-06-17:
- 생성:
Phase 3: UI model과 상태 표시 mapper 구현
-
Task 3.1: 라이브 다시듣기 표시 정책 RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.kt
- 작업:
isOwned && isRented동시 true면소장중상태가 선택되는지 검증한다.price == 0이면 무료 tag와 play CTA 상태가 선택되는지 검증한다.isAdult == true이면ic_new_shield_small표시 상태가 선택되는지 검증한다.isPointAvailable == true이면 point tag 표시 상태가 선택되는지 검증한다.seriesName은 secondary text에 포함되지 않는지 검증한다.isFirstContent,isOriginalSeries는 기존 오디오 item 정책과 동일한 tag 상태로 매핑되는지 검증한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"
- 기대 결과:
- mapper 미구현 상태에서 RED 실패한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelLiveMapperTest를 추가해isOwned && isRented동시 true 시소장중우선, 무료 콘텐츠의 free tag/play CTA, 19금 shield, point tag,seriesName미표시,isFirstContent/isOriginalSeriestag 정책,ContentSortlabel resource 매핑을 검증하도록 했다. - 2026-06-17: production mapper 구현 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"실행 결과CreatorChannelLiveReplayStatus,toReplayUiModel,toLabelResId미구현으로:app:compileDebugUnitTestKotlin FAILED가 발생해 RED를 확인했다.
- 2026-06-17:
- 생성:
-
Task 3.2: UI model/mapper 구현
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveMappers.kt
- 작업:
- sort label resource, replay item CTA/status, tag 상태, current live 상태를 UI model로 분리한다.
- 가격/포인트/소장중/대여중/play button 상태 우선순위를 순수 함수로 고정한다.
ContentSort와 문자열 리소스 매핑을 구현한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"
- 기대 결과:
- mapper 테스트가 PASS한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelLiveReplayUiModel,CreatorChannelLiveReplayStatus,CreatorChannelLiveSortOptionUiModel을 추가하고CreatorChannelAudioContentResponse.toReplayUiModel(),ContentSort.toLabelResId(),ContentSort.toSortOptionUiModel()을 구현했다. - 2026-06-17: 가격/상태 우선순위는
소장중>대여중> 무료 play > 가격 표시로 고정했다. tag는 기존 오디오 정책과 동일하게isOriginalSeries == true이면Original,isFirstContent이면First,isPointAvailable이면Point,price == 0이면Free를 매핑한다.secondaryText는duration만 사용해seriesName을 표시하지 않는다. - 2026-06-17:
ContentSortlabel은LATEST/POPULAR/PRICE_HIGH/PRICE_LOW에 기존 리소스screen_audio_content_sort_newest,screen_audio_content_sort_popularity,screen_audio_content_sort_price_high,screen_audio_content_sort_price_low를 재사용하고, 기존 리소스가 없는OWNED의소장순label은creator_channel_live_sort_owned문자열로 추가했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:ktlintCheck실행 결과BUILD SUCCESSFUL로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 Phase 3 변경과 무관하다. - 2026-06-17: 리뷰 게이트에서
LATEST가최신 콘텐츠,OWNED가소장중리소스에 매핑되는 문제가 지적되어LATEST는screen_audio_content_sort_newest,OWNED는 신규creator_channel_live_sort_owned로 보정했다. - 2026-06-17: label 보정 후
./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS. 병렬 실행 중:app:dataBindingGenBaseClassesDebug가 Gradle 생성물 경합으로R-def.txt누락 실패했으나 동일 compile 명령 순차 재실행에서 PASS했다.
- 2026-06-17:
- 생성 후보:
Phase 4: 라이브 탭 Fragment와 목록 UI 구현
-
Task 4.1: Fragment layout과 replay item layout 추가
- 생성:
app/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/main/res/layout/item_creator_channel_live_replay.xml
- 수정:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xml
- 작업:
- Figma 기준 sort-bar, current live card, replay list, owner CTA 영역을 배치한다.
- replay item은 88dp 썸네일, title 최대 2줄, duration 1줄, 우측 price/play/status 영역을 포함한다.
ic_new_shield_small,ic_new_player_play,ic_new_sort,ic_new_create_live를 layout 또는 binding에서 사용 가능하게 준비한다.
- 검증 명령:
./gradlew :app:mergeDebugResources
- 기대 결과:
- 신규 layout/string/drawable 참조가 resource merge에 성공한다.
- 검증 기록:
- 2026-06-17: Figma
290:8949,290:8950,290:8954,290:8959기준으로fragment_creator_channel_live.xml과item_creator_channel_live_replay.xml을 추가했다. sort-bar는 52dp, current live card는 78dp pill, replay item은 88dp 썸네일/최대 2줄 title/1줄 duration/우측 action 영역으로 구성했다. - 2026-06-17:
ic_new_sort,ic_new_shield_small,ic_new_player_play,ic_new_create_live는 기존 drawable을 재사용하고, live current/price/adult/retry 배경 drawable만 Phase 4 UI 표현에 필요한 최소 리소스로 추가했다. - 2026-06-17:
creator_channel_live_total_label, empty/error/retry/owner CTA 문자열을values,values-en,values-ja에 추가했다. - 2026-06-17:
./gradlew :app:mergeDebugResources실행 결과BUILD SUCCESSFUL로 통과했다.
- 2026-06-17: Figma
- 생성:
-
Task 4.2: Adapter와 Fragment 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveReplayAdapter.kt
- 작업:
- ViewModel 상태를 관찰해 sort-bar, current live card, replay list, loading/empty/error를 갱신한다.
- error 상태에서는 Sort-bar, current live card, replay list를 숨기고 중앙 안내 문구와 그 아래 retry 버튼을 표시한다.
- retry 버튼 클릭은
CreatorChannelLiveViewModel.retryLive()를 호출해 현재creatorId첫 페이지를 재요청한다. - replay item click은 기존 오디오 컨텐츠 상세/재생 진입 정책을 확인해 연결한다.
- current live card click은 기존 홈 탭 라이브 진입 정책을 재사용한다.
- RecyclerView scroll listener로 하단 접근 시 ViewModel load-more를 호출한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- Fragment/adapter가 컴파일된다.
- 검증 기록:
- 2026-06-17:
CreatorChannelLiveFragment를 추가해CreatorChannelLiveViewModel.liveStateLiveData의Loading,Empty,Error,Content상태에 따라 sort-bar/current live/replay list/empty/error/retry visibility를 갱신하도록 했다. - 2026-06-17: error 상태 retry 버튼은
CreatorChannelLiveViewModel.retryLive()를 호출한다. RecyclerView 하단 접근 시loadMore()를 호출하며 중복/hasNext guard는 ViewModel 정책을 따른다. - 2026-06-17:
CreatorChannelLiveReplayAdapter를 추가해 mapper의CreatorChannelLiveReplayUiModel을 19금 shield, original/first/point/free tag, play/소장중/대여중/가격 상태로 바인딩한다. - 2026-06-17: replay item click은
CreatorChannelLiveFragment.Host.onCreatorChannelLiveReplayClicked(audioContentId)로 위임하고,CreatorChannelActivity에서 기존AudioContentDetailActivity진입 경로를 재사용하도록 연결했다. current live card click은 기존 홈 탭과 동일하게onCreatorChannelCurrentLiveClicked(live)를 재사용한다. - 2026-06-17: Phase 5 범위인 sort popup/정렬 변경 동작은 구현하지 않고, Phase 4에서는 sort-bar 표시와 icon 준비까지만 유지했다.
- 2026-06-17:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL로 통과했다. 병렬 Gradle 실행 중 Kotlin incremental cache/daemon 경합 로그가 출력되었으나, fallback/최종 실행은 성공했다.
- 2026-06-17:
- 생성:
-
Task 4.3: Fragment layout 테스트 추가
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
fragment_creator_channel_live.xml에 sort-bar, RecyclerView, owner CTA가 존재하는지 검증한다.item_creator_channel_live_replay.xml에 19금 badge, point/free tag container, price/play/status 영역이 존재하는지 검증한다.ic_new_*리소스 참조가 layout 또는 코드 상수로 연결되는지 검증한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"
- 기대 결과:
- layout 테스트가 PASS한다.
- 검증 기록:
- 2026-06-17: production layout/code 추가 전
CreatorChannelLiveFragmentLayoutTest를 먼저 작성했다. RED 실행 결과fragment_creator_channel_live,item_creator_channel_live_replay및 필수 id 미존재로:app:compileDebugUnitTestKotlin FAILED가 발생해 실패를 확인했다. - 2026-06-17: layout/source 구현 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL로 통과해 Phase 4 layout 테스트와 기존 live ViewModel/mapper/pagination 테스트 회귀를 함께 확인했다.
- 2026-06-17: production layout/code 추가 전
- 생성:
Phase 5: 정렬 컨텍스트 메뉴 구현
-
Task 5.1: Sort popup 구현
- 생성 후보:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveSortPopup.ktapp/src/main/res/layout/view_creator_channel_live_sort_menu.xml
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt
- 작업:
- Sort-bar 정렬 영역 아래에 Figma
290:9041형태의 컨텍스트 메뉴를 표시한다. - 현재 선택 항목은 focused 배경으로 표시한다.
- 메뉴 외부 터치, 같은 정렬 재선택, 새 정렬 선택 시 메뉴를 닫는다.
- 새 정렬 선택 시 ViewModel에 정렬 변경을 전달한다.
- Sort-bar 정렬 영역 아래에 Figma
- 검증:
- 작은 화면에서 메뉴가 화면 밖으로 벗어나지 않도록 위치 보정 기준을 코드에 남긴다.
- 검증 기록:
- 2026-06-17: Figma
290:9041기준으로view_creator_channel_live_sort_menu.xml,bg_creator_channel_live_sort_popup.xml,bg_creator_channel_live_sort_selected.xml을 추가했다. Popup 배경은gray_900fill,gray_7001dp stroke,radius_14로 구성하고, 선택 row는gray_800배경으로 표시한다. - 2026-06-17:
CreatorChannelLiveSortPopup을 추가해ContentSort.entries와 기존toSortOptionUiModel(selectedSort)/toLabelResId()매핑을 재사용하도록 했다.PopupWindow는isOutsideTouchable = true,isFocusable = true로 외부 터치 dismiss를 지원하고, 같은 정렬 선택은 dismiss만 수행하며 새 정렬 선택은viewModel.changeSort(sort)로 전달한다. - 2026-06-17: 작은 화면에서 popup 오른쪽이 visible display frame 밖으로 나가는 경우
calculateHorizontalOffset()으로 음수 x offset을 계산해showAsDropDown()에 전달하도록 위치 보정 기준을 코드에 남겼다. - 2026-06-17:
CreatorChannelLiveFragment는layoutCreatorChannelLiveSortButton클릭 시 현재Content상태의selectedSort로 popup을 표시하고,onDestroyView()에서sortPopup?.dismiss()로 window leak을 방지한다.
- 2026-06-17: Figma
- 생성 후보:
-
Task 5.2: Sort 동작 테스트 보강
- 수정:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.kt
- 작업:
ContentSort별 label resource 매핑을 검증한다.- 정렬 변경 시 첫 페이지가 다시 로딩되는지 검증한다.
- 같은 정렬 재선택은 API 재호출을 하지 않는지 검증한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"
- 기대 결과:
- sort 관련 테스트가 PASS한다.
- 검증 기록:
- 2026-06-17: production 구현 전
CreatorChannelLiveFragmentLayoutTest에 popup layout/resource/source wiring 테스트를 먼저 추가했다. RED 실행 결과view_creator_channel_live_sort_menu,layout_creator_channel_live_sort_options미존재로:app:compileDebugUnitTestKotlin FAILED가 발생해 실패를 확인했다. - 2026-06-17: 기존
CreatorChannelLiveMapperTest는ContentSort별 label resource 매핑을 이미 검증하고, 기존CreatorChannelLiveViewModelTest는 정렬 변경 첫 페이지 재로딩 및 같은 정렬 재선택 no-op을 이미 검증하고 있어 중복 테스트를 추가하지 않았다. - 2026-06-17: 구현 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck실행 결과 모두BUILD SUCCESSFUL로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고와 기존 Kotlin deprecation/annotation 경고는 Phase 5 변경과 무관하다.
- 2026-06-17: production 구현 전
- 수정:
Phase 6: 본인 채널 하단 CTA와 라이브 시작 진입 연결
-
Task 6.1: 본인 CTA 노출과 inset 처리 구현
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/res/layout/fragment_creator_channel_live.xml
- 작업:
- 본인 채널일 때만 하단 CTA를 표시한다.
- 타인 채널 또는 본인 여부 로딩 전에는 CTA를 숨긴다.
- CTA 표시 시 RecyclerView 하단 padding을 추가해 마지막 item이 CTA에 가려지지 않도록 한다.
- navigation bar bottom inset을 CTA 영역에 반영한다.
- 버튼 icon은
ic_new_create_live, label은라이브 시작하기문자열 리소스를 사용한다.
- 검증 명령:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin
- 기대 결과:
- CTA resource와 inset 코드가 컴파일된다.
- 검증 기록:
- 2026-06-18: Figma
665:19359,665:19371을 확인해 본인 채널 하단 CTA가 검정 하단 영역 위 14dp inset,soda_400capsule,ic_new_create_live,라이브 시작하기label로 구성됨을 확인했다. 기존fragment_creator_channel_live.xml의 CTA 골격과bg_creator_channel_owner_fab,ic_new_create_live,creator_channel_live_start_button리소스를 재사용했다. - 2026-06-18:
CreatorChannelLiveFragmentLayoutTest에 owner CTA source/layout 계약 테스트를 추가했다. production 구현 전 실행은 신규 assertion까지 도달하기 전에 기존 KSP unit-test cache 손상으로:app:kspDebugUnitTestKotlin FAILED,java.io.UTFDataFormatException이 발생해app/build/kspCaches/debugUnitTest,app/build/generated/ksp/debugUnitTest생성물 캐시를 삭제 후 재검증했다. - 2026-06-18:
CreatorChannelLiveFragment에Host.isCreatorChannelOwner(),onCreatorChannelOwnerChanged(isOwner),bindOwnerCta()를 추가해 본인 채널에서만layoutCreatorChannelLiveOwnerCta를 표시하고 타인 채널 또는 본인 여부 로딩 전에는 숨기도록 했다. - 2026-06-18:
ViewCompat.setOnApplyWindowInsetsListener(binding.layoutCreatorChannelLiveOwnerCta)로WindowInsetsCompat.Type.navigationBars()bottom inset을 받아 CTA bottom margin에14dp + navigationBottomInset을 적용하고, CTA 표시 시rvCreatorChannelLiveReplaysbottom padding을102dp + navigationBottomInset으로 조정해 마지막 item이 CTA에 가려지지 않도록 했다. CTA 미표시 시 기존 32dp bottom padding으로 복원한다. - 2026-06-18:
./gradlew :app:mergeDebugResources실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-18:
./gradlew :app:compileDebugKotlin단독 재실행 결과BUILD SUCCESSFUL로 통과했다. 병렬 Gradle 실행 중 한 차례 extension import 해석 실패가 발생했으나 동일 compile 명령 단독 재실행에서 통과했으며, 기존 deprecation/annotation 경고는 Phase 6 변경과 무관하다.
- 2026-06-18: Figma
- 수정:
-
Task 6.2: 기존 라이브 시작 플로우 연결
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/live/room/create/LiveRoomCreateActivity.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt- 필요 시
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
CreatorChannelActivity의 기존 owner FAB live action과 같은 진입 경로를 재사용한다.- 중복 터치를 방지한다.
- 라이브 생성 완료 후 기존 홈 refresh/라이브룸 진입 정책과 충돌하지 않도록 처리한다.
- 검증:
- 라이브 시작 진입 대상 Activity/Fragment와 전달 extra를 계획 문서 해당 Task의
검증 기록에 남긴다.
- 라이브 시작 진입 대상 Activity/Fragment와 전달 extra를 계획 문서 해당 Task의
- 검증 기록:
- 2026-06-18:
CreatorChannelLiveFragment.Host에onCreatorChannelLiveStartClicked()를 추가하고 CTA 클릭 시layoutCreatorChannelLiveOwnerCta.isEnabled = false후 host callback을 호출하도록 연결했다.onResume()에서 CTA enabled 상태를 복원해 라이브 생성 화면에서 돌아온 뒤 다시 클릭 가능하다. - 2026-06-18:
CreatorChannelActivity.onCreatorChannelLiveStartClicked()는 기존onOwnerFabLiveClicked()를 재사용한다. 진입 대상은LiveRoomCreateActivity이며, 기존liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))경로를 그대로 사용하므로 별도 extra는 추가하지 않는다. - 2026-06-18: 라이브 생성 완료 후 기존 정책과 동일하게
RESULT_OK에서homeActionDelegate?.refreshHome()를 호출하고,Constants.EXTRA_ROOM_ID,Constants.EXTRA_ROOM_CHANNEL_NAME결과에 따라CreatorChannelLiveCoordinator.enterLiveRoom(roomId)또는creator_channel_live_created_messagetoast를 사용한다. - 2026-06-18:
CreatorChannelActivitySourceTest에 live tab owner CTA가 header 본인 여부를 Fragment로 전달하고 기존 라이브 생성 플로우를 재사용하는 source 계약 테스트를 추가했다. - 2026-06-18:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"재실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-18:
./gradlew :app:ktlintCheck실행 결과BUILD SUCCESSFUL로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 Phase 6 변경과 무관하다.
- 2026-06-18:
- 확인:
Phase 6 코드 리뷰/검증 기록
- 2026-06-18: Phase 6 변경 범위를 코드 리뷰했다.
CreatorChannelActivity의currentHeader?.isOwner == true기반 owner 상태 전달,CreatorChannelLiveFragment의 owner CTA visibility/inset/list bottom padding/click 중복 방지, 기존onOwnerFabLiveClicked()재사용 경로를 확인했으며 차단 이슈는 발견하지 못했다. - 2026-06-18: fresh verification으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"를 실행해BUILD SUCCESSFUL in 27s로 통과했다. 최초 sandbox 실행은~/.gradlewrapper lock 파일 권한 제한으로 실패해 권한 승인 후 재실행했다. - 2026-06-18: fresh verification으로
./gradlew :app:mergeDebugResources를 실행해BUILD SUCCESSFUL in 1s로 통과했다. 최초 sandbox 실행은~/.gradlewrapper lock 파일 권한 제한으로 실패해 권한 승인 후 재실행했다. - 2026-06-18: fresh verification으로
./gradlew :app:compileDebugKotlin을 실행해BUILD SUCCESSFUL in 1s로 통과했다. - 2026-06-18: fresh verification으로
./gradlew :app:ktlintCheck를 실행해BUILD SUCCESSFUL in 11s로 통과했다. Gradle deprecation 경고는 기존 빌드 설정 경고로 Phase 6 변경과 직접 관련 없다.
Phase 7: 탭 연결과 통합 동작
-
Task 7.1:
CreatorChannelTab.Live를 실제 Fragment로 연결- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
- 작업:
- Live 탭 position에서
CreatorChannelLiveFragment.newInstance(creatorId)를 반환한다. - 나머지 탭은 기존 placeholder 정책을 유지한다.
- Live 탭 position에서
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"
- 기대 결과:
- Live 탭만 실제 Fragment로 연결되고 나머지 placeholder 정책은 유지된다.
- 검증 기록:
- 2026-06-17: 리뷰 게이트 차단 이슈 보정으로
CreatorChannelPagerAdapter에서CreatorChannelTab.Live -> CreatorChannelLiveFragment.newInstance(creatorId)분기를 추가했다. Home/Live 탭은 실제 Fragment로 연결하고, 후속 탭은 기존 placeholder 정책을 유지한다. - 2026-06-17: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS.
- 2026-06-17: 리뷰 게이트 차단 이슈 보정으로
- 수정:
-
Task 7.2: DI와 lifecycle 통합
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt
- 작업:
- 신규 ViewModel/API/Repository injection이 기존 홈 탭과 충돌하지 않는지 확인한다.
- Fragment 재생성 후 정렬, page, 목록 상태가 ViewModel에 유지되는지 확인한다.
- 탭 전환 시 중복 최초 API 호출이 발생하지 않도록 초기 로딩 조건을 정리한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"./gradlew :app:compileDebugKotlin
- 기대 결과:
- Live 탭 관련 테스트와 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-18:
AppDI에서CreatorChannelApi와CreatorChannelRepository는 기존 홈 탭 공통 binding을 유지하고,CreatorChannelLiveViewModel(get())가 같은 repository를 주입받는 것을 확인했다. 별도 Live API/Repository binding은 추가하지 않았다. - 2026-06-18:
CreatorChannelLiveFragment.onViewCreated()는 초기 API 호출을 수행하지 않고,CreatorChannelActivity.onPageSelected()가 Live 탭 선택 시findLiveFragment()?.onCreatorChannelLiveTabSelected()를 호출해viewModel.loadLive(creatorId)를 시작하는 lazy load 경로를 확인했다. - 2026-06-18:
CreatorChannelLiveViewModel.loadLive()는 같은creatorId와 기존 상태가 있으면 no-op 하므로 Fragment 재생성/탭 재바인딩 중 정렬, page, 목록 상태를 유지한다. 해당 계약은CreatorChannelLiveViewModelTest의같은 creatorId로 다시 loadLive를 호출하면 기존 상태를 유지하고 API를 재호출하지 않는다테스트로 고정되어 있음을 확인했다. - 2026-06-18:
CreatorChannelActivity의binding.viewPager.offscreenPageLimit = CreatorChannelTab.entries.size - 1설정과 Live 탭 선택 hook은CreatorChannelActivitySourceTest에서 고정되어 있어 Home delegate 보존과 Live lazy load 통합 계약이 유지됨을 확인했다. - 2026-06-18:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL in 34s로 통과했다. - 2026-06-18:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 7s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 Phase 7.2 변경과 직접 관련 없다.
- 2026-06-18:
- 수정:
Phase 7 코드 리뷰/검증 기록
- 2026-06-18: Phase 7 전체 변경 범위를 코드 리뷰했다.
CreatorChannelPagerAdapter는CreatorChannelTab.Live에서CreatorChannelLiveFragment.newInstance(creatorId)를 반환하고 Home/후속 탭 정책은 유지한다.CreatorChannelActivity는 Live 탭 선택 시onCreatorChannelLiveTabSelected()로 lazy load를 시작하고, NestedScrollView 하단 접근 및 content 변경 후 bottom 재평가에서loadMore()경로를 호출한다.AppDI는 공통CreatorChannelApi/CreatorChannelRepositorybinding과CreatorChannelLiveViewModel(get())주입을 사용하며 별도 Live API/Repository binding을 만들지 않는다.CreatorChannelLiveViewModel.loadLive()의 동일creatorIdno-op 조건과 관련 테스트로 Fragment 재생성/탭 재바인딩 중 상태 유지 계약을 확인했다. 차단 이슈는 발견하지 못했다. - 2026-06-18: fresh verification으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"를 실행해BUILD SUCCESSFUL in 8s로 통과했다. 최초 sandbox 실행은~/.gradlewrapper lock 파일 권한 제한으로 실패해 권한 승인 후 재실행했다. - 2026-06-18: fresh verification으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"를 실행해BUILD SUCCESSFUL in 11s로 통과했다. - 2026-06-18: fresh verification으로
./gradlew :app:compileDebugKotlin을 실행해BUILD SUCCESSFUL in 2s로 통과했다. - 2026-06-18: fresh verification으로
./gradlew :app:ktlintCheck를 실행해BUILD SUCCESSFUL in 1s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 Phase 7 변경과 직접 관련 없다.
Phase 8: 최종 검증과 문서 기록
-
Task 8.1: 자동 검증 실행
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheck
- 기대 결과:
- 모든 명령이
BUILD SUCCESSFUL로 통과한다.
- 모든 명령이
- 검증 기록:
- 실행 일시, 명령, 결과, 기존 경고 여부를 이 Task 아래에 누적 기록한다.
- 2026-06-18:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"실행 결과BUILD SUCCESSFUL in 12s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"실행 결과BUILD SUCCESSFUL in 11s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"실행 결과BUILD SUCCESSFUL in 14s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 1s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 21s로 통과했다. Gradle daemon 1개 busy로 신규 daemon이 시작됐고, Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:ktlintCheck실행 결과BUILD SUCCESSFUL in 16s로 통과했다. Gradle daemon 1개 busy로 신규 daemon이 시작됐고, Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다.
- 실행 명령:
-
Task 8.2: 수동 확인
- 확인 항목:
라이브탭 진입 시page=0,sort=LATEST로 최초 조회된다.- Sort-bar에
전체 {count}와 현재 정렬 label,ic_new_sort가 표시된다. - 정렬 컨텍스트 메뉴가 Sort-bar 아래에 표시되고 선택/닫힘 동작이 정상이다.
currentLive != null일 때 현재 라이브 카드가 표시된다.- 라이브 다시듣기 item의 19금/포인트/무료/소장중/대여중/가격 상태가 DTO와 일치한다.
isOwned && isRented동시 true item은소장중으로 표시된다.seriesName은 표시되지 않는다.hasNext == true일 때 하단 스크롤로 다음 페이지가 append된다.- 본인 채널에서만 하단
라이브 시작하기CTA가 표시된다. - 타인 채널에서는 하단 CTA가 표시되지 않는다.
- CTA가 마지막 item을 가리지 않는다.
- 검증 기록:
- 실제 확인한 시나리오와 결과를 이 Task 아래에 한국어로 누적 기록한다.
- 2026-06-18: 연결 단말 확인을 위해
adb devices를 실행했고2cec640c34017ece device가 연결되어 있음을 확인했다.adb shell pm list packages | rg "sodalive"결과kr.co.vividnext.sodalive.debug,kr.co.vividnext.sodalive가 설치되어 있음을 확인했다. - 2026-06-18:
CreatorChannelActivity를extra_creator_id=1로 직접 실행해 Live 탭 실제 화면 검증을 시도했으나, Activity가 exported 대상이 아니어서Permission Denial ... not exported보안 예외로 차단됐다. - 2026-06-18:
adb shell monkey -p kr.co.vividnext.sodalive.debug 1로 디버그 앱 런처 실행은 성공했다. 실행 후 focus는kr.co.vividnext.sodalive.debug/com.gun0912.tedpermission.TedPermissionActivity로 확인되어 권한 화면 진입까지만 실제 단말 표면으로 확인했다. - 2026-06-18: Live 탭 상세 화면은 로그인/권한/앱 내부 이동 및 서버 데이터가 필요한 경로라 현재 shell 직접 실행으로는 Figma 대비 실제 화면 대조를 완료하지 못했다. 따라서 Task 8.2는 남은 실제 수동 QA로 유지한다.
- 2026-06-18: 자동 검증과 소스/테스트 근거로
page=0/sort=LATEST최초 조회, 정렬 변경/동일 정렬 no-op, load-more append,isOwned && isRented시소장중우선,seriesName미표시, owner CTA visibility/inset/click 중복 방지,ic_new_sort연결은 확인했다. 실제 화면의 Sort popup 표시/닫힘, current live 카드 렌더링, CTA가 마지막 item을 가리지 않는지는 후속 실제 단말 시나리오에서 확인이 필요하다.
- 확인 항목:
Phase 4 Review Fix: Lazy Load와 Empty/Error 중앙 정렬 보정
-
Task RF4.1: Live 탭 선택 시점 lazy load 보정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
- Live Fragment 생성 시점인
onViewCreated()에서는loadLive()를 호출하지 않는다. CreatorChannelTab.Live가 선택된 시점에만 Live Fragment에 명시적으로 로딩을 요청한다.- 같은 creatorId/state에 대한 중복 호출 방지는 기존
CreatorChannelLiveViewModel.loadLive()guard를 유지한다.
- Live Fragment 생성 시점인
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"
- 기대 결과:
- Live 탭을 보지 않은 Activity 최초 진입에서는 Live API 요청이 시작되지 않고, Live 탭 선택 시점에 첫 조회가 시작된다.
- 검증 기록:
- 2026-06-17: production 보정 전 source 테스트에서
offscreenPageLimit = CreatorChannelTab.entries.size - 1,onViewCreated()즉시viewModel.loadLive(creatorId)호출, Live 선택 hook 미존재로 RED 실패를 확인했다. - 2026-06-17:
CreatorChannelLiveFragment.onViewCreated()의 즉시loadLive()호출을 제거하고onCreatorChannelLiveTabSelected()로 이동했다.CreatorChannelActivity.onPageSelected()는 Live 탭 선택 시viewPager.post { findLiveFragment()?.onCreatorChannelLiveTabSelected() }를 호출하며, 전체 탭 선생성을 유발하던offscreenPageLimit = CreatorChannelTab.entries.size - 1설정을 제거했다. - 2026-06-17: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS.
- 2026-06-17: production 보정 전 source 테스트에서
- 수정:
-
Task RF4.2: Empty/Error 상태 viewport 중앙 정렬 보정
- 수정:
app/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
- Live Fragment root가
ViewPager2page 높이를 채우도록 보장한다. - Empty/Error 상태 전환 시에도
ViewPager2높이 재측정을 요청한다. - Activity의
updateViewPagerHeight()는 현재 page의 최소 높이를 탭 viewport 기준으로 보정한다.
- Live Fragment root가
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew :app:mergeDebugResources
- 기대 결과:
- Live Empty 문구와 Error/Retry 그룹이 작은 wrap-content 영역이 아니라 탭 viewport 중앙에 표시된다.
- 검증 기록:
- 2026-06-17: production 보정 전 layout/source 테스트에서 Live root
wrap_content, Empty/Error 상태 height callback 미보장, Activity page minimumHeight 보정 미존재로 RED 실패를 확인했다. - 2026-06-17:
fragment_creator_channel_live.xmlroot 높이를match_parent로 변경하고,bindEmpty()/bindError()에서도host.onCreatorChannelLiveContentChanged()를 호출하도록 했다.CreatorChannelActivity.updateViewPagerHeight()는 현재 page 측정 전currentPage.minimumHeight = calculateCreatorChannelTabViewportHeight()를 적용해 탭 viewport 기준 최소 높이를 보장한다. - 2026-06-17: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS,./gradlew :app:mergeDebugResourcesPASS.
- 2026-06-17: production 보정 전 layout/source 테스트에서 Live root
- 수정:
-
Task RF4.3: Home delegate 보존과 load-more 메타데이터 보존 보정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt
- 작업:
- Live lazy load는 유지하되 Home 탭의
HomeActionDelegate가 후속 탭 이동 중 제거되지 않도록ViewPager2.offscreenPageLimit을 기존처럼 전체 탭 수 기준으로 복구한다. - 다음 페이지 성공 시 기존 첫 페이지의
currentLive,liveReplayContentCount,selectedSort를 보존하고, 목록 append와page/size/hasNext만 다음 응답 기준으로 갱신한다.
- Live lazy load는 유지하되 Home 탭의
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"./gradlew :app:compileDebugKotlin
- 기대 결과:
- Home 탭 delegate 기반 상단 액션/refresh 경로가 Live 및 후속 탭 이동 후에도 유지된다.
- 다음 페이지 응답이
currentLive == null이거나 총 개수가 부분 값이어도 현재 라이브 카드와 총 개수/정렬 label이 첫 페이지 상태를 유지한다.
- 검증 기록:
- 2026-06-17:
CreatorChannelActivity.setupTabsAndPager()에binding.viewPager.offscreenPageLimit = CreatorChannelTab.entries.size - 1를 복구해 Home 탭 Fragment와HomeActionDelegate가 후속 탭 이동 중 제거되지 않도록 했다.CreatorChannelActivitySourceTest의 기존 offscreenPageLimit 제거 기대는 복구 정책에 맞춰 갱신했다. - 2026-06-17:
CreatorChannelLiveViewModel.loadMore()성공 처리에서data.toContentState()로 전체 content metadata를 덮어쓰지 않고, 기존Content를copy()해liveReplayContentsappend와page/size/hasNext만 다음 응답 기준으로 갱신하도록 변경했다. - 2026-06-17:
CreatorChannelLivePaginationTest에 다음 페이지 응답이currentLive == null, 다른liveReplayContentCount, 다른sort를 내려줘도 첫 페이지의 현재 라이브/count/sort를 보존하는 회귀 테스트를 추가했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"최초 실행은 상충하던 기존 offscreenPageLimit 제거 assertion으로 실패했고, 테스트 계약 갱신 후 재실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"최초 실행은 신규 테스트 fixture의CreatorChannelLiveResponse필드명 오기로 컴파일 실패했고,coverImageUrl로 수정 후 재실행 결과BUILD SUCCESSFUL로 통과했다. - 2026-06-17: 추가 회귀 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*",./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck를 실행했고 모두BUILD SUCCESSFUL로 통과했다.ktlintCheck에서는 기존.editorconfig의disabled_rulesdeprecation 경고가 계속 출력되었다.
- 2026-06-17:
- 수정:
Phase 6 Follow-up: CTA Figma 고정 영역 보정
-
Task RF6.1: 라이브 시작하기 CTA를 Figma
665:19371기준으로 보정- 수정:
app/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
- CTA outer 영역은 화면 하단에 고정된 100dp black container로 유지한다.
- 내부 버튼은 좌우 14dp, top 14dp 위치의
soda_400capsule로 표시한다. - CTA는 스크롤 컨텐츠가 아니라 Fragment root에 고정해 스크롤하지 않아도 보이게 한다.
- CTA 표시 시 리스트 하단 padding은 CTA 100dp 영역과 navigation bar inset을 반영해 마지막 item이 가려지지 않게 한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheck
- 기대 결과:
- 본인 채널의
라이브 시작하기CTA가 Figma처럼 하단 고정 100dp 영역 안에 표시되고, 스크롤하지 않아도 보인다.
- 본인 채널의
- 검증 기록:
- 2026-06-18: Figma
665:19359, CTA layer665:19371을 재확인했다. CTA는bottom=0,height=100, black 배경의 outer 영역이며, 내부 버튼은left/right=14,top=14,soda_400 #00BDF7, 24dp icon, 18sp medium label 구조다. 기존 구현은 56dp capsule 자체를 parent 하단에 붙이는 구조라 Figma outer CTA 영역과 달라 보정한다. - 2026-06-18: production 보정 전
CreatorChannelLiveFragmentLayoutTest를 실행해 Activity root overlay CTA 계약 미충족으로 실패함을 확인했다. 최초 RED 시도는 테스트 문자열 escaping 오류로 컴파일 실패했고, 테스트 문자열을 수정한 뒤CreatorChannelLiveFragmentLayoutTest > 라이브 owner CTA는 Activity root overlay로 Figma 고정 영역을 제공한다가 assertion 실패로 RED가 됐다. - 2026-06-18: CTA를
fragment_creator_channel_live.xml에서 제거하고activity_creator_channel.xmlroot overlay로 이동했다. Activity overlay는 100dp black container, 내부 52dpsoda_400capsule, 좌우/상단 14dp margin,ic_new_create_live,creator_channel_live_start_button구조로 Figma665:19371을 맞춘다. - 2026-06-18:
CreatorChannelActivity가 Live 탭이면서 본인 채널일 때만 CTA를 표시하도록updateLiveOwnerCtaVisibility()를 추가했다. navigation bar inset은 CTA container 높이와nestedScrollViewbottom padding에 반영해 CTA가 화면 하단에 고정되어도 컨텐츠가 가려지지 않도록 했다. - 2026-06-18: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL in 1m 33s로 GREEN을 확인했다. - 2026-06-18:
./gradlew :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 1s로 통과했다. - 2026-06-18:
./gradlew :app:ktlintCheck최초 실행은 제가 추가한 빈 줄 1개로 실패했고, 빈 줄 제거 후 재실행 결과BUILD SUCCESSFUL in 31s로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 계속 출력되었다. - 2026-06-18: 병렬 Gradle 실행 중 Kotlin incremental cache 경합으로
compileDebugKotlin이 실패/timeout 되었으나./gradlew --stop후 순차 재실행한./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL in 2m 4s로 통과했다. 기존 deprecation/annotation 경고는 변경 범위와 무관하다. - 2026-06-18: 최종 확인으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"를 순차 재실행해BUILD SUCCESSFUL in 27s로 통과했다.
- 2026-06-18: Figma
- 수정:
-
Task RF6.2: 실제 기기 bottom 위치와 짧은 Live 상태 스크롤 보정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
BaseActivityroot bottom padding과owner_fab_button/layout_creator_channel_live_owner_cta의 navigation inset 중복 적용을 제거한다.- Live 탭의 empty/error처럼 짧은 상태에서는
ViewPager2page가 viewport 높이로 강제 확장되지 않아 스크롤 없이 안내 문구와 retry 버튼이 보이도록 한다. - CTA 표시 시 content bottom padding은 CTA 100dp 영역만 반영하고 navigation inset은 root padding에 맡긴다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheck
- 기대 결과:
- 실제 기기에서 owner FAB와 live CTA가 XML preview 기준 bottom 위치와 동일한 safe-area 기준으로 표시된다.
- Live 탭 error/empty 같은 짧은 상태에서 불필요한 긴 스크롤이 생기지 않는다.
- 검증 기록:
- 2026-06-18: 사용자 실제 기기 확인 결과
owner_fab_button과layout_creator_channel_live_owner_cta가 XML preview보다 위에 표시되고, Live 탭 error/empty 짧은 상태에서도 Fragment 높이가 길어 스크롤이 발생함을 확인 요청받았다. 코드 확인 결과BaseActivity가 root에 system bar bottom padding을 이미 적용하는데CreatorChannelActivity가 동일 navigation inset을 FAB margin과 CTA height/padding에 추가로 반영하고 있어 bottom 위치가 중복 보정될 수 있음을 확인했다. - 2026-06-18: RED 확인으로
CreatorChannelActivitySourceTest와CreatorChannelLiveFragmentLayoutTest에 navigation inset 중복 제거,currentPage.minimumHeight강제 제거, Live rootwrap_content계약을 추가한 뒤 실행했다. production 보정 전CreatorChannelActivitySourceTest > 라이브 탭 pagination과 높이 갱신은 NestedScrollView 소유 스크롤 경로에서 처리한다,CreatorChannelActivitySourceTest > 크리에이터 채널 하단 고정 UI는 BaseActivity root bottom padding과 navigation inset을 중복 적용하지 않는다,CreatorChannelLiveFragmentLayoutTest > 라이브 fragment layout은 sort current live list empty error owner CTA를 제공한다,CreatorChannelLiveFragmentLayoutTest > 라이브 fragment와 adapter source는 필수 drawable과 retry loadMore click 연결을 포함한다가 실패함을 확인했다. - 2026-06-18:
CreatorChannelActivity.setupOwnerFabInsets()에서 FAB margin에 navigation inset을 더하던 listener를 제거하고, ViewPager content bottom padding만 유지했다. Live CTA도 Activity root overlay의 XML 100dp height를 유지하고,nestedScrollViewbottom padding에는 CTA 100dp만 반영하도록 변경했다. navigation bar 대응은BaseActivityroot bottom padding에 맡긴다. - 2026-06-18: 짧은 Live 상태에서 불필요하게 긴 스크롤이 생기지 않도록
updateViewPagerHeight()의currentPage.minimumHeight = calculateCreatorChannelTabViewportHeight()강제 설정을 제거하고,fragment_creator_channel_live.xmlroot를wrap_content로 변경했다. empty/error 안내 영역은 parent bottom constraint를 제거해 실제 표시 컨텐츠 높이만 측정되도록 했다. - 2026-06-18: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL in 30s로 통과했다. - 2026-06-18:
./gradlew :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 2s로 통과했다. - 2026-06-18:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 1s로 통과했다. - 2026-06-18:
./gradlew :app:ktlintCheck최초 실행은 테스트 source assertion 2줄의 line length/argument wrapping 위반으로 실패했고, 줄바꿈 보정 후 재실행 결과BUILD SUCCESSFUL in 6s로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 계속 출력되었다. - 2026-06-18: 포맷 보정 후 최종 targeted 재검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"를 실행해BUILD SUCCESSFUL in 13s로 통과했다.
- 2026-06-18: 사용자 실제 기기 확인 결과
- 수정:
Phase 7 Follow-up: 탭 전환 시 sticky tabbar anchor 보정
- Task RF7.1: 탭 전환 시 sticky 위치로 먼저 정렬한 뒤 컨텐츠 표시
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 판단:
- 현재 구조는
NestedScrollView가 전체 스크롤을 소유하고,ViewPager2page 높이가 선택 탭 컨텐츠 높이에 맞춰 바뀐다. - 짧은 탭으로 전환하면 parent scroll range가 줄면서 기존 sticky 상태가 풀려 tabbar가 header 아래로 갑자기 되돌아가 보일 수 있다.
- 탭을 선택한 순간 사용자의 목적은 header 재확인이 아니라 선택한 탭 컨텐츠 탐색이므로, tabbar를 sticky anchor로 고정한 뒤 컨텐츠를 보여주는 편이 전환감과 예측 가능성이 좋다.
- 따라서 sticky 상태가 아니더라도 다른 탭 선택 시
NestedScrollView를 sticky threshold까지 보정하는 방식을 채택한다.
- 현재 구조는
- 작업:
- sticky threshold 계산은 기존
updateScrollState()와 같은 기준을 사용한다.stickyTop = CreatorChannelScrollState.calculateStickyTop(statusBarHeight, baseTitleBarHeight)stickyScrollY = (binding.headerContainer.height - stickyTop).coerceAtLeast(0)
ViewPager2.OnPageChangeCallback.onPageSelected(position)에서 이전 탭과 다른 탭으로 전환될 때 sticky 보정을 수행한다.- 현재
nestedScrollView.scrollY가stickyScrollY보다 작은 경우에만scrollTo(0, stickyScrollY)또는 동등한 즉시 보정을 수행한다. - 현재
nestedScrollView.scrollY가 이미stickyScrollY이상이면 사용자의 기존 scroll 위치를 낮추지 않는다. - 같은 탭 재선택 또는 초기 adapter attach 과정에서 불필요한 scroll 보정이 발생하지 않도록 마지막 선택 tab index 또는 초기 선택 여부를 관리한다.
- Live 탭 lazy load, pagination bottom check, owner CTA visibility 갱신은 기존 흐름을 유지한다.
- sticky threshold 계산은 기존
- RED 테스트:
CreatorChannelActivitySourceTest에 탭 전환 시 sticky threshold helper와nestedScrollView.scrollTo(0, stickyScrollY)호출 계약을 추가한다.- 같은 탭 재선택/초기 선택에서는 sticky 보정을 하지 않는 guard 계약을 추가한다.
- 이미 sticky 기준 이상으로 스크롤된 경우 scrollY를 낮추지 않는 guard 계약을 추가한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheck
- 기대 결과:
- Home 또는 Live 등 어느 탭에서든 다른 탭을 선택하면 tabbar가 sticky 위치로 유지된 상태에서 선택 탭 컨텐츠가 표시된다.
- 선택 탭 컨텐츠가 empty/error처럼 짧아도 tabbar가 header 아래로 풀리는 전환이 보이지 않는다.
- 이미 sticky보다 아래로 스크롤한 상태에서 탭을 바꿔도 scrollY가 위로 되돌아가지 않는다.
- 검증 기록:
- 2026-06-18: 사용자 피드백에 따라 탭 전환 시 짧은 컨텐츠 때문에 sticky tabbar가 풀려 보이는 현상을 검토했다. 현재
updateViewPagerHeight()가 선택 page 높이를 컨텐츠에 맞추고 parentNestedScrollView가 스크롤을 소유하므로, 짧은 탭 전환 시 scroll range 변화로 tabbar가 갑자기 header 아래 위치로 보일 수 있다고 판단했다. UX상 탭 전환 시점에는 sticky tabbar를 anchor로 유지하는 편이 더 자연스럽다고 판단해 PRD와 plan-task에 후속 작업을 추가했다. 구현은 아직 진행하지 않았다. - 2026-06-18:
CreatorChannelActivitySourceTest에 탭 전환 시 sticky anchor 보정 source 계약을 추가했다. production 보정 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 결과탭 전환은 sticky tabbar anchor 아래로 내려간 scroll 위치를 되돌리지 않고 부족할 때만 보정한다가 assertion 실패로 RED가 됐다. - 2026-06-18:
CreatorChannelActivity에lastSelectedCreatorChannelTabPosition,adjustCreatorChannelStickyAnchorOnTabSelected(position),calculateCreatorChannelStickyScrollY()를 추가했다. 초기 선택 또는 같은 탭 재선택은 무시하고, 다른 탭으로 전환할 때 현재NestedScrollView.scrollY가 sticky threshold보다 작으면scrollTo(0, stickyScrollY)로 보정하며 이미 sticky 기준 이상이면 기존 scroll 위치를 낮추지 않는다. - 2026-06-18: 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 결과BUILD SUCCESSFUL in 26s로 GREEN을 확인했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 11s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew :app:ktlintCheck실행 결과BUILD SUCCESSFUL in 25s로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 계속 출력됐다. - 2026-06-18: 리뷰 게이트에서
TabLayoutMediator.attach()이후OnPageChangeCallback을 등록하므로 초기 Home 선택 이벤트를 받지 못하면 첫 실제 탭 전환이previousPosition == null로 처리되어 sticky 보정이 누락될 수 있다는 차단 이슈가 발견됐다. - 2026-06-18:
CreatorChannelActivitySourceTest에lastSelectedCreatorChannelTabPosition = binding.viewPager.currentItembaseline 초기화 계약을 추가했다. production 보정 전 동일 테스트를 실행해탭 전환은 sticky tabbar anchor 아래로 내려간 scroll 위치를 되돌리지 않고 부족할 때만 보정한다가 assertion 실패로 RED가 됐다. - 2026-06-18:
setupTabsAndPager()에서TabLayoutMediator.attach()직후,OnPageChangeCallback등록 전lastSelectedCreatorChannelTabPosition = binding.viewPager.currentItem를 초기화했다. 이로써 첫 실제 탭 전환도 이전 탭과 다른 선택으로 판단되어 sticky anchor 보정이 수행된다. - 2026-06-18: 병렬/daemon Gradle 재검증 중
processDebugResources의 기본 resource not found,packageDebugResources삭제 실패,dataBindingGenBaseClassesDebug의chk_fri누락, Kotlin incremental cachelookups.tab중복 등록이 발생했다. 변경 코드 실패가 아니라 생성물/daemon cache 문제로 판단해app/build/intermediates,app/build/generated,app/build/kspCaches,app/build/tmp,app/build/kotlin생성물 캐시를 정리하고 Gradle/Kotlin daemon을 비활성화해 순차 재검증했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 4m 19s로 통과했다. 기존 Kotlin deprecation/annotation 경고는 변경 범위와 무관하게 출력됐다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 결과BUILD SUCCESSFUL in 1m 32s로 통과했다. 기존 Gradle 9.0 호환성 deprecation 경고는 계속 출력됐다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:ktlintCheck실행 결과:app:ktlintMainSourceSetCheck에서 실패했다. 보고서 확인 결과Agora.kt,SodaLiveApp.kt,audio_contentpackage-name 등 기존 main source 위반 574건이 출력됐고,rg -n "CreatorChannelActivity|creator/channel|plan-task" app/build/reports/ktlint/ktlintMainSourceSetCheck/ktlintMainSourceSetCheck.txt결과 이번 RF7.1 변경 파일은 포함되지 않았다.
- 2026-06-18: 사용자 피드백에 따라 탭 전환 시 짧은 컨텐츠 때문에 sticky tabbar가 풀려 보이는 현상을 검토했다. 현재
- 수정:
Phase 8 Follow-up: Live empty 안내와 다시듣기 item Figma 보정
-
Task RF8.1: Live empty 안내 메시지와 다시듣기 item 간격/썸네일 radius 보정
- 수정:
app/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/main/res/layout/item_creator_channel_live_replay.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveReplayAdapter.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
- Live empty 상태에서
크리에이터가 라이브를 준비 중입니다.\n기대해 주세요!안내 문구가 실제 탭 영역 안에서 보이도록 empty 영역 높이와 중앙 정렬을 보장한다. - 라이브 다시듣기 item 사이 간격을 Figma 기준 8dp로 적용한다.
- 라이브 다시듣기 썸네일 이미지는 Figma 기준 14dp rounded corner로 실제 이미지가 clipping되도록 적용한다.
- Live empty 상태에서
- RED 테스트:
CreatorChannelLiveFragmentLayoutTest에 empty 메시지 표시 영역이 0dp match-constraints가 아닌 viewport 높이를 갖는지 확인하는 source/layout 계약을 추가한다.CreatorChannelLiveFragmentLayoutTest에 replay item 8dp spacing과 thumbnailradius_14clipping 계약을 추가한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin
- 기대 결과:
- Live empty 상태에서 안내 메시지가 보인다.
- 라이브 다시듣기 item 사이 간격은 8dp이고 썸네일 이미지는 14dp rounded corner로 표시된다.
- 검증 기록:
- 2026-06-18: 현재 코드 확인 결과 Live empty 문구는 root
ConstraintLayouttop에 직접 붙은TextView라wrap_contentpage 높이/탭 전환 환경에서 보장된 표시 영역을 갖지 못할 수 있음을 확인했다. 라이브 다시듣기 item은 썸네일 background에radius_14가 있으나 실제 이미지 clipping이 없고, item 간 8dp 간격도 적용되지 않았다. - 2026-06-18:
CreatorChannelLiveFragmentLayoutTest에 empty container 표시 영역과 replay item 8dp spacing/thumbnail clipping 계약을 추가했다. production 보정 전 실행한./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"는layout_creator_channel_live_empty미존재로:app:compileDebugUnitTestKotlin FAILED가 발생해 RED를 확인했다. - 2026-06-18:
fragment_creator_channel_live.xml에layout_creator_channel_live_emptyFrameLayout을 추가해minHeight=360dp,gravity=center안에서 empty message를 중앙 표시하도록 변경했다.CreatorChannelLiveFragment는 empty/loading/error/content 상태에서layoutCreatorChannelLiveEmptyvisibility를 제어하도록 보정했다. - 2026-06-18:
item_creator_channel_live_replay.xmlroot에android:layout_marginBottom="@dimen/spacing_8"을 추가하고, 썸네일 container에android:clipToOutline="true",android:outlineProvider="background"를 적용했다.CreatorChannelLiveReplayAdapter.ViewHolder에서도ViewOutlineProvider를 설정해 실제 썸네일 이미지를radius_14로 clipping하도록 보강했다. - 2026-06-18: 보정 후
CreatorChannelLiveFragmentLayoutTest최초 GREEN 시도는 테스트가 item root margin이 아니라 thumbnail margin을 확인해 실패했고, Figma 기준인 item 사이 간격을 source 계약으로 확인하도록 테스트를 바로잡았다. 재실행 결과./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"가BUILD SUCCESSFUL in 1m 7s로 통과했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 28s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 25s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다.
- 2026-06-18: 현재 코드 확인 결과 Live empty 문구는 root
- 수정:
-
Task RF8.2: Live empty 최소 높이를 sticky 가능한 탭 viewport 기준으로 보정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/res/layout/fragment_creator_channel_live.xmlapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
- 고정
360dpempty 높이를 제거한다. - Activity가 sticky anchor 이후 실제 탭 viewport를 계산해 Live Fragment empty container의 최소 높이로 전달한다.
- Live empty 상태에서
NestedScrollView의 최대 scroll range가 sticky threshold보다 작아져 tabbar가 header 아래로 풀리는 상황을 방지한다. - content/error 상태의 짧은 스크롤 정책은 기존 RF6.2 범위를 유지하고, empty 상태만 최소 높이를 보정한다.
- 고정
- RED 테스트:
CreatorChannelActivitySourceTest에calculateCreatorChannelLiveEmptyMinHeight()와findLiveFragment()?.onCreatorChannelLiveViewportHeightChanged(...)호출 계약을 추가한다.CreatorChannelLiveFragmentLayoutTest에 고정android:minHeight="360dp"제거와 runtimelayoutCreatorChannelLiveEmpty.minimumHeight적용 계약을 추가한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin
- 기대 결과:
- Live empty 안내 문구가 실제 탭 viewport 중앙에 표시된다.
- Live empty 탭 전환 시 sticky tabbar anchor가 scroll range 부족으로 풀리지 않는다.
- 검증 기록:
- 2026-06-18: 리뷰 지적에 따라 RF8.1의 고정
360dpempty 높이가 실제 탭 viewport 중앙/Sticky 유지 요구를 보장하지 못할 수 있음을 확인했다. 탭 컨텐츠가 너무 짧으면NestedScrollView.scrollTo(stickyScrollY)가 최대 scroll range에 clamp되어 tabbar가 sticky anchor에 도달하지 못할 수 있으므로 Activity 계산값을 empty 상태에만 전달하도록 보정한다. - 2026-06-18: production 보정 전
CreatorChannelActivitySourceTest와CreatorChannelLiveFragmentLayoutTest에 viewport min height 전달 계약과 고정360dp제거 계약을 추가했다. 실행 결과라이브 탭 pagination과 높이 갱신은 NestedScrollView 소유 스크롤 경로에서 처리한다,라이브 empty 최소 높이는 sticky anchor 이후 탭 viewport 기준으로 전달한다,라이브 fragment layout은 sort current live list empty error owner CTA를 제공한다,라이브 empty container 최소 높이는 Activity가 전달한 viewport 높이를 사용한다4개 테스트가 실패해 RED를 확인했다. - 2026-06-18:
fragment_creator_channel_live.xml에서android:minHeight="360dp"를 제거했다.CreatorChannelLiveFragment에는emptyMinHeight,onCreatorChannelLiveViewportHeightChanged(minHeight),applyEmptyMinHeight()를 추가해 empty 상태에서 Activity가 전달한 최소 높이를layoutCreatorChannelLiveEmpty.minimumHeight에 적용하도록 했다. - 2026-06-18:
CreatorChannelActivity는 Live 탭 선택/Live content 변경/ViewPager height 갱신 시updateCreatorChannelLiveViewportHeight()를 호출한다. 최소 높이는visibleTabViewportHeight = nestedScrollView.height - tabLayout.height와scrollRangeRequiredHeight = nestedScrollView.height + stickyScrollY - headerContainer.height중 큰 값으로 계산해, empty page가 실제 탭 viewport를 채우면서 sticky scroll range도 만족하도록 했다. - 2026-06-18: 보정 후
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL in 1m 50s로 통과했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 44s로 통과했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 56s로 통과했다.
- 2026-06-18: 리뷰 지적에 따라 RF8.1의 고정
- 수정:
-
Task RF8.3: Live owner CTA 표시 시 다시듣기 마지막 item cut-off 보정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
- 작업:
- Activity overlay CTA 표시 여부를 Live Fragment에 전달한다.
- CTA가 표시될 때 마지막 다시듣기 item이 하단 CTA 영역에 가려지지 않도록
RecyclerView하단 padding을 CTA 높이만큼 추가한다. - CTA 미표시 상태에서는 기존 기본 list bottom padding만 유지한다.
- RED 테스트:
CreatorChannelLiveFragmentLayoutTest에 CTA visibility callback과 replay list bottom padding 계약을 추가한다.CreatorChannelActivitySourceTest에 owner CTA visibility 변경 시 Live Fragment로 CTA 표시 여부를 전달하는 계약을 추가한다.
- 검증 명령:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:mergeDebugResources./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:compileDebugKotlin
- 기대 결과:
- 본인 채널 Live 탭에서 하단 CTA가 표시되어도 다시듣기 마지막 item 전체가 CTA 위로 스크롤되어 보인다.
- 검증 기록:
- 실제 원인 확인과 검증 결과를 이 Task 아래에 한국어로 누적 기록한다.
- 2026-06-18: 원인 확인 결과, Phase 6 Follow-up에서
라이브 시작하기CTA를 Activity root overlay로 이동하면서 기존 Fragment 내부RecyclerView하단 padding 보정이 제거됐고, Activity의NestedScrollViewbottom padding에만 의존하게 됐다. Phase 8 Follow-up에서 item 간격과 empty 높이를 보정한 뒤에도 실제 마지막 다시듣기 item을 소유한RecyclerView에는 CTA 높이만큼의 list bottom padding이 없어 owner CTA 아래에서 마지막 item이 잘릴 수 있음을 확인했다. - 2026-06-18: RED 확인으로
CreatorChannelActivitySourceTest와CreatorChannelLiveFragmentLayoutTest에 owner CTA visibility를 Live Fragment로 전달하고, Live Fragment가 replay list bottom padding을 기본 32dp 또는 CTA 표시 시 132dp로 적용하는 계약을 추가했다. production 보정 전 실행한./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"는CreatorChannelActivitySourceTest2건,CreatorChannelLiveFragmentLayoutTest1건이 실패해 RED를 확인했다. - 2026-06-18:
CreatorChannelActivity.updateLiveOwnerCtaVisibility()가findLiveFragment()?.onCreatorChannelLiveOwnerCtaVisibilityChanged(shouldShowLiveOwnerCta)를 호출하도록 변경하고, 바깥NestedScrollViewbottom padding 조정은 제거했다.CreatorChannelLiveFragment에는onCreatorChannelLiveOwnerCtaVisibilityChanged(isVisible)를 추가해rvCreatorChannelLiveReplaysbottom padding을 CTA 미표시 32dp, CTA 표시 132dp로 갱신하도록 했다. - 2026-06-18: 보정 후
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 결과BUILD SUCCESSFUL in 1m 11s로 통과했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:mergeDebugResources실행 결과BUILD SUCCESSFUL in 14s로 통과했다. - 2026-06-18:
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 17s로 통과했다. - 2026-06-18: 추가 검증으로
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process :app:ktlintCheck실행 결과BUILD SUCCESSFUL in 14s로 통과했다. 기존.editorconfig의disabled_rulesdeprecation 경고는 계속 출력됐다.
- 수정:
Verification Log
-
2026-06-17: Phase 1 진행.
CreatorChannelHomeApi/CreatorChannelHomeRepository를CreatorChannelApi/CreatorChannelRepository로 rename하고 기존 홈 endpoint/repository method 동작은 유지했다. -
2026-06-17:
./gradlew :app:compileDebugKotlinPASS. 최초 병렬 실행은 KSP incremental cache 손상으로 실패했으나app/build/kspCaches/debug생성물 캐시 삭제 후 순차 재실행에서 통과했다. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"PASS. -
2026-06-17: Phase 2 진행. 라이브 탭 DTO/API/Repository/ViewModel 계약과 RED/GREEN 테스트를 추가했다. UI/layout/mapper/Fragment/tab 연결은 Phase 3 이후 범위라 변경하지 않았다.
-
2026-06-17: RED 확인. production 구현 전 live ViewModel 테스트 실행 시
ContentSort,CreatorChannelLiveTabResponse,CreatorChannelLiveViewModel,CreatorChannelRepository.getLive미구현으로:app:compileDebugUnitTestKotlin FAILED가 발생했다. -
2026-06-17: 추가 RED 확인. 최초 로드 전
changeSort(POPULAR)호출이 기본LATEST최초 로드 계약을 오염시키는 실패를 확인한 뒤 guard를 적용했다. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"PASS. -
2026-06-17:
./gradlew :app:compileDebugKotlinPASS. -
2026-06-17: 리뷰 게이트 지적으로 stale async response 보강. production 보정 전 stale first-page/load-more 테스트 2개가 실패했고,
requestGenerationguard 적용 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS. -
2026-06-17: stale async response 보강 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"PASS. -
2026-06-17: stale async response 보강 후
./gradlew :app:compileDebugKotlinPASS. -
2026-06-17: 리뷰 코멘트 반영. Figma
290:8959기준 전체 empty 상태는 Sort-bar/list 없이 중앙 문구를 표시하고, 본인 채널 하단 CTA는 유지하는 것으로 PRD/계획 문서에 기록했다. -
2026-06-17: 같은
creatorId의loadLive()재호출 상태 보존 RED 테스트를 추가했다. 보정 전 테스트 실패 확인 후 기존 상태가 있으면 no-op 하도록 구현했고,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"PASS. -
2026-06-17: 리뷰 코멘트 반영 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS. -
2026-06-17: 리뷰 코멘트 반영 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"PASS. 병렬 실행 중 Robolectric 임시 DataStore 정리 경고가 한 차례 출력되었으나, 동일 명령 순차 재실행 결과BUILD SUCCESSFUL로 통과했다. -
2026-06-17: 리뷰 코멘트 반영 후
./gradlew :app:compileDebugKotlinPASS. -
2026-06-17: 리뷰 코멘트 반영.
loadLive()의 같은creatorId상태 보존 guard는 유지하고, Error 상태 재시도는 명시적retryLive()로 분리했다. production 보정 전retryLive미구현 컴파일 실패를 확인했고, 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest"PASS. -
2026-06-17: Phase 4 UI 작업에 error 안내 문구 아래 retry 버튼을 표시하고, retry 버튼이
CreatorChannelLiveViewModel.retryLive()를 호출한다는 요구사항을 추가했다. -
2026-06-17:
retryLive()추가 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS. -
2026-06-17:
retryLive()추가 후./gradlew :app:compileDebugKotlinPASS. -
2026-06-17:
retryLive()추가 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest"최초 병렬 실행에서채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다가 실패했으나, 변경 범위와 무관한 SharedPreferenceManager 상태 간섭 가능성을 분리하기 위해 동일 명령을 순차 재실행했고BUILD SUCCESSFUL로 통과했다. -
2026-06-17: 전체 회귀 명령 실행. 최초 sandbox 실행은
~/.gradlewrapper lock 접근 제한으로 실패해 권한 승인 후 Gradle 명령을 재실행했다. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS. -
2026-06-17:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"FAIL.CreatorChannelActivitySourceTest의 기존 XML/source 문자열 검증 3개가 실패했다:activity_creator_channel.xml의ic_new_talk/ic_new_dm기대,item_creator_channel_home_donation.xml의196dp기대,item_creator_channel_home_series_content.xml의android:text="Only"기대. -
2026-06-17:
./gradlew :app:mergeDebugResourcesPASS. -
2026-06-17:
./gradlew :app:compileDebugKotlinPASS. -
2026-06-17:
./gradlew :app:ktlintCheck최초 실행에서 신규 live test 줄바꿈과CreatorChannelLiveModels.kt파일명 규칙 위반으로 FAIL. 테스트 helper 줄바꿈을 정리하고 응답 모델 파일명을CreatorChannelLiveTabResponse.kt로 변경한 뒤 재실행해 PASS. -
2026-06-17: ktlint 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS. -
2026-06-17: ktlint 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"재실행 결과 동일한CreatorChannelActivitySourceTest3개 실패가 재현됐다. -
2026-06-17: Phase 3 진행. 라이브 다시듣기 UI model/mapper와 mapper RED/GREEN 테스트를 추가했다. Fragment/layout/sort popup/tab 연결은 Phase 4 이후 범위라 변경하지 않았다.
-
2026-06-17: Phase 3 RED 확인. production mapper 구현 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"실행 시CreatorChannelLiveReplayStatus,toReplayUiModel,toLabelResId미구현으로:app:compileDebugUnitTestKotlin FAILED가 발생했다. -
2026-06-17: Phase 3 GREEN 확인.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"PASS. -
2026-06-17: Phase 3 회귀 검증.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS. ktlint 실행 중 기존.editorconfig의disabled_rulesdeprecation 경고가 출력되었다. -
2026-06-17: Phase 3 리뷰 게이트 보정.
LATESTlabel은최신순리소스screen_audio_content_sort_newest로,OWNEDlabel은 신규 다국어 리소스creator_channel_live_sort_owned로 변경했다. 보정 후./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS. -
2026-06-17: Phase 4 진행.
CreatorChannelLiveFragment,CreatorChannelLiveReplayAdapter,fragment_creator_channel_live.xml,item_creator_channel_live_replay.xml, 최소 배경 drawable/string, layout RED/GREEN 테스트를 추가했다. Sort popup/정렬 변경 동작과 owner CTA 실제 노출/inset/라이브 시작 연결은 Phase 5/6 범위로 남겼다. -
2026-06-17: Phase 4 RED 확인. production layout/code 추가 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"실행 시 신규 layout/id/source 미존재로:app:compileDebugUnitTestKotlin FAILED가 발생했다. -
2026-06-17: Phase 4 GREEN 확인.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS. -
2026-06-17: Phase 4 회귀 검증.
./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS,./gradlew :app:ktlintCheckPASS. Gradle 병렬 실행 중 Kotlin daemon/incremental cache 경합 로그가 일부 출력되었으나 fallback/최종 결과는BUILD SUCCESSFUL이었다. 기존.editorconfig의disabled_rulesdeprecation 경고와 Kotlin/Android deprecation 경고는 변경 범위와 무관하게 계속 출력된다. -
2026-06-17: Phase 4 리뷰 보정 진행.
NestedScrollView가 스크롤을 소유하는 구조에서 Live pagination을 Fragment 내부RecyclerView.OnScrollListener가 아니라CreatorChannelActivity의 parent scroll bottom detection으로 전달하도록 보정했다. Live content bind 후onCreatorChannelLiveContentChanged()로ViewPager2높이 재측정을 요청하고, 현재 라이브 시간은 홈 탭의formatCreatorChannelLiveDateTime()을 재사용하도록 변경했다. Owned/Rented 다시듣기 item은 PRD 요구대로 play icon과 상태 텍스트를 함께 표시한다. -
2026-06-17: Phase 4 리뷰 보정 RED 확인. production 보정 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"는 신규 Live fragment source 계약 미충족으로 FAIL,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"는 신규 NestedScrollView pagination/height callback 계약 미충족으로 FAIL했다. 같은 실행에서 기존 오디오 상세 이동 source assertion도startAudioContentDetail()helper 추출 이후의 현재 코드와 맞지 않아 테스트 계약만 갱신했다. -
2026-06-17: Phase 4 리뷰 보정 GREEN 확인. 병렬 Gradle 실행 중 Kotlin daemon/incremental cache 경합으로
Constants.class누락 및 fallback timeout이 발생했으나, daemon 정리 후 순차 재실행한./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL로 통과했다. 이어서./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS. -
2026-06-17: Phase 4 리뷰 보정 최종 검증.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS,./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:ktlintCheckPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS.ktlintCheck에서는 기존.editorconfig의disabled_rulesdeprecation 경고가 계속 출력되었으나 실패 원인은 아니었다. -
2026-06-17: Phase 4 리뷰 게이트에서
CreatorChannelPagerAdapter가 아직 Live 탭을CreatorChannelPlaceholderFragment로 생성해 Live 보정 코드가 실제 사용자 경로에서 실행되지 않는다는 차단 이슈가 발견됐다. 최소 보정으로CreatorChannelTab.Live -> CreatorChannelLiveFragment.newInstance(creatorId)분기를 추가하고, 기존 placeholder-only source test를 홈/라이브 실제 Fragment + 후속 탭 placeholder 계약으로 갱신했다. -
2026-06-17: Live pager 연결 보정 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS.ktlintCheck에서는 기존.editorconfig의disabled_rulesdeprecation 경고가 계속 출력되었다. -
2026-06-17: 리뷰 지적 2건 보정. Live 탭 API는 Live 탭 선택 시점 lazy load로 변경했고, Empty/Error는 Live page root와 Activity page minimumHeight 보정으로 탭 viewport 중앙 정렬을 보장했다. 최종 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS,./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS. 초기 병렬 RED 확인 중 Kotlin incremental cache 경합 로그가 출력됐으나 daemon 정리 후 순차 검증은 모두 통과했다. -
2026-06-17: 추가 리뷰 지적 2건 보정.
ViewPager2.offscreenPageLimit을 복구해 Home 탭의HomeActionDelegate기반 상단 액션/refresh 경로를 유지하고, Live load-more 성공 시 첫 페이지의 current live/count/sort metadata를 보존하도록 변경했다. 검증으로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS.ktlintCheck에서는 기존.editorconfig의disabled_rulesdeprecation 경고가 계속 출력되었다. -
2026-06-17: Phase 5 코드 리뷰 및 검증. Figma
290:9041컨텍스트 메뉴와 스크린샷을 재확인해gray_900배경,gray_700stroke,radius_14, 선택 rowgray_800, 16sp medium, 12/8 padding 기준이 구현에 반영됐음을 확인했다. PRD/계획의 서버 정렬 계약은ContentSort5개 옵션이므로 Figma의추천순row는 이번 범위에서 제외했다. -
2026-06-17: Phase 5 코드 리뷰에서 수정 필요 결함은 발견하지 못했다.
CreatorChannelLiveSortPopup은 외부 터치 dismiss, 같은 정렬 재선택 dismiss-only, 새 정렬 선택 시viewModel.changeSort(sort)전달, 우측 화면 밖 보정,onDestroyView()dismiss 경로를 갖는다. -
2026-06-17: Phase 5 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveFragmentLayoutTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*"PASS,./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS를 확인했다. -
2026-06-17: 추가 상위 회귀 검증
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"최초 실행은 drawable 3개 추가 후 resource id 캐시가 맞지 않아CreatorChannelHomeMapperTest,CreatorChannelTitleBarStateTest의 drawable id assertion이 3씩 밀려 실패했다. 해당 클래스들을--rerun-tasks로 강제 재컴파일하자 통과했고, 이후 동일 채널 전체 회귀 명령을 순차 재실행해BUILD SUCCESSFUL로 통과했다. 병렬--rerun-tasks중 Kotlin incremental cache 경합 로그가 출력됐으나 최종 순차 검증은 통과했다. -
2026-06-18: Phase 8 자동 검증 실행.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Live*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck모두BUILD SUCCESSFUL로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다. -
2026-06-18: Phase 8 수동 검증 시도. 연결 단말과 설치 패키지는 확인했으나
CreatorChannelActivity직접 실행은 exported 제한으로 차단됐고, 런처 실행은 TedPermissionActivity 권한 화면까지만 확인했다. Live 탭 실제 화면/Figma 대조는 후속 로그인/권한/서버 데이터가 준비된 단말 시나리오에서 계속 확인해야 한다. -
2026-06-18: Phase 6 Follow-up 진행.
라이브 시작하기CTA를 Fragment 내부 스크롤 컨텐츠에서 Activity root overlay로 이동해 Figma665:19371기준 100dp black 하단 고정 영역과 14dp inset cyan capsule 버튼 구조로 보정했다. RED/GREEN 확인으로CreatorChannelLiveFragmentLayoutTest실패 후 통과,mergeDebugResourcesPASS,compileDebugKotlinPASS,ktlintCheckPASS를 확인했다. 병렬 Gradle 실행 중 Kotlin incremental cache 경합은 daemon stop 후 순차 재실행으로 해소했다. -
2026-06-18: Phase 6 Follow-up RF6.2 진행.
BaseActivityroot bottom padding과 Activity 하단 UI navigation inset의 중복 적용을 제거해 실제 기기에서 preview보다 위로 뜨는 문제를 보정했다. Live 탭 empty/error 짧은 상태는ViewPager2page minimum height 강제와fragment_creator_channel_live.xmlrootmatch_parent를 제거해 불필요한 긴 스크롤이 생기지 않도록 했다. targeted RED/GREEN 테스트,mergeDebugResources,compileDebugKotlin,ktlintCheck가 최종 통과했다. -
2026-06-18: Live API 기본
size를 20으로 변경한 뒤 연결 지점을 점검했다.CreatorChannelLiveViewModel.DEFAULT_PAGE_SIZE를 참조하는 테스트 bytecode가 기존const val값 10을 inline해 mock stub이 production 호출size=20과 불일치하는 문제를 확인했고, 기본값을 runtimeval로 바꿔 stale inline 재발 가능성을 낮췄다. 문서의 초기 로드 기록도page=0,size=20,sort=LATEST로 보정했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLivePaginationTest"실행 결과BUILD SUCCESSFUL in 27s로 통과했다. -
2026-06-18: Live API 기본
size=20보정 후./gradlew :app:compileDebugKotlin실행 결과BUILD SUCCESSFUL in 1s로 통과했다. Gradle 9.0 호환성 deprecation 경고는 기존 빌드 설정 경고로 출력됐다.