Files
sodalive-android/docs/20260617_크리에이터_채널_라이브_탭/plan-task.md

52 KiB

크리에이터 채널 라이브 탭 구현 계획/TASK

For agentic workers: 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시 - [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.

Goal: GET /api/v2/creator-channels/{creatorId}/live 응답을 기반으로 크리에이터 채널의 라이브 탭에 현재 라이브, 라이브 다시듣기 목록, 정렬, pagination, 본인 채널 전용 하단 라이브 시작하기 CTA를 표시한다.

Architecture: 기존 CreatorChannelActivityViewPager2/CreatorChannelPagerAdapter 구조를 유지하고, CreatorChannelTab.Live의 placeholder를 신규 CreatorChannelLiveFragment로 교체한다. 라이브 탭 전용 Fragment/ViewModel/mapper/UI model/adapter/popup은 kr.co.vividnext.sodalive.v2.creator.channel.live 하위에 둔다. API/Repository는 홈 탭과 라이브 탭이 함께 쓰는 채널 공통 계층으로 보고 기존 CreatorChannelHomeApi/CreatorChannelHomeRepositoryCreatorChannelApi/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
  • 별도 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
  • 첫 페이지 page0이다.
  • 다음 페이지는 마지막 성공 응답의 page + 1로 요청한다.
  • ContentSort 기본값은 LATEST이다.
  • isOwned == trueisRented == true가 동시에 내려오면 소장중을 우선 표시한다.
  • seriesName은 라이브 다시듣기 item에 표시하지 않는다.
  • isFirstContent, isOriginalSeries는 기존 오디오 item 정책과 동일하게 매핑한다.
  • currentLive == null이고 liveReplayContents.isEmpty()인 전체 empty 상태에서는 Sort-bar, 현재 라이브 카드, 라이브 다시듣기 리스트를 제거하고 중앙에 크리에이터가 라이브를 준비 중입니다.\n기대해 주세요! 문구를 표시한다.
  • 전체 empty 상태가 본인 채널에 표시되는 경우에도 empty 문구는 동일하게 표시하고, 하단 라이브 시작하기 CTA는 본인 채널 정책에 따라 계속 표시한다.
  • 같은 creatorIdloadLive()가 재호출되면 Fragment/View 재생성 또는 탭 재바인딩으로 보고 기존 ViewModel 상태를 유지한다. 명시적 새로고침은 후속 필요 시 별도 refresh API로 분리한다.
  • 최초 조회 실패로 Error 상태인 경우 retryLive()를 통해 현재 creatorId의 첫 페이지를 다시 요청한다.
  • 전체 error 상태에서는 안내 문구 아래 retry 버튼을 표시하고, retry 버튼은 CreatorChannelLiveViewModel.retryLive()를 호출한다.
  • drawable 리소스는 기존 파일을 재사용한다.
    • ic_new_sort
    • ic_new_shield_small
    • ic_new_player_play
    • ic_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 상태는 Figma 290:8949, 290:8950, 290:8954, 290:8956, 290:8959를 기준으로 구현한다.
  • Phase 5: 필수 참조
    • 정렬 컨텍스트 메뉴는 Figma 290:9041 기준으로 구현한다.
  • Phase 6: 필수 참조
    • 본인 채널 하단 CTA는 Figma 665:19359, 665:19371 기준으로 구현한다.
  • Phase 7: 부분 참조
    • 탭 연결, 화면 이동, pagination 동작은 기존 코드 패턴 중심으로 검증한다.
  • Phase 8: 필수 참조
    • 최종 수동 화면 검증은 PRD의 모든 Figma 노드와 실제 화면을 대조한다.

파일 구조

  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • CreatorChannelTab.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.kt
    • CreatorChannelAudioContentResponse에 라이브 탭 전용 필드 isAdult, isOwned, isRented를 서버 계약에 맞춰 추가한다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/common/data/ContentSort.kt
    • v2 API 공용 정렬 enum으로 ContentSort를 둔다.
  • 생성: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt
    • CreatorChannelLiveTabResponse를 크리에이터 채널 라이브 탭 응답 전용 모델로 둔다.
  • 생성 후보: 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 문구를 추가한다.
  • 수정: 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.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt
    • app/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.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeMappers.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
    • 작업:
      • CreatorChannelTab.Live가 현재 placeholder로 연결되는 지점을 확인한다.
      • 본인 여부(isOwner)가 Activity와 탭 Fragment로 전달되는 기존 경로를 확인한다.
      • 라이브 시작 진입이 기존 CreatorChannelActivityonOwnerFabLiveClicked() 또는 LiveRoomCreateActivity 경로를 재사용할 수 있는지 확인한다.
    • 검증:
      • 라이브 탭 구현이 홈 탭 section adapter를 변경하지 않고 독립 Fragment로 추가 가능한지 기록한다.
    • 검증 기록:
      • 2026-06-17: CreatorChannelPagerAdapter는 현재 CreatorChannelTab.HomeCreatorChannelHomeFragment.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로 처리됨을 확인했다.
  • Task 1.2: 기존 오디오 item 정책과 drawable/string 리소스 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeAudioContentCardView.kt
      • app/src/main/res/layout/item_creator_channel_home_audio_content.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/AudioContentTag.kt
      • app/src/main/res/drawable-mdpi/ic_new_sort.png
      • app/src/main/res/drawable-mdpi/ic_new_shield_small.png
      • app/src/main/res/drawable-mdpi/ic_new_player_play.png
      • app/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는 기존 홈 카드에서 durationseriesName을 조합하지만, 라이브 다시듣기 item에서는 PRD/계획 기준에 따라 seriesName을 표시하지 않는 것으로 재확인했다.
      • 2026-06-17: AudioContentTag의 top tag 순서는 Original, First, bottom tag 순서는 Point, Free로 정의되어 있음을 확인했다.
      • 2026-06-17: test -fapp/src/main/res/drawable-mdpi/ic_new_sort.png, ic_new_shield_small.png, ic_new_player_play.png, ic_new_create_live.png가 모두 존재함을 확인했다.
  • 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.kt
      • 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/CreatorChannelHomeViewModel.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModelTest.kt
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
      • 기타 CreatorChannelHomeApi, CreatorChannelHomeRepository import 참조 파일
    • 작업:
      • class/interface 이름을 CreatorChannelApi, CreatorChannelRepository로 변경한다.
      • 기존 홈 탭 동작은 변경하지 않는다.
      • 별도 CreatorChannelLiveApi, CreatorChannelLiveRepository는 만들지 않는다.
    • 검증 명령:
      • ./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 참조를 갱신했다. 홈 endpoint GET /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로 통과했다.

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.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt
    • 작업:
      • CreatorChannelLiveTabResponse를 라이브 탭 data/CreatorChannelLiveTabResponse.kt@Keep, @SerializedName 기반 data class로 추가한다.
      • ContentSort enum에 LATEST, POPULAR, OWNED, PRICE_HIGH, PRICE_LOW를 추가하고 v2.common.data 패키지에 둔다.
      • 기존 CreatorChannelAudioContentResponseisAdult, isOwned, isRented 필드를 추가할 때 홈 탭 API 하위 호환성이 깨지지 않는지 확인한다.
      • 하위 호환이 필요하면 라이브 탭 전용 DTO 분리 또는 nullable/default 정책을 테스트로 고정한다.
    • 검증 명령:
      • ./gradlew :app:compileDebugKotlin
    • 기대 결과:
      • DTO 추가 후 컴파일된다.
    • 검증 기록:
      • 2026-06-17: CreatorChannelHomeModels.ktCreatorChannelLiveTabResponseContentSort(LATEST, POPULAR, OWNED, PRICE_HIGH, PRICE_LOW)를 추가했다. 기존 홈 탭 테스트 fixture의 positional constructor 호환을 유지하기 위해 CreatorChannelAudioContentResponse의 라이브 탭 전용 isAdult, isOwned, isRented 필드는 기본값이 있는 trailing field로 추가했다.
      • 2026-06-17: ./gradlew :app:compileDebugKotlin 실행 결과 BUILD SUCCESSFUL로 통과했다.
  • Task 2.1.1: 라이브 탭 응답 모델 파일 경계 분리

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/common/data/ContentSort.kt
      • app/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.kt
      • docs/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
    • 기대 결과:
      • ContentSortv2/common/data/ContentSort.kt에 정의된다.
      • CreatorChannelLiveTabResponsev2/creator/channel/live/data/CreatorChannelLiveTabResponse.kt에 정의된다.
      • 모델 파일 분리 후 Kotlin 컴파일이 PASS한다.
    • 검증 기록:
      • 2026-06-17: 사용자 리뷰에 따라 CreatorChannelLiveTabResponseContentSortCreatorChannelHomeModels.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가 ~/.gradle lock 파일 생성을 막아 실패했다. 동일 명령을 권한 승인 후 재실행해 BUILD SUCCESSFUL in 24s로 통과했다.
      • 2026-06-17: 사용자 추가 지시에 따라 ContentSortv2.common.data.ContentSort, CreatorChannelLiveTabResponsev2.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가 남지 않았고, ContentSortv2.common.data, CreatorChannelLiveTabResponsev2.creator.channel.live.data 참조로 갱신됐음을 확인했다.
      • 2026-06-17: ./gradlew :app:compileDebugKotlin 최초 실행은 sandbox가 ~/.gradle lock 파일 생성을 막아 실패했다. 동일 명령을 권한 승인 후 재실행해 BUILD SUCCESSFUL in 12s로 통과했다.
      • 2026-06-17: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*" 실행 결과 BUILD SUCCESSFUL in 32s로 통과했다.
  • Task 2.2: 라이브 탭 endpoint와 Repository method 추가

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
    • 작업:
      • @GET("/api/v2/creator-channels/{creatorId}/live") endpoint를 추가한다.
      • query parameter page, size, sort를 전달한다.
      • sortContentSort.name 또는 Retrofit enum 변환의 기존 정책을 확인해 서버 값이 LATEST 등 대문자로 나가도록 한다.
    • 검증 명령:
      • ./gradlew :app:compileDebugKotlin
    • 기대 결과:
      • API/Repository가 기존 Koin graph와 충돌 없이 컴파일된다.
    • 검증 기록:
      • 2026-06-17: CreatorChannelApiGET /api/v2/creator-channels/{creatorId}/live endpoint를 추가하고 page, size, sort query와 Authorization header를 전달하도록 했다. CreatorChannelRepository.getLive()는 별도 live repository를 만들지 않고 기존 공통 repository에서 API를 얇게 위임한다.
      • 2026-06-17: sortContentSort enum query로 전달해 LATEST 등 enum name이 Retrofit query 값으로 사용되도록 했다.
      • 2026-06-17: ./gradlew :app:compileDebugKotlin 실행 결과 BUILD SUCCESSFUL로 통과했다.
  • Task 2.3: ViewModel RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.kt
      • app/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: CreatorChannelLiveViewModelTestCreatorChannelLivePaginationTest를 추가했다. 최초 로딩 page=0/sort=LATEST, hasNext 기반 page + 1 pagination, 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: 리뷰 코멘트에 따라 같은 creatorIdloadLive()가 재호출되면 기존 목록/page/load-more 결과를 유지해야 한다는 테스트를 추가했다. production 보정 전 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveViewModelTest" 실행 결과 해당 테스트가 실패함을 확인했다.
  • 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=10, 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: requestGeneration guard를 추가해 오래된 첫 페이지/load-more 성공 또는 실패 응답을 무시하도록 보정했다. 보정 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*" 실행 결과 BUILD SUCCESSFUL로 통과했다.
      • 2026-06-17: AppDICreatorChannelLiveViewModel(get()) binding을 추가했다. 기존 CreatorChannelApi/CreatorChannelRepository binding은 재사용했다.
      • 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로 통과했다.

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/isOriginalSeries tag 정책, ContentSort label 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를 확인했다.
  • Task 3.2: UI model/mapper 구현

    • 생성 후보:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.kt
      • app/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를 매핑한다. secondaryTextduration만 사용해 seriesName을 표시하지 않는다.
      • 2026-06-17: ContentSort label은 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로 통과했다. 기존 .editorconfigdisabled_rules deprecation 경고는 출력되었으나 Phase 3 변경과 무관하다.
      • 2026-06-17: 리뷰 게이트에서 LATEST최신 콘텐츠, OWNED소장중 리소스에 매핑되는 문제가 지적되어 LATESTscreen_audio_content_sort_newest, OWNED는 신규 creator_channel_live_sort_owned로 보정했다.
      • 2026-06-17: label 보정 후 ./gradlew :app:mergeDebugResources PASS, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest" PASS, ./gradlew :app:compileDebugKotlin PASS, ./gradlew :app:ktlintCheck PASS. 병렬 실행 중 :app:dataBindingGenBaseClassesDebug가 Gradle 생성물 경합으로 R-def.txt 누락 실패했으나 동일 compile 명령 순차 재실행에서 PASS했다.

Phase 4: 라이브 탭 Fragment와 목록 UI 구현

  • Task 4.1: Fragment layout과 replay item layout 추가

    • 생성:
      • app/src/main/res/layout/fragment_creator_channel_live.xml
      • app/src/main/res/layout/item_creator_channel_live_replay.xml
    • 수정:
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/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에 성공한다.
  • Task 4.2: Adapter와 Fragment 구현

    • 생성:
      • 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/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가 컴파일된다.
  • 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한다.

Phase 5: 정렬 컨텍스트 메뉴 구현

  • Task 5.1: Sort popup 구현

    • 생성 후보:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveSortPopup.kt
      • app/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에 정렬 변경을 전달한다.
    • 검증:
      • 작은 화면에서 메뉴가 화면 밖으로 벗어나지 않도록 위치 보정 기준을 코드에 남긴다.
  • Task 5.2: Sort 동작 테스트 보강

    • 수정:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModelTest.kt
      • app/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한다.

Phase 6: 본인 채널 하단 CTA와 라이브 시작 진입 연결

  • Task 6.1: 본인 CTA 노출과 inset 처리 구현

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt
      • app/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 코드가 컴파일된다.
  • Task 6.2: 기존 라이브 시작 플로우 연결

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/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의 검증 기록에 남긴다.

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 정책을 유지한다.
    • 검증 명령:
      • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"
    • 기대 결과:
      • Live 탭만 실제 Fragment로 연결되고 나머지 placeholder 정책은 유지된다.
  • Task 7.2: DI와 lifecycle 통합

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
      • app/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한다.

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 아래에 누적 기록한다.
  • 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 아래에 한국어로 누적 기록한다.

Verification Log

  • 2026-06-17: Phase 1 진행. CreatorChannelHomeApi/CreatorChannelHomeRepositoryCreatorChannelApi/CreatorChannelRepository로 rename하고 기존 홈 endpoint/repository method 동작은 유지했다.
  • 2026-06-17: ./gradlew :app:compileDebugKotlin PASS. 최초 병렬 실행은 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:compileDebugKotlin PASS.
  • 2026-06-17: 리뷰 게이트 지적으로 stale async response 보강. production 보정 전 stale first-page/load-more 테스트 2개가 실패했고, requestGeneration guard 적용 후 ./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:compileDebugKotlin PASS.
  • 2026-06-17: 리뷰 코멘트 반영. Figma 290:8959 기준 전체 empty 상태는 Sort-bar/list 없이 중앙 문구를 표시하고, 본인 채널 하단 CTA는 유지하는 것으로 PRD/계획 문서에 기록했다.
  • 2026-06-17: 같은 creatorIdloadLive() 재호출 상태 보존 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:compileDebugKotlin PASS.
  • 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:compileDebugKotlin PASS.
  • 2026-06-17: retryLive() 추가 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest" 최초 병렬 실행에서 채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다가 실패했으나, 변경 범위와 무관한 SharedPreferenceManager 상태 간섭 가능성을 분리하기 위해 동일 명령을 순차 재실행했고 BUILD SUCCESSFUL로 통과했다.
  • 2026-06-17: 전체 회귀 명령 실행. 최초 sandbox 실행은 ~/.gradle wrapper 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.xmlic_new_talk/ic_new_dm 기대, item_creator_channel_home_donation.xml196dp 기대, item_creator_channel_home_series_content.xmlandroid:text="Only" 기대.
  • 2026-06-17: ./gradlew :app:mergeDebugResources PASS.
  • 2026-06-17: ./gradlew :app:compileDebugKotlin PASS.
  • 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:compileDebugKotlin PASS, ./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.*" 재실행 결과 동일한 CreatorChannelActivitySourceTest 3개 실패가 재현됐다.
  • 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:compileDebugKotlin PASS, ./gradlew :app:ktlintCheck PASS. ktlint 실행 중 기존 .editorconfigdisabled_rules deprecation 경고가 출력되었다.
  • 2026-06-17: Phase 3 리뷰 게이트 보정. LATEST label은 최신순 리소스 screen_audio_content_sort_newest로, OWNED label은 신규 다국어 리소스 creator_channel_live_sort_owned로 변경했다. 보정 후 ./gradlew :app:mergeDebugResources PASS, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.CreatorChannelLiveMapperTest" PASS, ./gradlew :app:compileDebugKotlin PASS, ./gradlew :app:ktlintCheck PASS.