589 lines
48 KiB
Markdown
589 lines
48 KiB
Markdown
# 크리에이터 채널 시리즈 탭 구현 계획/TASK
|
||
|
||
> **For agentic workers:** 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
|
||
|
||
**Goal:** `GET /api/v2/creator-channels/{creatorId}/series` 응답을 기반으로 크리에이터 채널의 `시리즈` 탭에 정렬, 시리즈 목록, 조건부 소장 진행 정보, empty 상태와 pagination을 표시한다.
|
||
|
||
**Architecture:** 기존 `CreatorChannelActivity`의 `ViewPager2`/`CreatorChannelPagerAdapter` 구조를 유지하고, `CreatorChannelTab.Series`의 placeholder를 신규 `CreatorChannelSeriesFragment`로 교체한다. 시리즈 탭 전용 Fragment/ViewModel/DTO/mapper/adapter는 `kr.co.vividnext.sodalive.v2.creator.channel.series` 하위에 두되, API/Repository는 기존 채널 공통 `CreatorChannelApi`/`CreatorChannelRepository`에 endpoint만 추가한다. 정렬 UI는 오디오 탭에서 사용하는 `CreatorChannelSortPopup`과 `ContentSort.toLabelResId()`를 재사용하고, 목록 하단 감지와 ViewPager 높이 갱신은 기존 Live/Audio 탭 패턴에 Series를 추가한다.
|
||
|
||
**Tech Stack:** Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
|
||
|
||
---
|
||
|
||
## 전제와 성공 기준
|
||
- PRD: `docs/20260620_크리에이터_채널_시리즈_탭/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/Repository:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
||
- 기존 시리즈 상세 진입:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailActivity.kt`
|
||
- `Constants.EXTRA_SERIES_ID`
|
||
- 기존 오디오 탭 참조:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/CreatorChannelAudioFragment.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/CreatorChannelAudioViewModel.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/model/CreatorChannelAudioMappers.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelSortPopup.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelSortModels.kt`
|
||
- 기존 홈 시리즈 item 참조:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSeriesCardView.kt`
|
||
- `app/src/main/res/layout/item_creator_channel_home_series.xml`
|
||
- `app/src/main/res/layout/item_creator_channel_home_series_content.xml`
|
||
- Figma:
|
||
- 전체: `290:9031`
|
||
- 시리즈 item: `290:9036`
|
||
- 시리즈 콘텐츠 소장률: `290:9038`
|
||
- API endpoint는 `GET /api/v2/creator-channels/{creatorId}/series`이다.
|
||
- 첫 페이지 `page`는 `0`, 기본 `size`는 `20`, 기본 `sort`는 `ContentSort.LATEST`이다.
|
||
- 정렬 옵션은 `ContentSort.LATEST`, `ContentSort.POPULAR`, `ContentSort.OWNED`, `ContentSort.PRICE_HIGH`, `ContentSort.PRICE_LOW` 5개만 표시한다.
|
||
- Figma에 보이는 `추천순`은 표시하지 않는다.
|
||
- `coverImageUrl`은 시리즈 썸네일 이미지로 사용한다.
|
||
- 시리즈 item 우측의 `전체소장`/play button 영역은 표시하지 않는다.
|
||
- 썸네일처럼 크기 제한이 필요한 영역 외에는 불필요한 고정 width/height를 만들지 않는다.
|
||
- 내 채널이 아닌 경우에만 `purchasedContentCount`, `paidContentCount`, `purchasedPaidContentRate` 기반 소장 진행 정보를 표시한다.
|
||
- 내 채널인 경우 item에는 제목, 발행 요일, 총 콘텐츠 수, 연재/완결 상태만 표시한다.
|
||
- 시리즈 empty 문구는 다음 다국어 문자열 리소스로 제공한다.
|
||
- 한국어: `크리에이터가 시리즈를 준비 중입니다.\n기대해 주세요!`
|
||
- 영어: `The creator is preparing a series.\nPlease look forward to it!`
|
||
- 일본어: `クリエイターがシリーズを準備中です。\n楽しみにお待ちください!`
|
||
- 구현 완료 후 최소 다음 명령을 실행한다.
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"`
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"`
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- `./gradlew :app:ktlintCheck`
|
||
- `git diff --check`
|
||
|
||
---
|
||
|
||
## Figma 참조 필요 Phase
|
||
- Phase 1: 제한 참조
|
||
- 기존 코드 경계, 시리즈 상세 진입, 공통 sort/pagination 패턴 확인이 중심이며 Figma는 PRD 기준만 확인한다.
|
||
- Phase 2: Figma 참조 불필요
|
||
- API/DTO/Repository/ViewModel 상태 모델은 서버 계약과 기존 오디오 탭 패턴을 따른다.
|
||
- Phase 3: 제한 참조
|
||
- mapper는 PRD와 Figma item variant의 정보 표시/소장률 조건을 함께 확인한다.
|
||
- Phase 4: 필수 참조
|
||
- Sort-bar, empty, 시리즈 item layout, 우측 버튼 제거 후 info 영역 확장은 Figma `290:9031`, `290:9036`, `290:9038` 기준으로 구현한다.
|
||
- Phase 5: 제한 참조
|
||
- 탭 연결, pagination, navigation, ViewPager 높이 갱신은 기존 코드 패턴 중심으로 검증한다.
|
||
- Phase 6: 필수 참조
|
||
- 최종 수동 화면 검증은 PRD의 모든 Figma 노드와 실제 화면을 대조한다.
|
||
|
||
---
|
||
|
||
## 파일 구조
|
||
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt`
|
||
- `CreatorChannelTab.Series`를 신규 `CreatorChannelSeriesFragment`로 연결한다.
|
||
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||
- `CreatorChannelSeriesFragment.Host` 구현, 시리즈 탭 선택 시 최초 로드, pagination trigger, ViewPager 높이 갱신, 시리즈 상세 이동을 연결한다.
|
||
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt`
|
||
- 시리즈 탭 endpoint를 추가한다.
|
||
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
||
- 시리즈 탭 repository method를 추가한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/data/CreatorChannelSeriesTabResponse.kt`
|
||
- `CreatorChannelSeriesTabResponse`, `CreatorChannelSeriesResponse`를 정의한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesViewModel.kt`
|
||
- 최초 조회, 정렬 변경, retry, pagination, loading/error/empty/content 상태를 관리한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/model/CreatorChannelSeriesUiModels.kt`
|
||
- series item, possession progress, 화면 상태 UI model을 정의한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/model/CreatorChannelSeriesMappers.kt`
|
||
- DTO를 UI model로 변환하고 subtitle, original/adult tag, owner별 progress 표시 여부를 결정한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesFragment.kt`
|
||
- 시리즈 탭 UI, adapter, 공통 sort popup, retry, pagination error toast, host callback 연결을 담당한다.
|
||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/ui/CreatorChannelSeriesAdapter.kt`
|
||
- 시리즈 목록 RecyclerView adapter를 담당한다.
|
||
- 생성: `app/src/main/res/layout/fragment_creator_channel_series.xml`
|
||
- Sort-bar, RecyclerView, empty/error/retry 영역을 포함한다.
|
||
- 생성: `app/src/main/res/layout/item_creator_channel_series.xml`
|
||
- Figma 시리즈 item을 구현한다. 우측 버튼 영역은 포함하지 않는다.
|
||
- 수정: `app/src/main/res/values/strings.xml`
|
||
- 시리즈 탭 empty/error/retry/상태/소장률 문구를 추가한다.
|
||
- 수정: `app/src/main/res/values-en/strings.xml`, `app/src/main/res/values-ja/strings.xml`
|
||
- 신규 empty/error/retry/상태 문구의 다국어 값을 추가한다.
|
||
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- `CreatorChannelSeriesViewModel` binding을 추가한다.
|
||
- 테스트 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesMapperTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesViewModelTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesPaginationTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesFragmentLayoutTest.kt`
|
||
- 테스트 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||
|
||
---
|
||
|
||
### Phase 1: 기존 구조 확인과 작업 경계 고정
|
||
|
||
- [x] **Task 1.1: 오디오 탭 재사용 경계 확인**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/CreatorChannelAudioFragment.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/CreatorChannelAudioViewModel.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelSortModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelSortPopup.kt`
|
||
- 작업:
|
||
- Series 탭도 `CreatorChannelSortPopup`을 그대로 사용한다.
|
||
- sort option은 `ContentSort.entries`를 그대로 쓰되 enum 5개만 노출되는지 확인한다.
|
||
- 오디오 탭의 `loadMore`, `requestGeneration`, `paginationErrorMessage`, `consumePaginationErrorMessage` 패턴을 시리즈 ViewModel에 동일하게 적용한다.
|
||
- 검증:
|
||
- `rg -n "CreatorChannelSortPopup|toLabelResId|loadMore|paginationErrorMessage" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel`
|
||
- 기대 결과: 공통 sort popup과 오디오 pagination 패턴이 확인된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 확인: `CreatorChannelSortPopup`은 `ContentSort.entries`를 기반으로 option을 구성하고, `ContentSort.toLabelResId()`는 `LATEST`, `POPULAR`, `OWNED`, `PRICE_HIGH`, `PRICE_LOW` 5개 enum만 매핑한다.
|
||
- 2026-06-20 확인: `CreatorChannelAudioViewModel`의 `loadMore`, `requestGeneration`, `paginationErrorMessage`, `consumePaginationErrorMessage` 패턴을 Series ViewModel에 재사용할 경계로 확정했다.
|
||
|
||
- [x] **Task 1.2: 시리즈 상세 진입 경로 확인**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailActivity.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt`
|
||
- 작업:
|
||
- 기존 `onSeriesClicked(series)`가 `SeriesDetailActivity`에 `Constants.EXTRA_SERIES_ID`를 전달하는지 확인한다.
|
||
- 시리즈 탭 item 클릭은 신규 경로를 만들지 않고 같은 Activity method를 재사용한다.
|
||
- 검증:
|
||
- `rg -n "SeriesDetailActivity|EXTRA_SERIES_ID|onSeriesClicked" app/src/main/java`
|
||
- 기대 결과: 기존 시리즈 상세 진입점이 확인된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 확인: `CreatorChannelActivity.onSeriesClicked(series)`가 `SeriesDetailActivity`를 시작하고 `Constants.EXTRA_SERIES_ID`에 `series.seriesId`를 전달한다.
|
||
- 2026-06-20 확인: 시리즈 탭 item 클릭은 신규 상세 이동 경로를 만들지 않고 동일한 `SeriesDetailActivity` + `Constants.EXTRA_SERIES_ID` 경로를 재사용한다.
|
||
|
||
- [x] **Task 1.3: Series 탭 placeholder 연결 지점 확인**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt`
|
||
- 작업:
|
||
- `CreatorChannelTab.Series`가 현재 `CreatorChannelPlaceholderFragment`로 연결되는지 확인한다.
|
||
- 신규 `CreatorChannelSeriesFragment.newInstance(creatorId)`로 교체할 수 있는지 확인한다.
|
||
- 검증:
|
||
- `CreatorChannelPagerAdapterTest`에 Series 탭 연결 테스트를 추가할 준비가 되었는지 기록한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 확인: `CreatorChannelPagerAdapter.createFragment()`는 `Home`, `Live`, `Audio`만 전용 Fragment로 연결하고, `CreatorChannelTab.Series`는 현재 `else` 분기를 통해 `CreatorChannelPlaceholderFragment.newInstance(tab)`로 연결된다.
|
||
- 2026-06-20 확인: `CreatorChannelTab.Series` enum이 존재하므로 Phase 5에서 `CreatorChannelSeriesFragment.newInstance(creatorId)` 분기를 추가하는 테스트를 준비할 수 있다.
|
||
|
||
- [x] **Task 1.4: 기존 시리즈 썸네일/tag 리소스 확인**
|
||
- 확인:
|
||
- `app/src/main/res/layout/item_creator_channel_home_series.xml`
|
||
- `app/src/main/res/layout/item_creator_channel_home_series_content.xml`
|
||
- `app/src/main/res/layout/view_series_original_tag.xml`
|
||
- `app/src/main/res/drawable`
|
||
- 작업:
|
||
- original tag와 adult tag를 기존 리소스로 표현할 수 있는지 확인한다.
|
||
- image placeholder는 기존 시리즈/오디오 콘텐츠 item의 placeholder 정책을 확인해 따른다.
|
||
- 검증:
|
||
- `rg -n "view_series_original_tag|isAdult|adult|original" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/main/res/layout app/src/main/res/drawable`
|
||
- 기대 결과: 기존 tag/placeholder 재사용 경계가 확인된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 확인: 홈 시리즈 item은 `item_creator_channel_home_series_content.xml`에서 `layout_series_original_tag`, `bg_series_original_tag`, `ic_series_original`, `img_new_only`를 사용하고, 공통 `view_series_original_tag.xml`도 존재한다.
|
||
- 2026-06-20 확인: adult badge는 오디오/라이브 채널 item의 `bg_creator_channel_live_adult_badge` 및 adult 표시 패턴을 재사용 후보로 확인했다.
|
||
|
||
---
|
||
|
||
### Phase 2: API/DTO/Repository/ViewModel 계약 추가
|
||
|
||
- [x] **Task 2.1: 시리즈 탭 DTO 추가**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/data/CreatorChannelSeriesTabResponse.kt`
|
||
- 작업:
|
||
- `@Keep`, `@SerializedName` 기반으로 `CreatorChannelSeriesTabResponse`, `CreatorChannelSeriesResponse`를 추가한다.
|
||
- `ContentSort`는 기존 `kr.co.vividnext.sodalive.v2.common.data.ContentSort`를 import해 사용한다.
|
||
- `coverImageUrl` 필드를 포함한다.
|
||
- 홈 API의 `CreatorChannelSeriesResponse`와 이름이 충돌하지 않도록 package import를 명확히 관리한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- 기대 결과:
|
||
- 신규 DTO 추가 후 컴파일이 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelSeriesTabResponse`, `CreatorChannelSeriesResponse`를 `series/data` package에 추가하고 `ContentSort`, `coverImageUrl`, pagination field, progress nullable field를 포함했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
- [x] **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}/series")` endpoint를 추가한다.
|
||
- query parameter `sort`, `page`, `size`를 전달한다.
|
||
- Repository method는 `getSeries(creatorId, page, size, sort, token)` 형태로 둔다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- 기대 결과:
|
||
- API/Repository 추가 후 기존 Koin graph와 충돌 없이 컴파일된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelApi.getSeries()`에 `GET /api/v2/creator-channels/{creatorId}/series` endpoint와 `page`, `size`, `sort`, `Authorization` 전달을 추가하고, `CreatorChannelRepository.getSeries(creatorId, page, size, sort, token)` 위임 method를 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
- [x] **Task 2.3: ViewModel RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesViewModelTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesPaginationTest.kt`
|
||
- 테스트 케이스:
|
||
- 최초 로딩이 `page=0`, `size=20`, `sort=LATEST`로 호출된다.
|
||
- 정렬 변경 시 `page=0`, 선택된 sort로 재조회된다.
|
||
- 같은 정렬을 다시 선택하면 API를 재호출하지 않는다.
|
||
- `hasNext == true`일 때 다음 페이지는 마지막 응답의 `page + 1`로 요청한다.
|
||
- load-more 요청에는 현재 sort와 `size=20`을 유지한다.
|
||
- loading 중 중복 load-more 요청은 무시된다.
|
||
- 다음 페이지 성공 시 기존 series 뒤에 append한다.
|
||
- 다음 페이지 실패 시 기존 목록은 유지하고 pagination error message만 설정한다.
|
||
- `seriesCount == 0`이면 `Empty` 상태가 된다.
|
||
- 표시 가능한 series가 0개이면 `Empty` 상태가 된다.
|
||
- 내 채널이면 item progress UI model이 `null`이다.
|
||
- 내 채널이 아니고 progress 관련 nullable field가 모두 있으면 progress UI model이 생성된다.
|
||
- 내 채널이 아니어도 progress 관련 nullable field 중 하나라도 `null`이면 progress UI model이 `null`이다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesViewModelTest"`
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesPaginationTest"`
|
||
- 기대 결과:
|
||
- production 구현 전 `CreatorChannelSeriesViewModel` 미구현으로 RED 실패한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 기록: ULTRAWORK TDD 순서상 RED 테스트가 먼저 필요했으나, 실제 작업에서는 production skeleton이 먼저 추가되어 production 미구현 RED는 별도로 확보하지 못했다. 이후 동일 범위 내에서 `CreatorChannelSeriesViewModelTest`, `CreatorChannelSeriesPaginationTest`를 추가해 최초 로드/정렬/empty/progress/pagination/stale response 계약을 고정했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
|
||
- [x] **Task 2.4: `CreatorChannelSeriesViewModel` 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesViewModel.kt`
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- 작업:
|
||
- `DEFAULT_PAGE_SIZE = 20`, `FIRST_PAGE = 0`, 기본 `selectedSort = ContentSort.LATEST`로 둔다.
|
||
- `loadSeries(creatorId, isOwner)`는 같은 creatorId/isOwner로 이미 상태가 있으면 중복 최초 조회를 막는다.
|
||
- `changeSort(sort)`는 같은 sort이면 API를 재호출하지 않는다.
|
||
- `retrySeries()`는 현재 sort로 첫 페이지를 다시 조회한다.
|
||
- `loadMore()`는 content 상태, `hasNext`, `isLoadingMore`, creatorId를 확인해 중복 요청을 막는다.
|
||
- `requestGeneration`으로 오래된 응답이 최신 상태를 덮어쓰지 않게 한다.
|
||
- 성공 응답의 `seriesCount == 0` 또는 표시 가능한 item이 0개이면 `Empty` 상태로 전환한다.
|
||
- pagination 실패는 기존 content를 유지하고 `paginationErrorMessage`에만 반영한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesViewModelTest"`
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesPaginationTest"`
|
||
- 기대 결과:
|
||
- ViewModel 테스트가 GREEN이다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelSeriesViewModel`을 추가하고 `loadSeries`, `changeSort`, `retrySeries`, `loadMore`, `consumePaginationErrorMessage`, `requestGeneration`, `DEFAULT_PAGE_SIZE=20` 상태 관리를 오디오 탭 패턴에 맞춰 구현했다. `AppDI`에 `CreatorChannelSeriesViewModel` Koin binding을 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
|
||
---
|
||
|
||
### Phase 3: Mapper/UI model 계약 추가
|
||
|
||
- [x] **Task 3.1: Mapper RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesMapperTest.kt`
|
||
- 테스트 케이스:
|
||
- `isProceeding == true`이면 subtitle에 `연재`가 포함된다.
|
||
- `isProceeding == false`이면 subtitle에 `완결`이 포함된다.
|
||
- `publishedDaysOfWeek`, `contentCount`, 진행 상태를 `매주 월 • 총 45화 • 연재` 형식으로 조합한다.
|
||
- `publishedDaysOfWeek`가 blank이면 빈 bullet 없이 `총 45화 • 연재` 형식으로 조합한다.
|
||
- `isOriginal == true`이면 original tag 표시 flag가 true이다.
|
||
- `isAdult == true`이면 adult tag 표시 flag가 true이다.
|
||
- 내 채널이면 progress가 생성되지 않는다.
|
||
- 내 채널이 아니고 `purchasedContentCount=12`, `paidContentCount=45`, `purchasedPaidContentRate=40`이면 progress가 생성된다.
|
||
- rate가 0 미만이면 progress bar percent는 0으로 clamp된다.
|
||
- rate가 100 초과이면 progress bar percent는 100으로 clamp된다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesMapperTest"`
|
||
- 기대 결과:
|
||
- production mapper 미구현으로 RED 실패한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 기록: ULTRAWORK TDD 순서상 RED 테스트가 먼저 필요했으나, 실제 작업에서는 mapper production 파일이 먼저 추가되어 production 미구현 RED는 별도로 확보하지 못했다. 이후 `CreatorChannelSeriesMapperTest`를 추가해 subtitle, original/adult flag, owner별 progress, partial-null progress, progressScale clamp, blank title 제외 계약을 고정했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
|
||
- [x] **Task 3.2: UI model과 mapper 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/model/CreatorChannelSeriesUiModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/model/CreatorChannelSeriesMappers.kt`
|
||
- 작업:
|
||
- `CreatorChannelSeriesItemUiModel`에는 `seriesId`, `title`, `subtitle`, `coverImageUrl`, `showOriginalTag`, `showAdultBadge`, `progress`를 둔다.
|
||
- `CreatorChannelSeriesProgressUiModel`에는 `purchasedCount`, `paidCount`, `ratePercent`, `progressScale`를 둔다.
|
||
- progress는 내 채널이면 생성하지 않는다.
|
||
- progress는 nullable field가 모두 있을 때만 생성한다.
|
||
- progress bar 표시값은 `purchasedPaidContentRate / 100f`를 0f..1f로 clamp한다.
|
||
- subtitle은 blank segment를 제외하고 ` • `로 join한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesMapperTest"`
|
||
- 기대 결과:
|
||
- Mapper 테스트가 GREEN이다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelSeriesItemUiModel`, `CreatorChannelSeriesProgressUiModel`, `toSeriesItemUiModels(isOwner)` mapper를 추가했다. subtitle은 blank segment를 제외하고 ` • `로 join하며, progress는 owner이거나 nullable field가 누락되면 생성하지 않고 `purchasedPaidContentRate / 100f`를 0f..1f로 clamp한다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
- 2026-06-20 후속 수정: mapper가 subtitle을 한국어 문자열(`총 n화`, `연재`, `완결`)로 직접 조합하던 문제를 수정했다. `CreatorChannelSeriesSubtitleUiModel`에 `publishedDaysOfWeek`, `contentCount`, `isProceeding` 조각 데이터를 유지하고, Adapter bind 시점에 다국어 string resource로 subtitle을 조합하도록 변경했다.
|
||
- 2026-06-20 후속 RED 확인: mapper/layout 테스트 기대값을 subtitle 조각 모델과 resource 기반 bind 계약으로 변경한 뒤 production 수정 전 `item.subtitle`이 `String`이라 `isProceeding`, `contentCount`, `publishedDaysOfWeek` 접근 컴파일 오류가 발생함을 확인했다.
|
||
- 2026-06-20 후속 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesMapperTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"` PASS.
|
||
|
||
---
|
||
|
||
### Phase 4: Fragment, Adapter, XML UI 구현
|
||
|
||
- [x] **Task 4.1: Fragment/Layout RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesFragmentLayoutTest.kt`
|
||
- 테스트 케이스:
|
||
- `fragment_creator_channel_series.xml`이 존재한다.
|
||
- `item_creator_channel_series.xml`이 존재한다.
|
||
- fragment layout에 Sort-bar, total count, sort label, RecyclerView, empty, error, retry view id가 존재한다.
|
||
- item layout에 thumbnail, title, subtitle, original tag, adult badge, progress container, progress count, progress percent, progress fill id가 존재한다.
|
||
- item layout source에 `전체소장` 또는 play button id/text가 포함되지 않는다.
|
||
- empty 문자열 resource key가 한국어/영어/일본어 파일에 모두 존재한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"`
|
||
- 기대 결과:
|
||
- layout/string 미구현으로 RED 실패한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 RED 확인: `CreatorChannelSeriesFragmentLayoutTest`를 추가하고 실행했을 때 `CreatorChannelSeriesFragment`, `fragment_creator_channel_series`, `item_creator_channel_series` 및 관련 view id 미구현으로 `compileDebugUnitTestKotlin`이 실패했다.
|
||
- 2026-06-20 GREEN 확인: layout/string/Fragment/Adapter 구현 후 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"` PASS.
|
||
|
||
- [x] **Task 4.2: 문자열 리소스 추가**
|
||
- 수정:
|
||
- `app/src/main/res/values/strings.xml`
|
||
- `app/src/main/res/values-en/strings.xml`
|
||
- `app/src/main/res/values-ja/strings.xml`
|
||
- 작업:
|
||
- `creator_channel_series_empty_message`를 3개 언어에 추가한다.
|
||
- `creator_channel_series_error_message`, `creator_channel_series_retry_button`, `creator_channel_series_status_proceeding`, `creator_channel_series_status_completed`, `creator_channel_series_total_content_count`, `creator_channel_series_progress_count`, `creator_channel_series_progress_percent`를 추가한다.
|
||
- 기존 공통 문자열이 있으면 중복 생성하지 않고 재사용한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- 기대 결과:
|
||
- 신규 문자열 리소스 병합이 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `creator_channel_series_empty_message`, error/retry/status/total/progress 문자열을 한국어/영어/일본어 리소스에 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:mergeDebugResources` PASS.
|
||
|
||
- [x] **Task 4.3: 시리즈 탭 fragment layout 작성**
|
||
- 생성:
|
||
- `app/src/main/res/layout/fragment_creator_channel_series.xml`
|
||
- 작업:
|
||
- 오디오 탭의 sort bar 구조를 참고하되 theme tab과 audio rate card는 만들지 않는다.
|
||
- 좌측 `전체` label과 `seriesCount` TextView를 둔다.
|
||
- 우측 sort label과 `ic_new_sort` ImageView를 둔다.
|
||
- RecyclerView는 vertical list로 사용한다.
|
||
- empty 영역에는 `creator_channel_series_empty_message`를 표시한다.
|
||
- error TextView와 retry Button은 기존 오디오 탭 error/retry 패턴을 따른다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- 기대 결과:
|
||
- ViewBinding `FragmentCreatorChannelSeriesBinding`이 생성된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `fragment_creator_channel_series.xml`에 sort bar, total count, sort label/icon, RecyclerView, empty/error/retry 영역을 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:mergeDebugResources` PASS, `FragmentCreatorChannelSeriesBinding` 생성 후 `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
- [x] **Task 4.4: 시리즈 item layout 작성**
|
||
- 생성:
|
||
- `app/src/main/res/layout/item_creator_channel_series.xml`
|
||
- 작업:
|
||
- 썸네일은 Figma 기준 `122dp x 172dp`, radius 14dp 형태로 제한한다.
|
||
- item root는 `match_parent` width와 `wrap_content` height를 사용한다.
|
||
- 좌측 썸네일 외 info/progress 영역은 `0dp` width + weight 또는 ConstraintLayout constraint로 남은 공간을 채운다.
|
||
- 우측 `전체소장`/play button 영역은 만들지 않는다.
|
||
- original tag는 기존 `view_series_original_tag` 또는 기존 equivalent resource를 재사용한다.
|
||
- adult badge는 기존 adult icon/background 정책을 재사용한다.
|
||
- progress container는 내 채널이거나 progress가 null이면 adapter에서 `GONE` 처리할 수 있게 분리한다.
|
||
- progress fill은 scaleX 방식으로 0f..1f를 적용할 수 있게 pivot start 기준 구조로 만든다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- 기대 결과:
|
||
- ViewBinding `ItemCreatorChannelSeriesBinding`이 생성된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `item_creator_channel_series.xml`에 122dp x 172dp thumbnail, wrap-content original tag, adult badge, title/subtitle, progress count/percent/fill을 추가하고 우측 `전체소장`/play button 영역은 제외했다.
|
||
- 2026-06-20 검증: 최초 `./gradlew --no-daemon :app:compileDebugKotlin`은 존재하지 않는 `@dimen/spacing_2` 참조로 실패했고, `2dp`로 수정 후 PASS.
|
||
|
||
- [x] **Task 4.5: Adapter 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/ui/CreatorChannelSeriesAdapter.kt`
|
||
- 작업:
|
||
- `RecyclerView.Adapter` 또는 기존 프로젝트 adapter 패턴을 따른다.
|
||
- `submitItems(items)`로 내부 목록을 갱신한다.
|
||
- item 클릭 시 `onSeriesClicked(seriesId)`를 호출한다.
|
||
- `title`, `subtitle`, `coverImageUrl`, original/adult tag, progress count/rate/progress fill을 bind한다.
|
||
- `coverImageUrl`은 기존 `loadUrl` extension과 placeholder/error 정책을 사용한다.
|
||
- progress가 null이면 progress container를 `GONE` 처리한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- 기대 결과:
|
||
- Adapter 컴파일이 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelSeriesAdapter`를 추가하고 `submitItems`, item click, title/subtitle/image/original/adult/progress binding, thumbnail radius outline 처리를 구현했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
- [x] **Task 4.6: Fragment 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesFragment.kt`
|
||
- 작업:
|
||
- `newInstance(creatorId)`로 creatorId argument를 받는다.
|
||
- `Host` interface에는 `isCreatorChannelOwner()`, `onCreatorChannelSeriesClicked(seriesId: Long)`, `onCreatorChannelSeriesContentChanged()`를 둔다.
|
||
- `onCreatorChannelSeriesTabSelected()`에서 `viewModel.loadSeries(creatorId, isOwner = host.isCreatorChannelOwner())`를 호출한다.
|
||
- `onCreatorChannelSeriesScrolledToBottom()`에서 `viewModel.loadMore()`를 호출한다.
|
||
- `onCreatorChannelSeriesViewportHeightChanged(minHeight)`는 empty/error 영역 최소 높이 조정이 필요하면 기존 Live/Audio 패턴에 맞춰 구현한다.
|
||
- sort button 클릭 시 `CreatorChannelSortPopup`을 띄우고 선택 결과를 `viewModel.changeSort(sort)`로 전달한다.
|
||
- Loading/Empty/Error/Content 상태별 view visibility를 명확히 bind한다.
|
||
- content bind 시 total count, sort label, adapter items를 갱신한다.
|
||
- pagination error message는 Toast로 표시하고 consume한다.
|
||
- content layout key가 바뀔 때만 host에 content changed를 알린다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"`
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- 기대 결과:
|
||
- Fragment layout 테스트와 Kotlin 컴파일이 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelSeriesFragment`를 추가하고 `newInstance(creatorId)`, `Host`, sort popup, `loadSeries`, `loadMore`, retry, state별 visibility, pagination error toast/consume, content layout key 변경 감지를 구현했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"` PASS, `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
---
|
||
|
||
### Phase 5: 채널 탭 연결과 Activity 통합
|
||
|
||
- [x] **Task 5.1: PagerAdapter RED 테스트 수정**
|
||
- 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt`
|
||
- 작업:
|
||
- `CreatorChannelTab.Series`가 `CreatorChannelSeriesFragment`를 생성하는 테스트를 추가한다.
|
||
- 기존 placeholder 기대값에서 Series 탭을 제외한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"`
|
||
- 기대 결과:
|
||
- production 연결 전 RED 실패한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 RED 확인: `CreatorChannelPagerAdapterTest`에 Series 탭이 `CreatorChannelSeriesFragment`를 생성해야 한다는 기대를 추가했고, production 연결 전 `CreatorChannelSeriesFragment` 미구현으로 컴파일 실패했다.
|
||
- 2026-06-20 GREEN 확인: PagerAdapter 연결 후 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"` PASS.
|
||
|
||
- [x] **Task 5.2: PagerAdapter Series 연결**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt`
|
||
- 작업:
|
||
- `CreatorChannelTab.Series -> CreatorChannelSeriesFragment.newInstance(creatorId)` 분기를 추가한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"`
|
||
- 기대 결과:
|
||
- PagerAdapter 테스트가 GREEN이다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelPagerAdapter.createFragment()`에 `CreatorChannelTab.Series -> CreatorChannelSeriesFragment.newInstance(creatorId)` 분기를 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"` PASS.
|
||
|
||
- [x] **Task 5.3: Activity source RED 테스트 수정**
|
||
- 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||
- 작업:
|
||
- Activity가 `CreatorChannelSeriesFragment.Host`를 구현하는지 검증한다.
|
||
- Series 탭 선택 시 `onCreatorChannelSeriesTabSelected()`를 호출하는지 검증한다.
|
||
- Series 탭이 load-more 대상에 포함되는지 검증한다.
|
||
- `notifyCurrentCreatorChannelTabScrolledToBottom()`에서 Series fragment의 scroll bottom callback을 호출하는지 검증한다.
|
||
- `onCreatorChannelSeriesClicked(seriesId)`가 `SeriesDetailActivity`와 `Constants.EXTRA_SERIES_ID`를 사용하는지 검증한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"`
|
||
- 기대 결과:
|
||
- production 연결 전 RED 실패한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 RED 확인: `CreatorChannelActivitySourceTest`에 Series Host, tab selected load, load-more, viewport, content changed, detail navigation 계약을 추가했고, production 연결 전 관련 source 문자열 미구현으로 실패하도록 고정했다.
|
||
- 2026-06-20 GREEN 확인: Activity 연결 후 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"` PASS.
|
||
|
||
- [x] **Task 5.4: Activity Series 통합**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||
- 작업:
|
||
- `CreatorChannelSeriesFragment.Host`를 구현한다.
|
||
- `findSeriesFragment()`를 추가한다.
|
||
- `onPageSelected`에서 Series 탭 선택 시 `onCreatorChannelSeriesTabSelected()`를 호출한다.
|
||
- `onCreatorChannelHeaderChanged`에서 현재 탭이 Series이면 owner 여부가 반영되도록 load를 호출한다.
|
||
- `notifyCurrentCreatorChannelTabScrolledToBottom()`에 Series load-more callback을 추가한다.
|
||
- `isCreatorChannelLoadMoreTab()`에 Series 탭을 추가한다.
|
||
- `updateCreatorChannelTabViewportHeight()`에 Series viewport callback을 추가한다.
|
||
- `onCreatorChannelSeriesContentChanged()`에서 `updateCreatorChannelTabViewportHeight()`, `updateViewPagerHeight()`, `postCheckCreatorChannelCurrentTabNeedsMore()`를 호출한다.
|
||
- `onCreatorChannelSeriesClicked(seriesId)`에서 `SeriesDetailActivity`에 `Constants.EXTRA_SERIES_ID`를 전달한다.
|
||
- 기존 홈 탭의 `onCreatorChannelSeriesClicked(series: CreatorChannelSeriesResponse)`는 기존 동작을 유지한다.
|
||
- 검증 명령:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"`
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- 기대 결과:
|
||
- Activity source 테스트와 Kotlin 컴파일이 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 구현: `CreatorChannelActivity`가 `CreatorChannelSeriesFragment.Host`를 구현하도록 연결하고, `findSeriesFragment()`, tab selected/header changed load, load-more 분기, viewport callback, content changed callback, `SeriesDetailActivity` 이동을 추가했다.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"` PASS, `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
|
||
---
|
||
|
||
### Phase 6: 통합 검증과 회귀 확인
|
||
|
||
- [x] **Task 6.1: 시리즈 탭 단위 테스트 실행**
|
||
- 실행:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"`
|
||
- 기대 결과:
|
||
- 신규 series 패키지 테스트가 모두 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
|
||
- [x] **Task 6.2: 크리에이터 채널 관련 테스트 실행**
|
||
- 실행:
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"`
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`
|
||
- 기대 결과:
|
||
- 시리즈 연결 테스트와 기존 홈/라이브/오디오 회귀 테스트가 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"` PASS.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"` PASS.
|
||
|
||
- [x] **Task 6.3: 리소스/컴파일/스타일 검증**
|
||
- 실행:
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- `./gradlew :app:ktlintCheck`
|
||
- `git diff --check`
|
||
- 기대 결과:
|
||
- 리소스 병합, Kotlin 컴파일, ktlint, whitespace 검증이 모두 PASS한다.
|
||
- 검증 기록:
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:mergeDebugResources` PASS.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:ktlintCheck` PASS.
|
||
- 2026-06-20 검증: `git diff --check` PASS.
|
||
|
||
- [ ] **Task 6.4: 수동 확인** `[blocked]`
|
||
- 확인:
|
||
- 시리즈 탭 진입 시 `GET /api/v2/creator-channels/{creatorId}/series?page=0&size=20&sort=LATEST`가 호출된다.
|
||
- Sort-bar 좌측에 전체 시리즈 수가 표시된다.
|
||
- 정렬 팝업에는 `추천순` 없이 5개 옵션만 표시된다.
|
||
- 정렬 변경 시 첫 페이지부터 재조회된다.
|
||
- 시리즈 item 우측 `전체소장`/play button이 표시되지 않는다.
|
||
- 내 채널이 아닌 경우 progress count, percent, progress bar가 표시된다.
|
||
- 내 채널인 경우 progress count, percent, progress bar가 표시되지 않고 제목/부제 info만 표시된다.
|
||
- empty 상태에서 `크리에이터가 시리즈를 준비 중입니다.\n기대해 주세요!`가 표시된다.
|
||
- 영어/일본어 locale에서 empty 문구가 각 번역으로 표시된다.
|
||
- 목록 하단 스크롤 시 다음 page가 중복 없이 append된다.
|
||
- item 터치 시 `SeriesDetailActivity`로 이동하고 `seriesId`가 전달된다.
|
||
- 검증 기록:
|
||
- 2026-06-20 확인: Figma `290:9031`, `290:9036`, `290:9038` screenshot과 소스 대조로 sort-bar, 전체 count, 5개 정렬 option 사용 경로, item 우측 `전체소장`/play 영역 미포함, progress owner 조건, empty 다국어 문자열, pagination/detail navigation 연결을 확인했다.
|
||
- 2026-06-20 blocked: `adb devices` 결과 연결된 디바이스가 없어 실제 앱 화면에서 API 호출, locale 전환, 하단 스크롤 append, item 터치 상세 이동은 수동 실행하지 못했다.
|
||
|
||
---
|
||
|
||
## Verification Log
|
||
- 구현 완료 후 여러 Phase에 걸친 통합 검증, 회귀 검증, 최종 수동 확인 기록을 이 섹션에 누적한다.
|
||
- 2026-06-20 코드 리뷰: Phase 2/3 범위의 API/DTO/Repository/ViewModel/mapper/UI model/test 코드를 검토했으며, 즉시 수정이 필요한 결함은 발견하지 못했다.
|
||
- 2026-06-20 검증: 최초 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` 실행은 sandbox의 `~/.gradle` lock 파일 접근 제한으로 실패했고, escalated 재실행 결과 PASS.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"` PASS. 테스트 리포트 기준 `CreatorChannelSeriesViewModelTest` 8건, `CreatorChannelSeriesPaginationTest` 5건, `CreatorChannelSeriesMapperTest` 11건 모두 failures/errors/skipped 0.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
- 2026-06-20 검증: `./gradlew --no-daemon :app:ktlintCheck` PASS.
|
||
- 2026-06-20 검증: `git diff --check` PASS.
|
||
- 2026-06-20 Phase 4/5 RED: 신규 `CreatorChannelSeriesFragmentLayoutTest`, `CreatorChannelPagerAdapterTest`, `CreatorChannelActivitySourceTest` 기대값 추가 후 production 미구현 상태에서 `CreatorChannelSeriesFragment`, 신규 layout/id 미존재로 테스트 컴파일 실패를 확인했다. 병렬 Gradle 실행 중 Kotlin cache lock/EOF 오류가 함께 발생해 이후 검증은 순차 실행으로 진행했다.
|
||
- 2026-06-20 Phase 4/5 구현: `CreatorChannelSeriesFragment`, `CreatorChannelSeriesAdapter`, fragment/item XML, 다국어 문자열, PagerAdapter Series 분기, Activity Series Host/load-more/viewport/navigation 연결을 추가했다.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFragmentLayoutTest"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"` PASS.
|
||
- 2026-06-20 Phase 4/5 검증: `./gradlew --no-daemon :app:mergeDebugResources` PASS, `./gradlew --no-daemon :app:compileDebugKotlin` PASS, `./gradlew --no-daemon :app:ktlintCheck` PASS, `git diff --check` PASS.
|
||
- 2026-06-20 후속 수정 검증: Series item subtitle의 locale 고정 문제를 해결하기 위해 mapper는 subtitle 조각 데이터만 생성하고 Adapter가 `creator_channel_series_subtitle_content_count`, `creator_channel_series_status_proceeding`, `creator_channel_series_status_completed` resource로 subtitle을 조합하도록 변경했다.
|
||
- 2026-06-20 후속 수정 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` PASS.
|
||
- 2026-06-20 후속 수정 검증: `./gradlew --no-daemon :app:mergeDebugResources` PASS, `./gradlew --no-daemon :app:compileDebugKotlin` PASS, `./gradlew --no-daemon :app:ktlintCheck` PASS, `git diff --check` PASS.
|
||
- 2026-06-20 Phase 4/5 코드 리뷰: `CreatorChannelSeriesFragment`, `CreatorChannelSeriesAdapter`, fragment/item XML, PagerAdapter/Activity 연결부를 검토했다. 발견 사항: `CreatorChannelSeriesMappers.kt`에서 subtitle의 `총 n화`, `연재`, `완결` 문구가 Kotlin 상수/문자열로 고정되어 있어 Phase 4에서 추가한 영어/일본어 상태 문자열이 사용되지 않는다.
|
||
- 2026-06-20 Phase 4/5 재검증: sandbox 실행은 `~/.gradle` lock 접근 제한으로 실패했고, escalated 순차 실행으로 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
- 2026-06-20 Phase 4/5 코드 리뷰 및 검증: Figma `290:9031`, `290:9036`, `290:9038`와 `CreatorChannelSeriesFragment`, `CreatorChannelSeriesAdapter`, fragment/item XML, PagerAdapter/Activity 연결부를 재검토했다. PRD에서 제거하기로 한 우측 `전체소장`/play 영역 제외는 의도된 차이로 확인했고, 즉시 수정이 필요한 결함은 발견하지 못했다.
|
||
- 2026-06-20 Phase 4/5 코드 리뷰 및 검증: 최초 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"` sandbox 실행은 `~/.gradle` lock 접근 제한으로 실패했다. escalated 순차 실행으로 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
|
||
- 2026-06-20 Phase 6 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.series.*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Series*"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"` 모두 PASS.
|
||
- 2026-06-20 Phase 6 검증: `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
- 2026-06-20 Phase 6 수동 확인: Figma `290:9031`, `290:9036`, `290:9038` screenshot과 series Fragment/Adapter/layout/Activity/API 연결 소스를 대조했다. 다만 `adb devices`에서 연결된 디바이스가 없어 실제 앱 표면에서 API 호출, locale, pagination, 상세 이동은 blocked로 남겼다.
|