65 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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentOverviewApi,ContentOverviewPageResponse,ContentOverviewType,ContentOverviewItemResponse,ContentOverviewRepository를 추가했다. endpoint는GET /api/v2/contents, query key는page,size,type만 사용한다. - 2026-06-27 DI 등록:
AppDI의networkModule에ContentOverviewApi,repositoryModule에ContentOverviewRepository를 등록했다. ViewModel DI는 Phase 6.5에서 등록하도록 이 Task에서는 제외했다. - 2026-06-27 실행:
./gradlew :app:compileDebugKotlin - 결과: PASS.
- 2026-06-27 코드 리뷰 확인: PRD/계획서의 응답 필드 계약은
items,contentId,coverImage인데 현재 DTO는contents,audioContentId,imageUrl,totalCount를 사용한다. 서버 최신 계약 변경이 아니라면 API 응답 파싱 실패 위험이 있어 수정이 필요하다. - 2026-06-27 리뷰 후속 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*" - RED 결과: 테스트 fixture를 문서 계약인
items,contentId,coverImage로 변경하자 기존 DTO의contents,audioContentId,imageUrl,totalCount생성자와 맞지 않아 컴파일 실패했다. - 2026-06-27 리뷰 후속 구현:
ContentOverviewPageResponse를items기반으로,ContentOverviewItemResponse를contentId,coverImage기반으로 수정하고 mapper도 동일 계약을 사용하도록 변경했다.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 테스트 추가:
ContentOverviewMapperTest를 생성해 type title resource, tag mapping, adult badge, paging metadata/type 보존을 검증하도록 했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewMapperTest" - RED 결과:
overview.modelmapper/UI model 미구현으로Unresolved reference 'model',toTitleResId,toUiModel,toContent컴파일 실패를 확인했다.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentOverviewUiState,ContentOverviewUiModel,ContentOverviewType.toTitleResId(),ContentOverviewPageResponse.toContent(),ContentOverviewItemResponse.toUiModel()을 추가했다. - 2026-06-27 확인:
NEW_AND_HOT_AUDIO는New&Hot,FIRST_AUDIO_CONTENT는처음부터 함께 성장!title resource로 매핑하고,Original/First/Point/Freetag 및 adult badge를 DTO boolean/price 기준으로 매핑했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewMapperTest" - GREEN 결과: PASS.
- 생성:
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 테스트 추가:
ContentOverviewViewModelTest를 생성해 초기 load, Content/Empty/Error, toast, load-more append,hasNext=false추가 요청 방지, pagination error 보존, stale response 방지를 검증하도록 했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewViewModelTest" - RED 결과:
ContentOverviewViewModel미구현으로Unresolved reference 'ContentOverviewViewModel',loadFirstPage,loadMore,overviewStateLiveData컴파일 실패를 확인했다.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentOverviewViewModel을 추가해page=0,size=20,Bearer ${SharedPreferenceManager.token},requestGeneration, 첫 페이지isLoading, 추가 페이지Content.isLoadingMore/paginationErrorMessage패턴을 구현했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewViewModelTest" - GREEN 결과: PASS.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현: Figma
482:15105기준으로 black root, 60dp title bar, 좌측ic_new_bar_back, 22sp bold title(Typography.Heading2), 2열 RecyclerView layout을 추가했다. - 2026-06-27 구현:
ContentOverviewAdapter를 추가해item_content_audio_card.xml/AudioContentCardView를 재사용하고setGridItemWidthPx,setContent,setTags,setAdultVisible,loadUrl을 바인딩했다. - 2026-06-27 구현:
AndroidManifest.xml에.v2.main.content.overview.ContentOverviewActivity를 등록했다. - 2026-06-27 실행:
./gradlew :app:mergeDebugResources - 결과: PASS.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 테스트 추가:
ContentOverviewActivitySourceTest를 생성해newIntent, intent extra, Figma layout id, 2열GridLayoutManager, 하단 scrollloadMore, 상세 이동 extra, manifest 등록, Phase 7 라우팅 미추가를 검증하도록 했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest" - RED 결과: layout/manifest/Phase 7 미연결 검증은 통과했고
ContentOverviewActivity.kt미생성으로 Activity source 관련 테스트 2건이FileNotFoundException으로 실패하는 것을 확인했다.
- 생성:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 구현:
ContentOverviewActivity를 추가해newIntent(context, type), type title, backfinish(),Loading/Content/Empty/Error렌더링, 2열 grid width 계산, scroll bottomloadMore(),AudioContentDetailActivity상세 이동을 연결했다. - 2026-06-27 구현:
AppDI.viewModelModule에ContentOverviewViewModel을 등록했다. intent type extra가 없거나 파싱 실패하면 계획서 기본값인NEW_AND_HOT_AUDIO를 사용한다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*" - GREEN 결과: PASS.
- 2026-06-27 추가 검증: 포맷 수정 후 동일 overview 테스트 재실행 결과 PASS.
- 2026-06-27 코드 리뷰 확인: PRD edge case의
contentId <= 0상세 이동 무시 조건이 현재ContentOverviewActivity.openAudioContentDetail(contentId)또는 adapter click 경로에 반영되어 있지 않다. 잘못된 id 응답 시 상세 화면을 열 수 있어 수정이 필요하다. - 2026-06-27 리뷰 후속 RED 테스트 추가:
ContentOverviewActivitySourceTest에 Activity의if (contentId <= 0) return및 Adapter의return@setOnClickListener검증을 추가했다. - 2026-06-27 리뷰 후속 구현:
ContentOverviewActivity.openAudioContentDetail(contentId)와ContentOverviewAdapterclick listener에contentId <= 0상세 이동 무시 조건을 추가했다.
- 생성:
-
Task 6.6: 신규 전체보기 loading UI 표시 후속 수정
- 수정:
app/src/main/res/layout/activity_content_overview.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivitySourceTest.kt
- 작업:
- 첫 페이지
Loading상태에서 사용자에게 보이는 중앙 progress view를 표시한다. Content.isLoadingMore가 true일 때 기존 목록은 유지하고 하단 progress view를 표시한다.Empty,Error,Content의 비로딩 상태에서는 progress view를 숨긴다.- skeleton loading, 신규 정렬/필터, Phase 7 신규 전체보기 라우팅은 추가하지 않는다.
- 첫 페이지
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest" - Expected: first-page/loading-more progress source 테스트 PASS.
- Run:
- 검증 기록:
- 구현 시 이 Task 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 작업 결정: PRD의 첫 페이지 로딩/추가 페이지 로딩 상태 구분 요구를 엄격히 충족하기 위해
LoadingDialog가 아닌 inline progress view 2개를 추가하는 최소 수정으로 진행한다. - 2026-06-27 RED 테스트 추가:
ContentOverviewActivitySourceTest에 first-page progress id, load-more progress id,viewModel.isLoading.observe(this),renderLoading()progress 표시,state.isLoadingMore에 따른 load-more progress 표시 검증을 추가했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest" - RED 결과:
ContentOverviewActivitySourceTest.kt:48,ContentOverviewActivitySourceTest.kt:59에서 예상 실패했다. 원인은activity_content_overview.xml에 progress view가 없고ContentOverviewActivity가 로딩 progress를 바인딩하지 않았기 때문이다. - 2026-06-27 구현:
activity_content_overview.xml에pb_content_overview_initial_loading,pb_content_overview_load_more를 추가하고,ContentOverviewActivity에서viewModel.isLoading과Content.isLoadingMore에 따라 progress visibility를 제어하도록 했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest" - GREEN 결과: PASS.
- 2026-06-27 코드 리뷰 및 검증 재확인: 첫 페이지/loading-more progress view와 Activity binding이 유지되어 있고,
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"재실행 결과 PASS.
- 수정:
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 테스트 수정:
HomeMainFragmentSourceTest의 Phase 2 미연결 assertion을 Phase 7 라우팅 assertion으로 변경해ContentOverviewActivity/ContentOverviewTypeimport,viewHomeFirstAudioTitle.ivSectionTitleChevron.setOnClickListener,FIRST_AUDIO_CONTENTintent 생성을 요구하도록 했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"를 production 구현 전 병렬 실행했으나 Gradle/Kotlin daemon cache 충돌 및 동시 compile 오류로 테스트 assertion 결과까지 도달하지 못했다. 이후./gradlew --stop으로 daemon을 정리하고 순차 검증으로 전환했다. - 2026-06-27 구현:
HomeMainFragment에ContentOverviewActivity,ContentOverviewTypeimport를 추가하고viewHomeFirstAudioTitle.ivSectionTitleChevron.setOnClickListener에서ensureMainV2NavigationAllowed를 거쳐ContentOverviewActivity.newIntent(requireContext(), ContentOverviewType.FIRST_AUDIO_CONTENT)를 실행하도록 연결했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" - GREEN 결과: PASS.
- 2026-06-27 리뷰 후속 보강:
HomeMainFragmentSourceTest가openFirstAudioOverview()함수 내부에서ensureMainV2NavigationAllowed가startActivity보다 먼저 호출되고FIRST_AUDIO_CONTENTintent를 생성하는 순서를 검증하도록 보강했다.
- 수정:
-
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 아래에 명령, 결과, 확인 내용을 한국어로 누적한다.
- 2026-06-27 RED 테스트 수정:
ContentMainFragmentSourceTest의New&Hot미연결 assertion을 Phase 7 라우팅 assertion으로 변경해ContentOverviewActivity/ContentOverviewTypeimport,viewContentNewAndHotTitle.ivSectionTitleChevron.setOnClickListener,NEW_AND_HOT_AUDIO이동 helper를 요구하도록 했다. - 2026-06-27 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"를 production 구현 전 병렬 실행했으나 Gradle/Kotlin daemon cache 충돌 및 동시 compile 오류로 테스트 assertion 결과까지 도달하지 못했다. 이후./gradlew --stop으로 daemon을 정리하고 순차 검증으로 전환했다. - 2026-06-27 구현:
ContentMainFragment에ContentOverviewActivity,ContentOverviewTypeimport를 추가하고viewContentNewAndHotTitle.ivSectionTitleChevron.setOnClickListener에서ensureMainV2NavigationAllowed를 거쳐ContentOverviewActivity.newIntent(requireContext(), ContentOverviewType.NEW_AND_HOT_AUDIO)경로로 이동하도록 연결했다. 기존 전체 탭 이동 대상 섹션의openContentAllTab연결은 유지했다. - 2026-06-27 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" - GREEN 결과: PASS. 최초 GREEN 시도에서는 source assertion이 helper 구조가 아닌 literal intent 호출을 기대해 1회 실패했고, production 코드는 유지한 채
openContentOverview(ContentOverviewType.NEW_AND_HOT_AUDIO)/ContentOverviewActivity.newIntent(requireContext(), type)구조를 검증하도록 테스트를 조정한 뒤 PASS했다. - 2026-06-27 리뷰 후속 보강:
ContentMainFragmentSourceTest가openContentOverview(type)함수 내부에서ensureMainV2NavigationAllowed가startActivity보다 먼저 호출되고ContentOverviewActivity.newIntent(requireContext(), type)를 실행하는 순서를 검증하도록 보강했다.
- 수정:
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/미완료로 기록했다.
- 2026-06-27 Phase 5~6 범위 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 인접 회귀 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 인접 회귀 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 범위 실행:
./gradlew :app:mergeDebugResources결과 PASS. - 2026-06-27 Phase 5~6 범위 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 범위 실행:
./gradlew :app:ktlintCheck는 신규 overview 테스트의 긴 줄/unused import로 1회 실패 후 포맷 수정, 재실행 결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 5~6 범위 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 5~6 범위 확인:
rg -n "viewHomeFirstAudioTitle\.ivSectionTitleChevron\.setOnClickListener|viewContentNewAndHotTitle\.ivSectionTitleChevron\.setOnClickListener" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home app/src/main/java/kr/co/vividnext/sodalive/v2/main/content결과 매치 없음. Phase 7 신규 전체보기 이동 라우팅은 아직 추가하지 않았다. - 2026-06-27 Phase 5~6 코드 리뷰 확인: 문서 기준 발견 사항 2건을 확인했다. 1) 신규 API DTO 필드명이 PRD/계획서의
items/contentId/coverImage와 달리contents/audioContentId/imageUrl로 구현되어 서버 계약 확인 또는 수정이 필요하다. 2)contentId <= 0item 상세 이동 무시 조건이 누락되어 수정이 필요하다. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:mergeDebugResources는 sandbox의~/.gradlelock 접근 제한으로 1회 실패 후 권한 상승 재실행 결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:ktlintCheck결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 5~6 인접 회귀 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 인접 회귀 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 예상 실패. 테스트가 문서 계약items/contentId/coverImage와 invalidcontentId <= 0guard를 요구하지만 production이 아직 이전 DTO 계약과 guard 누락 상태였음을 확인했다. - 2026-06-27 Phase 5~6 리뷰 후속 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 확인:
rg -n "totalCount|contents|audioContentId|imageUrl" "app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview" "app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/overview"실행 결과audioContentId,imageUrl잔여 참조는 없고contents는 API path/문자열 이름의 정상 매치만 남았다. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
./gradlew :app:ktlintCheck는 테스트 fixture 들여쓰기 문제로 1회 실패 후 수정, 재실행 결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 5~6 리뷰 후속 재검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Git 상태 확인:
app/src/main/res/drawable-mdpi/ic_new_arrow_up_gray.png,app/src/main/res/drawable-mdpi/ic_new_arrow_up_white.png가AD상태이며 현재 소스/문서에서 참조되지 않는다. 커밋 전 포함 여부 정리가 필요하다. - 2026-06-27 Phase 5~6 코드 리뷰 재확인:
ContentOverviewActivity.renderLoading()은 RecyclerView와 empty/error TextView를 숨기기만 하고,activity_content_overview.xml에도 loading/progress view가 없어 PRD의 첫 페이지 로딩/추가 페이지 로딩 상태 구분 요구가 사용자에게 표시되지 않는다.ContentOverviewViewModel.isLoading도 Activity에서 observe하지 않는다. 코드 수정이 필요하다. - 2026-06-27 Phase 5~6 fresh 검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 fresh 검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 fresh 검증 실행:
./gradlew :app:ktlintCheck결과 PASS. - 2026-06-27 Phase 5~6 fresh 검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 5~6 fresh 검증 실행:
./gradlew :app:mergeDebugResources는 sandbox의~/.gradlewrapper lock 접근 제한으로 1회 실패 후 권한 상승 재실행 결과 PASS. - 2026-06-27 Phase 5~6 인접 회귀 fresh 검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 인접 회귀 fresh 검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"결과 PASS. - 2026-06-27 Phase 5~6 loading UI 후속 작업 시작: PRD의 첫 페이지 로딩/추가 페이지 로딩 상태 구분 요구를 사용자에게 보이는 UI로 반영하기 위해 Task 6.6을 추가했다. Phase 7 라우팅과 legacy 파일 수정은 범위에서 제외한다.
- 2026-06-27 Phase 5~6 loading UI 후속 RED 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest"결과 예상 실패. layout progress id와 Activity 로딩 바인딩이 아직 없음을 확인했다. - 2026-06-27 Phase 5~6 loading UI 후속 GREEN 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest"결과 PASS. - 2026-06-27 Phase 5~6 loading UI 후속 검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 loading UI 후속 검증 실행:
./gradlew :app:mergeDebugResources결과 PASS. - 2026-06-27 Phase 5~6 loading UI 후속 검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 loading UI 후속 검증 실행:
./gradlew :app:ktlintCheck결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 5~6 loading UI 후속 검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 5~6 코드 리뷰 및 검증 재확인: Phase 5 API/DTO/Repository/UI model과 Phase 6 ViewModel/Activity/Adapter 구현을 PRD/계획서 기준으로 재검토했고 추가 코드 수정이 필요한 발견 사항은 없었다.
Task 6.6은 코드와 검증 기록이 완료 상태라 체크박스를 완료로 갱신했다. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*"결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:mergeDebugResources는 sandbox의~/.gradlewrapper lock 접근 제한으로 1회 실패 후 권한 상승 재실행 결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
./gradlew :app:ktlintCheck결과 PASS. - 2026-06-27 Phase 5~6 재검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 7 범위 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest"결과 PASS. - 2026-06-27 Phase 7 범위 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 7 범위 실행:
./gradlew :app:ktlintCheck는HomeMainFragmentSourceTest의 긴 assertion 줄로 1회 실패 후 포맷 수정, 재실행 결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 7 범위 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 7 수동 화면 검증: 현재 작업 환경에서 Android 기기 또는 에뮬레이터를 통한 실제 화면 조작을 수행할 수 없어 홈
처음부터 함께 성장!및 콘텐츠New&Hotchevron 클릭 후 신규 전체보기 화면 진입은 실제 앱 화면에서 확인하지 못했다. source 테스트와 compile/ktlint로 라우팅 계약은 검증했다. - 2026-06-27 Phase 7 리뷰 게이트 결과: source 테스트가 신규 overview 라우팅의
ensureMainV2NavigationAllowedguard 경유를 충분히 고정하지 못한다는 지적으로 1회 Reject를 받았다. production 코드는 유지하고 홈/콘텐츠 source 테스트의 guard 내부 순서 검증을 보강했다. - 2026-06-27 Phase 7 리뷰 후속 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest"결과 PASS. - 2026-06-27 Phase 7 리뷰 후속 재검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 7 리뷰 후속 재검증 실행:
./gradlew :app:ktlintCheck결과 PASS. 기존.editorconfig의disabled_rulesdeprecation 경고는 출력되었으나 실패 원인은 아니었다. - 2026-06-27 Phase 7 리뷰 후속 재검증 실행:
git diff --check결과 PASS(출력 없음). - 2026-06-27 Phase 7 코드 리뷰 및 점검: 홈
처음부터 함께 성장!과 콘텐츠New&Hotchevron이 각각ContentOverviewType.FIRST_AUDIO_CONTENT,ContentOverviewType.NEW_AND_HOT_AUDIO로ContentOverviewActivity.newIntent(...)를 호출하고, 두 경로 모두ensureMainV2NavigationAllowedguard를 먼저 경유하는 것을 확인했다. 추가 코드 수정이 필요한 발견 사항은 없었다. - 2026-06-27 Phase 7 코드 리뷰 및 점검 재검증 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.overview.ContentOverviewActivitySourceTest"결과 PASS. - 2026-06-27 Phase 7 코드 리뷰 및 점검 재검증 실행:
./gradlew :app:compileDebugKotlin결과 PASS. - 2026-06-27 Phase 7 코드 리뷰 및 점검 재검증 실행:
./gradlew :app:ktlintCheck결과 PASS. - 2026-06-27 Phase 7 코드 리뷰 및 점검 재검증 실행:
git diff --check결과 PASS(출력 없음).