40 KiB
콘텐츠 전체보기 구현 계획/TASK
For agentic workers: REQUIRED SUB-SKILL: 구현 시
superpowers:subagent-driven-development또는superpowers:executing-plans를 사용해 task 단위로 진행한다. 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시- [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
Goal: 홈/콘텐츠 추천 섹션의 전체보기 chevron을 연결하고, 기존 콘텐츠 전체 탭 이동을 먼저 적용한 뒤 New&Hot 및 처음부터 함께 성장! 전용 신규 전체보기 화면을 제공한다.
Architecture: 구현 순서는 chevron 표시 -> 기존 콘텐츠 전체 탭 이동 -> 신규 전체보기 페이지/API로 고정한다. 기존 화면 이동은 MainV2Activity, ContentMainFragment, ContentAllTabViewModel에 작은 public navigation contract를 추가해 처리한다. 신규 전체보기 화면은 마지막 단계에서 kr.co.vividnext.sodalive.v2.main.content.overview 하위 Activity/ViewModel/API/Repository/DTO/UI model/adapter로 격리한다.
Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
전제와 성공 기준
- PRD:
docs/20260627_콘텐츠_전체보기/prd.md - 구현 순서:
- 1순위: 홈/콘텐츠 추천 섹션 chevron 표시.
- 2순위: 신규 페이지가 필요 없는 섹션을 기존 콘텐츠
전체탭으로 이동. - 3순위: 신규 콘텐츠 전체보기 페이지/API 생성 및 신규 페이지 대상 섹션 이동.
- 전체보기 chevron 표시 섹션:
- 홈 추천 탭:
처음부터 함께 성장! - 콘텐츠 추천 탭:
오직 보이스온에서만!,새로 올라온 오디오,New&Hot,무료 오디오,포인트 오디오
- 홈 추천 탭:
- 기존 콘텐츠
전체탭 이동 섹션:오직 보이스온에서만!-> 콘텐츠 탭전체->오리지널카테고리새로 올라온 오디오-> 콘텐츠 탭전체->오디오카테고리무료 오디오-> 콘텐츠 탭전체->무료카테고리 ->인기순포인트 오디오-> 콘텐츠 탭전체->포인트카테고리 ->인기순
- 신규 전체보기 페이지 이동 섹션:
- 홈 추천 탭
처음부터 함께 성장!-> 신규 콘텐츠 전체보기 ->FIRST_AUDIO_CONTENT - 콘텐츠 추천 탭
New&Hot-> 신규 콘텐츠 전체보기 ->NEW_AND_HOT_AUDIO
- 홈 추천 탭
댓글 많은 오디오,추천 오디오에는 chevron을 표시하지 않는다.- 신규 API endpoint는
GET /api/v2/contents이다. - 신규 API query parameter key는 소문자
page,size,type만 사용한다. - 신규 API 기본값은
page=0,size=20,type=NEW_AND_HOT_AUDIO다. - 앱 구현 DTO annotation은 Gson
@SerializedName을 사용한다. - 신규 전체보기 화면 title은
NEW_AND_HOT_AUDIO일 때New&Hot,FIRST_AUDIO_CONTENT일 때처음부터 함께 성장!이다. - 신규 전체보기 화면은 Figma node
482:15105기준 2열 오디오 카드 grid를 사용한다. - 구현 완료 후 최소 다음 명령을 실행한다.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
Phase 순서
- Phase 1: 기존 구조 확인과 작업 경계 고정
- Phase 2: 홈/콘텐츠 추천 섹션 chevron 표시
- Phase 3: 기존 콘텐츠
전체탭 이동 contract 추가 - Phase 4: 기존 콘텐츠
전체탭 이동 섹션 연결 - Phase 5: 신규 전체보기 API, DTO, Repository, UI model 추가
- Phase 6: 신규 전체보기 ViewModel, Activity, Adapter 구현
- Phase 7: 신규 전체보기 대상 섹션 이동 연결
- Phase 8: 통합 검증과 수동 확인
Figma 참조 필요 Phase
- Phase 1: 제한 참조
- 기존 홈/콘텐츠 추천 섹션 title, 콘텐츠 전체 탭, MainV2 navigation 구조 확인 중심으로 진행한다.
- Phase 2: 제한 참조
- chevron 표시는 기존
view_section_title.xml과 홈/콘텐츠 추천 섹션 구조를 따른다.
- chevron 표시는 기존
- Phase 3~4: Figma 참조 불필요
- 기존 콘텐츠
전체탭 이동은 기존ContentAllTabViewModel의 type/sort 계약과 MainV2 tab 전환 구조를 따른다.
- 기존 콘텐츠
- Phase 5: Figma 참조 불필요
- API/DTO/Repository/UI model/mapper/ViewModel은 PRD 서버 계약과 기존 V2 data layer/paging 패턴을 따른다.
- Phase 6: 필수 참조
- 신규 전체보기 Activity layout은 Figma
482:15105의 title bar, black background, 2열 grid, card spacing을 기준으로 확인한다.
- 신규 전체보기 Activity layout은 Figma
- Phase 7~8: 필수 참조
- 신규 페이지 대상 섹션 이동과 최종 수동 화면 검증은 PRD의 Figma reference와 실제 화면을 대조한다.
파일 구조
기존 화면 이동 선행 범위
- Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt처음부터 함께 성장!title chevron을 먼저 표시한다. 신규 페이지 이동은 Phase 7에서 연결한다.
- Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt- 콘텐츠 추천 섹션 chevron 표시, 기존 콘텐츠
전체탭 이동, 신규 페이지 이동을 단계별로 추가한다.
- 콘텐츠 추천 섹션 chevron 표시, 기존 콘텐츠
- Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModel.kt- 외부 진입용 type/sort 선택 API를 추가한다.
- Modify:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt- 콘텐츠 탭 내부
전체를 특정 type/sort로 여는 public method와 기존 fragment 재사용 처리를 추가한다.
- 콘텐츠 탭 내부
- Modify:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt- 콘텐츠 추천 섹션 chevron/routing/source를 검증한다.
- Modify:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModelTest.kt- 외부 type/sort 선택 API를 검증한다.
- Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentSourceTest.kt- 홈
처음부터 함께 성장!chevron/source를 검증한다.
- 홈
- Modify:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2ActivitySourceTest.kt- 콘텐츠 탭 내부
전체type/sort routing contract를 검증한다.
- 콘텐츠 탭 내부
신규 전체보기 후행 범위
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewApi.ktGET /api/v2/contentsRetrofit endpoint를 정의한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewModels.ktContentOverviewPageResponse,ContentOverviewType,ContentOverviewItemResponseDTO를 정의한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewRepository.kt- API 호출을 repository method로 감싼다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewUiState.ktLoading,Content,Empty,Error상태와 paging/loading-more 상태를 정의한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewUiModels.kt- 신규 전체보기 화면 item/title/type UI model을 정의한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewMappers.kt- DTO를 UI model로 변환하고
AudioContentTag를 매핑한다.
- DTO를 UI model로 변환하고
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewViewModel.kt- type/page/hasNext/loading-more/API 상태를 관리한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.kt- title bar, 2열 RecyclerView, paging, empty/error/loading, 상세 routing을 연결한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ui/ContentOverviewAdapter.ktAudioContentCardView기반 2열 grid item을 바인딩한다.
- Create:
app/src/main/res/layout/activity_content_overview.xml- 신규 전체보기 화면 layout이다.
- Modify:
app/src/main/AndroidManifest.xmlContentOverviewActivity를 등록한다.
- Modify:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt- 신규 API, Repository, ViewModel을 Koin에 등록한다.
- Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewMapperTest.kt- DTO -> UI model/tag/title mapping을 검증한다.
- Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewViewModelTest.kt- 첫 페이지, load-more, 실패, stale response 방지를 검증한다.
- Create:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivitySourceTest.kt- layout id, 2열 grid, intent extra, 상세 이동 source를 검증한다.
Phase 1: 기존 구조 확인과 작업 경계 고정
-
Task 1.1: PRD와 기존 구현 상태 대조
- 확인:
docs/20260627_콘텐츠_전체보기/prd.mdapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/main/res/layout/view_section_title.xml
- 작업:
HomeMainFragment의ViewSectionTitleBinding.setTitle(titleResId, showMore)가 chevron 표시를 이미 지원하는지 확인한다.ContentMainFragment의ViewSectionTitleBinding.setTitle(titleResId)가 현재 chevron을 항상 숨기는지 확인한다.- 이번 작업에서 레거시
audio_content/*파일을 직접 수정하지 않는 것을 확인한다.
- 검증:
- Run:
rg -n "setTitle\\(|ivSectionTitleChevron|viewHomeFirstAudioTitle|viewContentNewAndHotTitle|viewContentOriginalSeriesTitle" app/src/main/java/kr/co/vividnext/sodalive/v2/main app/src/main/res/layout/view_section_title.xml - Expected: 홈 title helper와 콘텐츠 title helper, chevron view id가 확인된다.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 실행:
rg -n "setTitle\(|ivSectionTitleChevron|viewHomeFirstAudioTitle|viewContentNewAndHotTitle|viewContentOriginalSeriesTitle" app/src/main/java/kr/co/vividnext/sodalive/v2/main app/src/main/res/layout/view_section_title.xml - 결과:
view_section_title.xml의iv_section_title_chevron,HomeMainFragment의setTitle(titleResId, showMore),ContentMainFragment의 section title 설정 지점을 확인했다. - 확인: 홈 title helper는
showMore로 chevron 표시를 이미 지원하며viewHomeFirstAudioTitle은showMore = true상태였다. 콘텐츠 title helper는 기존에 chevron을 숨기고 있어 Phase 2에서 helper 확장이 필요했다. - 확인: 이번 Phase 1~2 범위에서는 레거시
audio_content/*파일을 직접 수정하지 않았다.
- 확인:
-
Task 1.2: 기존 콘텐츠 전체 탭 이동 가능 지점 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/data/MainContentAllTabModels.kt
- 작업:
MainV2Activity의 bottom tab 전환과 fragment 재사용 위치를 확인한다.ContentMainFragment의showAllContent()와ContentAllTabViewModeltype/sort 변경 위치를 확인한다.MainContentAllType.ORIGINAL,AUDIO,FREE,POINT와ContentSort.POPULAR,LATEST가 기존 목적지 요구를 충족하는지 확인한다.
- 검증:
- Run:
rg -n "MainContentAllType|ContentSort|showAllContent|changeType|changeSort|clickTab\\(MainV2Tab.CONTENT\\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main - Expected: type/sort 선택과 bottom tab 전환 지점이 확인된다.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 실행:
rg -n "MainContentAllType|ContentSort|showAllContent|changeType|changeSort|clickTab\(MainV2Tab.CONTENT\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main - 결과:
MainV2Activity.clickTab(MainV2Tab.CONTENT),ContentMainFragment.showAllContent(),ContentAllTabViewModel.changeType/changeSort,MainContentAllType.ORIGINAL/AUDIO/FREE/POINT,ContentSort사용 지점을 확인했다. - 확인: Phase 1에서는 기존 콘텐츠 전체 탭 이동 가능 지점만 확인했고, 실제 contract/API 추가는 Phase 3 범위로 남겼다.
- 확인:
Phase 2: 홈/콘텐츠 추천 섹션 chevron 먼저 표시
-
Task 2.1: 홈 추천 탭 FIRST 섹션 chevron 표시
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
- 테스트:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentSourceTest.kt
- 작업:
viewHomeFirstAudioTitle.setTitle(..., showMore = true)상태를 유지하거나 누락되어 있으면 추가한다.- 이 Task에서는 신규 페이지 이동 click listener를 연결하지 않는다.
- source test는
viewHomeFirstAudioTitle.setTitle호출이showMore = true임을 검증한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" - Expected: 홈 FIRST chevron 표시 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 확인:
HomeMainFragment의viewHomeFirstAudioTitle.setTitle(R.string.home_recommendation_section_first_audio_contents, showMore = true)가 이미 적용되어 있었다. - 2026-06-27 테스트 추가:
HomeMainFragmentSourceTest를 생성해 FIRST 섹션showMore = true와 Phase 2 범위에서 click listener가 아직 연결되지 않았음을 검증했다. - 2026-06-27 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" - 결과: PASS.
- 수정:
-
Task 2.2: 콘텐츠 추천 탭 section title helper 확장
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt
- 테스트:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt
- 작업:
- 콘텐츠 탭의
ViewSectionTitleBinding.setTitle(titleResId)를setTitle(titleResId, showMore = false)로 확장한다. 오직 보이스온에서만!,새로 올라온 오디오,New&Hot,무료 오디오,포인트 오디오는showMore = true로 설정한다.댓글 많은 오디오,추천 오디오는showMore = false를 유지한다.- 이 Task에서는 기존 전체 탭 이동과 신규 페이지 이동 click listener를 연결하지 않는다.
- 콘텐츠 탭의
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - Expected: 콘텐츠 추천 chevron 표시/숨김 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - RED 결과: 신규
content recommendation section titles show only Phase 2 chevrons테스트가ContentMainFragmentSourceTest.kt:463에서 실패했다. 원인은 콘텐츠 title helper가showMore를 지원하지 않고 chevron을 항상 숨기는 기존 구현이었다. - 2026-06-27 구현:
ContentMainFragment의ViewSectionTitleBinding.setTitle(titleResId, showMore = false)를 추가하고오직 보이스온에서만!,새로 올라온 오디오,New&Hot,무료 오디오,포인트 오디오에만showMore = true를 지정했다.댓글 많은 오디오,추천 오디오는 기본값false를 유지했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - GREEN 결과: PASS.
- 참고: 홈/콘텐츠 source 테스트를 병렬 실행하던 중 한 번
:app:kspDebugKotlingenerated file 접근 오류가 발생했으나, 동일 콘텐츠 테스트 단독 재실행 시 정상 PASS했다.
- 수정:
Phase 3: 기존 콘텐츠 전체 탭 이동 contract 추가
-
Task 3.1: ContentAllTabViewModel 외부 선택 API 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModel.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModelTest.kt
- 작업:
fun selectTypeAndSort(type: MainContentAllType, sort: ContentSort)를 추가한다.- 함수는
selectedType,selectedSort,selectedDayOfWeek를 갱신하고page=0부터 재조회한다. ORIGINAL,AUDIO,FREE,POINT는dayOfWeek = null로 요청한다.- 기존
changeType,changeSort,loadInitial테스트를 깨지 않는다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" - Expected: 기존 테스트와 신규 외부 선택 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentAllTabViewModel.selectTypeAndSort(type, sort)를 추가해selectedType,selectedSort,selectedDayOfWeek를 갱신하고 첫 페이지를 재조회하도록 했다.SERIES가 아닌 type은selectedDayOfWeekFor(type)경로로dayOfWeek = null을 유지한다. - 2026-06-27 테스트 추가:
ContentAllTabViewModelTest에 외부 선택 요청FREE+POPULAR,ORIGINAL+LATEST가 모두page=0,dayOfWeek=null로 호출되는 검증을 추가했다. - 2026-06-27 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" - 결과: PASS.
- 수정:
-
Task 3.2: ContentMainFragment에 내부 전체 탭 선택 API 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt
- 작업:
fun selectAllTab(type: MainContentAllType, sort: ContentSort = ContentSort.LATEST)를 추가한다.- 함수는
binding.textTabBarContent.root의 선택을전체로 전환하고showContentTab(CONTENT_TAB_ALL)을 호출한다. - 이후
contentAllTabViewModel.selectTypeAndSort(type, sort)를 호출한다. - fragment view가 아직 생성되지 않은 경우를 대비해 pending type/sort를 fragment field에 저장하고
onViewCreated이후 적용한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - Expected: 내부 전체 탭 선택 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentMainFragment.selectAllTab(type, sort)를 추가하고 view 생성 전 호출은pendingAllTabSelection에 저장한 뒤onViewCreated이후 적용하도록 했다. - 2026-06-27 확인: pending 적용 시
hasSelectedAllTab = true를 먼저 설정한 뒤textTabBarContent.root.selectTab(CONTENT_TAB_ALL)과showContentTab(CONTENT_TAB_ALL)을 호출해TextTabBarView.selectTablistener가 유발할 수 있는 기본loadInitial()중복 호출을 피했다. 실제 데이터 재조회는contentAllTabViewModel.selectTypeAndSort(...)단일 경로로 수행한다. - 2026-06-27 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - 결과: PASS.
- 수정:
-
Task 3.3: MainV2Activity 콘텐츠 전체 탭 진입 API 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2ActivitySourceTest.kt
- 작업:
fun openContentAllTab(type: MainContentAllType, sort: ContentSort = ContentSort.LATEST)를 추가한다.- 현재 탭을
MainV2Tab.CONTENT로 전환한다. changeFragment이후 기존 또는 신규ContentMainFragment에selectAllTab(type, sort)를 전달한다.- 이미 콘텐츠 탭에 있는 경우 fragment를 중복 생성하지 않고 현재 fragment method를 호출한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2ActivitySourceTest" - Expected: MainV2 콘텐츠 전체 탭 routing source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
MainV2Activity.openContentAllTab(type, sort)를 추가해 CONTENT 탭이 아니면viewModel.clickTab(MainV2Tab.CONTENT)와changeFragment(MainV2Tab.CONTENT)로 콘텐츠 Fragment를 준비한 뒤 기존/newContentMainFragment.selectAllTab(type, sort)를 호출하도록 했다. - 2026-06-27 확인: 이미 CONTENT 탭인 경우
changeFragment를 직접 호출하지 않고 기존ContentMainFragment를 찾아selectAllTab만 호출하므로 fragment 중복 생성을 피한다. - 2026-06-27 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2ActivitySourceTest" - 결과: PASS.
- 수정:
Phase 4: 기존 콘텐츠 전체 탭 이동 섹션 연결
-
Task 4.1: 콘텐츠 추천 탭 기존 전체 탭 목적지 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt
- 테스트:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt
- 작업:
오직 보이스온에서만!chevron은MainContentAllType.ORIGINAL,ContentSort.LATEST로MainV2Activity.openContentAllTab을 호출한다.새로 올라온 오디오chevron은MainContentAllType.AUDIO,ContentSort.LATEST로 호출한다.무료 오디오chevron은MainContentAllType.FREE,ContentSort.POPULAR로 호출한다.포인트 오디오chevron은MainContentAllType.POINT,ContentSort.POPULAR로 호출한다.New&Hot은 신규 페이지 대상이므로 이 Phase에서는 click listener를 연결하지 않는다.- 홈
처음부터 함께 성장!은 신규 페이지 대상이므로 이 Phase에서는 click listener를 연결하지 않는다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - Expected: 기존 전체 탭 이동 섹션별 목적지 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
오직 보이스온에서만!,새로 올라온 오디오,무료 오디오,포인트 오디오chevron click listener를ensureMainV2NavigationAllowed로 감싸고(activity as? MainV2Activity)?.openContentAllTab(...)경로에 연결했다. - 2026-06-27 확인: routing destination은 각각
ORIGINAL/LATEST,AUDIO/LATEST,FREE/POPULAR,POINT/POPULAR로 지정했다.New&Hot및 홈처음부터 함께 성장!click listener는 Phase 4 범위에서 연결하지 않았다. - 2026-06-27 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - 결과: PASS.
- 수정:
-
Task 4.2: 기존 전체 탭 이동 수동 검증
- 확인:
- 콘텐츠 추천 탭
오직 보이스온에서만!chevron 클릭 시 콘텐츠전체->오리지널진입. - 콘텐츠 추천 탭
새로 올라온 오디오chevron 클릭 시 콘텐츠전체->오디오진입. - 콘텐츠 추천 탭
무료 오디오chevron 클릭 시 콘텐츠전체->무료+인기순진입. - 콘텐츠 추천 탭
포인트 오디오chevron 클릭 시 콘텐츠전체->포인트+인기순진입. - 콘텐츠 추천 탭
New&Hot과 홈처음부터 함께 성장!은 chevron만 표시되고 신규 페이지 이동은 아직 연결되지 않음.
- 콘텐츠 추천 탭
- 검증 기록:
- 구현 시 이 Task 아래에 테스트 기기/빌드 variant/API 응답 조건과 확인 결과를 한국어로 누적한다.
- 2026-06-27 상태: BLOCKED/미완료. 현재 작업 환경에서 Android 기기 또는 에뮬레이터를 통한 실제 화면 조작을 수행할 수 없어 수동 검증을 완료하지 못했다.
- 2026-06-27 대체 확인: source 테스트로 Phase 4 click listener 연결 대상과 미연결 대상(
New&Hot)은 검증했으나, 실제 앱 화면에서 chevron 클릭 후 콘텐츠전체탭 UI/type/sort 표시까지는 확인하지 못했다.
- 확인:
Phase 5: 신규 전체보기 API, DTO, Repository, UI model 추가
-
Task 5.1: ContentOverview API/DTO/Repository 추가
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewApi.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewRepository.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
ContentOverviewApi.getContents(authHeader, page, size, type)를@GET("/api/v2/contents")로 정의한다.- query annotation 이름은
page,size,type만 사용한다. - DTO는
@Keep과@SerializedName을 사용한다. ContentOverviewType은NEW_AND_HOT_AUDIO,FIRST_AUDIO_CONTENT만 정의한다.- Repository는
Single<ApiResponse<ContentOverviewPageResponse>>를 반환한다. - Koin
networkModule,repositoryModule에 신규 API/Repository를 등록한다.
- 검증:
- Run:
./gradlew :app:compileDebugKotlin - Expected: 신규 data layer와 DI 등록이 컴파일된다.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 5.2: Mapper RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewMapperTest.kt
- 테스트 케이스:
NEW_AND_HOT_AUDIOtitle은New&Hot이다.FIRST_AUDIO_CONTENTtitle은처음부터 함께 성장!이다.price == 0이면AudioContentTag.Free가 포함된다.isPointAvailable == true이면AudioContentTag.Point가 포함된다.isFirstContent == true이면AudioContentTag.First가 포함된다.isOriginalSeries == true이면AudioContentTag.Original이 포함된다.isAdult == true이면showAdultBadge == true다.page,size,hasNext,type은 UI state에 보존된다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewMapperTest" - Expected: mapper 구현 전 RED 실패.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 5.3: UI model과 mapper 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewUiState.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewMappers.kt
- 작업:
ContentOverviewUiState.Loading,Content,Empty,Error를 정의한다.ContentOverviewUiModel은contentId,title,coverImage,creatorNickname,tags,showAdultBadge를 포함한다.ContentOverviewType.toTitleResId()를 정의한다.NEW_AND_HOT_AUDIO는R.string.content_recommendation_section_new_and_hot,FIRST_AUDIO_CONTENT는R.string.home_recommendation_section_first_audio_contents로 매핑한다.ContentOverviewItemResponse.toUiModel()에서 PRD tag mapping을 구현한다.ContentOverviewPageResponse.toContent()에서 paging metadata와 item list를 보존한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewMapperTest" - Expected: mapper 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
Phase 6: 신규 전체보기 ViewModel, Activity, Adapter 구현
-
Task 6.1: ViewModel RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewViewModelTest.kt
- 테스트 케이스:
- 초기 로드는 전달받은 type으로
page=0,size=20을 요청한다. - 첫 페이지 성공 + items 있음은
Content상태를 emit한다. - 첫 페이지 성공 + items empty는
Empty상태를 emit한다. - 첫 페이지 실패는
Error상태와 toast를 emit한다. hasNext = true상태에서loadMore()는 다음 page를 요청하고 append한다.hasNext = false이면loadMore()가 repository를 추가 호출하지 않는다.- 추가 페이지 실패 시 기존 items를 유지하고 pagination error message를 emit한다.
- type 변경 또는 새 화면 생성 상태 간 page/items를 공유하지 않는다.
- 초기 로드는 전달받은 type으로
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewViewModelTest" - Expected: ViewModel 구현 전 RED 실패.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 6.2: ViewModel 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewViewModel.kt
- 작업:
loadFirstPage(type: ContentOverviewType)를 구현한다.loadMore()는Content상태,hasNext,isLoadingMore를 확인한 뒤 다음 page만 요청한다.requestGeneration으로 stale response가 현재 화면 상태를 덮어쓰지 않게 한다.- auth header는 기존 ViewModel 패턴대로
Bearer ${SharedPreferenceManager.token}를 사용한다. - 첫 페이지 loading은
isLoadingLiveData로 처리하고, 추가 페이지 loading은Content.isLoadingMore로 처리한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewViewModelTest" - Expected: ViewModel 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 6.3: Activity layout과 Adapter 구현
- 생성:
app/src/main/res/layout/activity_content_overview.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ui/ContentOverviewAdapter.kt
- 수정:
app/src/main/AndroidManifest.xml
- 작업:
- layout root는 black background를 사용한다.
- title bar는 60dp 높이, 좌측 back chevron, 22sp bold title을 배치한다.
- RecyclerView는 2열
GridLayoutManager로 표시한다. - RecyclerView item은 기존
item_content_audio_card.xml의AudioContentCardView를 재사용한다. - Adapter는
setGridItemWidthPx(widthPx)를 호출해 2열 item width를 적용한다. - 이미지 로딩은 기존
loadUrl확장을 사용한다. - manifest에
kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivity를 등록한다.
- 검증:
- Run:
./gradlew :app:mergeDebugResources - Expected: 신규 layout과 resource reference가 merge된다.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 6.4: Activity source RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivitySourceTest.kt
- 테스트 케이스:
ContentOverviewActivity.newIntent(context, type)가 type extra를 포함한다.- layout에 back button, title TextView, RecyclerView, empty/error TextView가 있다.
- Activity가
GridLayoutManager(this, CONTENT_OVERVIEW_GRID_SPAN_COUNT)로 2열 grid를 설정한다. - scroll bottom에서
viewModel.loadMore()를 호출한다. - item click은
Constants.EXTRA_AUDIO_CONTENT_ID로AudioContentDetailActivity를 연다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest" - Expected: Activity 구현 전 또는 연결 전 RED 실패.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
-
Task 6.5: Activity 구현과 ViewModel 연결
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
newIntent(context, type)companion method를 제공한다.- intent type extra가 없거나 enum parsing에 실패하면
NEW_AND_HOT_AUDIO를 fallback으로 사용한다. - title은 type mapping으로 표시한다.
- back button은
finish()를 호출한다. Content,Empty,Error,Loading상태별 UI를 바인딩한다.- item click은
AudioContentDetailActivity를 열고Constants.EXTRA_AUDIO_CONTENT_ID에contentId를 전달한다. - 성인 콘텐츠 접근 제한은 기존
AudioContentDetailActivity정책을 따른다. - Koin
viewModelModule에ContentOverviewViewModel을 등록한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*" - Expected: overview mapper/ViewModel/source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 생성:
Phase 7: 신규 전체보기 대상 섹션 이동 연결
-
Task 7.1: 홈 FIRST 신규 전체보기 이동 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
- 테스트:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentSourceTest.kt
- 작업:
viewHomeFirstAudioTitle.ivSectionTitleChevron.setOnClickListener를 추가한다.- 클릭 시
ContentOverviewActivity.newIntent(requireContext(), ContentOverviewType.FIRST_AUDIO_CONTENT)로 이동한다. ensureMainV2NavigationAllowed로 기존 홈 navigation guard 패턴을 따른다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" - Expected: 홈 FIRST 신규 전체보기 이동 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 수정:
-
Task 7.2: 콘텐츠 New&Hot 신규 전체보기 이동 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt
- 테스트:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt
- 작업:
viewContentNewAndHotTitle.ivSectionTitleChevron.setOnClickListener를 추가한다.- 클릭 시
ContentOverviewActivity.newIntent(requireContext(), ContentOverviewType.NEW_AND_HOT_AUDIO)로 이동한다. ensureMainV2NavigationAllowed로 기존 콘텐츠 navigation guard 패턴을 따른다.- 기존 전체 탭 이동 대상 섹션 click listener는 Phase 4 동작을 유지한다.
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - Expected: 콘텐츠 New&Hot 신규 전체보기 이동 source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 수정:
Phase 8: 통합 검증과 수동 확인
-
Task 8.1: 단위/source 테스트 실행
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"
- 기대 결과:
- 수정된 홈/콘텐츠/MainV2 source 테스트와 신규 overview 테스트가 모두 PASS한다.
- 검증 기록:
- 구현 시 이 Task 아래에 실행 명령과 결과를 한국어로 누적한다.
- 실행:
-
Task 8.2: 빌드/리소스/린트 검증
- 실행:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
- 기대 결과:
- resource merge, Kotlin compile, ktlint, whitespace 검증이 모두 PASS한다.
- 검증 기록:
- 구현 시 이 Task 아래에 실행 명령과 결과를 한국어로 누적한다.
- 실행:
-
Task 8.3: 수동 화면 검증
- 확인:
- 홈 추천 탭
처음부터 함께 성장!title 우측 chevron 표시. - 콘텐츠 추천 탭
오직 보이스온에서만!,새로 올라온 오디오,New&Hot,무료 오디오,포인트 오디오title 우측 chevron 표시. - 콘텐츠 추천 탭
댓글 많은 오디오,추천 오디오에는 chevron이 표시되지 않음. - 콘텐츠 추천 탭
오직 보이스온에서만!chevron 클릭 시 콘텐츠전체->오리지널진입. - 콘텐츠 추천 탭
새로 올라온 오디오chevron 클릭 시 콘텐츠전체->오디오진입. - 콘텐츠 추천 탭
무료 오디오chevron 클릭 시 콘텐츠전체->무료+인기순진입. - 콘텐츠 추천 탭
포인트 오디오chevron 클릭 시 콘텐츠전체->포인트+인기순진입. - 홈 추천 탭
처음부터 함께 성장!chevron 클릭 시처음부터 함께 성장!신규 전체보기 진입. - 콘텐츠 추천 탭
New&Hotchevron 클릭 시New&Hot신규 전체보기 진입. - 신규 전체보기 화면 title bar, back button, 2열 grid, tag, 성인 badge, 상세 이동.
- 신규 전체보기 화면 하단 스크롤 시
page + 1추가 요청.
- 홈 추천 탭
- 검증 기록:
- 구현 시 이 Task 아래에 테스트 기기/빌드 variant/API 응답 조건과 확인 결과를 한국어로 누적한다.
- 확인:
Verification Log
- 구현 완료 후 여러 Phase에 걸친 통합 검증, 회귀 검증, 최종 수동 확인 기록을 이 섹션에 누적한다.
- 2026-06-27 Phase 3~4 범위 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest"결과 PASS. - 2026-06-27 Phase 3~4 범위 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 3~4 범위 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2ActivitySourceTest"결과 PASS. - 2026-06-27 Phase 3~4 범위 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 3~4 범위 실행:
./gradlew :app:ktlintCheck는 신규 source 테스트의 긴 줄로 1회 실패 후 포맷 수정, 재실행 결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 3~4 범위 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 수동 화면 검증: Android 기기/에뮬레이터를 사용할 수 없어 Phase 4 기존 전체 탭 이동 수동 클릭 검증은 완료하지 못했다. Task 4.2에 BLOCKED/미완료로 기록했다.