From a9446ef5cc601f3d29aeb9c21843f494de4719d0 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 24 Jun 2026 14:45:53 +0900 Subject: [PATCH] =?UTF-8?q?docs(content):=20=EB=9E=AD=ED=82=B9=20=ED=83=AD?= =?UTF-8?q?=20Phase=204-5=20=EA=B2=80=EC=A6=9D=EC=9D=84=20=EA=B8=B0?= =?UTF-8?q?=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/docs/20260623_메인_콘텐츠_탭_내부_랭킹_탭/plan-task.md b/docs/20260623_메인_콘텐츠_탭_내부_랭킹_탭/plan-task.md index b6b6c840..23d4b8b1 100644 --- a/docs/20260623_메인_콘텐츠_탭_내부_랭킹_탭/plan-task.md +++ b/docs/20260623_메인_콘텐츠_탭_내부_랭킹_탭/plan-task.md @@ -394,7 +394,7 @@ ### Phase 4: ViewModel과 DI 등록 -- [ ] **Task 4.1: UI state와 ViewModel 작성** +- [x] **Task 4.1: UI state와 ViewModel 작성** - 생성: - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/model/AudioRankingsUiState.kt` - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentRankingViewModel.kt` @@ -412,9 +412,15 @@ - Run: `./gradlew :app:compileDebugKotlin` - Expected: ViewModel 컴파일 성공. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24 RED: `ContentRankingViewModelTest`를 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 시 `Unresolved reference 'AudioRankingsUiState'`, `Unresolved reference 'ContentRankingViewModel'`, `Unresolved reference 'loadRankings'` 등으로 실패해 UI state/ViewModel 미구현 상태를 확인했다. + - 2026-06-24 GREEN: `AudioRankingsUiState`, `ContentRankingViewModel`을 추가해 초기 `WEEKLY_POPULAR`, token/type API 호출, content/empty/error/throwable, 중복 호출 방지, force reload, stale response 무시를 구현했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 결과 `BUILD SUCCESSFUL`. + - 2026-06-24: Kotlin LSP 진단은 환경에 `kotlin-lsp`가 설치되어 있지 않아 실행 불가(`Command not found: kotlin-lsp`). Gradle test/compile로 대체 검증했다. + - 2026-06-24 리뷰 보완 RED: 리뷰 게이트에서 이미 로드한 랭킹 타입 재선택 시 API 호출은 막히지만 캐시된 상태를 재방출하지 않아 이전 타입 목록이 남을 수 있음을 지적했다. `이미 로드한 type을 다시 선택하면 API 재호출 없이 캐시된 상태를 emit한다` 테스트를 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 시 `ContentRankingViewModelTest.kt:146` assertion 실패로 RED를 확인했다. + - 2026-06-24 리뷰 보완 GREEN: `ContentRankingViewModel`의 `loadedTypes`를 `cachedStates`로 변경해 `Content`/`Empty` 성공 상태를 타입별로 저장하고, 이미 로드한 타입 재선택 시 API 재호출 없이 `_rankingStateLiveData`에 캐시 상태를 emit하도록 수정했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 결과 `BUILD SUCCESSFUL`. + - 2026-06-24 Phase 4/5 리뷰 보완 RED: 이전 type 요청 응답이 늦게 도착하면 화면 상태 반영은 막히지만 `_isLoading=false`가 먼저 emit되어 현재 type 요청의 로딩 다이얼로그가 조기 종료될 수 있음을 확인했다. `이전 type 응답이 늦게 도착해도 현재 로딩 상태를 끄지 않는다` 테스트를 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 시 `ContentRankingViewModelTest.kt:193` assertion 실패로 RED를 확인했다. + - 2026-06-24 Phase 4/5 리뷰 보완 GREEN: `ContentRankingViewModel`에 `latestRequestId`와 `isCurrentRequest(...)`를 추가해 최신 요청만 loading/state/toast를 마감하도록 수정했다. 캐시 상태 재방출 시에도 이전 pending 요청을 무효화하고 `_isLoading=false`를 emit하도록 정리했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"` 실행 결과 `BUILD SUCCESSFUL`. -- [ ] **Task 4.2: Koin DI 등록** +- [x] **Task 4.2: Koin DI 등록** - 수정: - `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt` - 구현: @@ -426,13 +432,13 @@ - Run: `./gradlew :app:compileDebugKotlin` - Expected: Koin 등록과 import 컴파일 성공. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `AppDI.kt`에 `AudioRankingsApi`, `AudioRankingsRepository`, `ContentRankingViewModel` 등록을 추가했다. `./gradlew :app:compileDebugKotlin` 실행 결과 `BUILD SUCCESSFUL`. --- ### Phase 5: 레이아웃과 탭 전환 UI 연결 -- [ ] **Task 5.1: 랭킹 layout/source RED 테스트 추가** +- [x] **Task 5.1: 랭킹 layout/source RED 테스트 추가** - 수정: - `app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt` - 추가 테스트: @@ -446,9 +452,10 @@ - Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"` - Expected: layout id/ViewModel/adapter 연결 미구현으로 RED 실패. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24 RED: `ContentMainFragmentSourceTest`에 랭킹 layout id, capsule tab include, `ContentRankingViewModel`, `ContentRankingAdapter.createGridLayoutManager(requireContext())`, `AudioRankingType.WEEKLY_POPULAR`, Text Tab/Capsule Tab 연결 source assertion을 추가했다. 같은 시점의 테스트 컴파일은 Phase 4 RED 테스트의 `ContentRankingViewModel` 미구현 오류로 먼저 실패해 Fragment source 미구현 상태를 함께 확인했다. + - 2026-06-24 GREEN: Phase 5 production 연결 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"` 실행 결과 `BUILD SUCCESSFUL`. -- [ ] **Task 5.2: strings 추가** +- [x] **Task 5.2: strings 추가** - 수정: - `app/src/main/res/values/strings.xml` - `app/src/main/res/values-en/strings.xml` @@ -467,9 +474,9 @@ - Run: `./gradlew :app:mergeDebugResources` - Expected: 문자열 리소스 merge 성공. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `values`, `values-en`, `values-ja`에 콘텐츠 Text Tab `랭킹`/`전체`와 랭킹 유형 6개 라벨 문자열을 추가했다. `./gradlew :app:mergeDebugResources` 실행 결과 `BUILD SUCCESSFUL`. -- [ ] **Task 5.3: `fragment_v2_main_content.xml`에 랭킹 container 추가** +- [x] **Task 5.3: `fragment_v2_main_content.xml`에 랭킹 container 추가** - 수정: - `app/src/main/res/layout/fragment_v2_main_content.xml` - 구현: @@ -509,9 +516,9 @@ - Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"` - Expected: resource merge 성공, source test는 Fragment 연결 전 일부 RED 유지 가능. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `fragment_v2_main_content.xml`의 `text_tab_bar_content` 아래에 `view_content_ranking_type_tabs` include와 `rv_content_rankings`를 초기 `gone` 상태로 추가했다. `./gradlew :app:mergeDebugResources` 실행 결과 `BUILD SUCCESSFUL`, `ContentMainFragmentSourceTest`에서 layout id/source assertion `BUILD SUCCESSFUL`. -- [ ] **Task 5.4: `ContentMainFragment`에 Text Tab 전환과 랭킹 탭 UI 연결** +- [x] **Task 5.4: `ContentMainFragment`에 Text Tab 전환과 랭킹 탭 UI 연결** - 수정: - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt` - 구현: @@ -529,9 +536,9 @@ - Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"` - Expected: Fragment source 연결 관련 assertion PASS. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `ContentMainFragment`에 `ContentRankingViewModel`, content tab 상수, `추천`/`랭킹`/`전체` Text Tab 메뉴와 visibility 전환을 연결했다. `랭킹` 최초 선택 시 `contentRankingViewModel.loadRankings(AudioRankingType.WEEKLY_POPULAR)`를 호출하고, `전체`는 별도 API/content 없이 추천/랭킹 surface를 숨기도록 유지했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"` 실행 결과 `BUILD SUCCESSFUL`. -- [ ] **Task 5.5: 랭킹 유형 Capsule Tab 연결** +- [x] **Task 5.5: 랭킹 유형 Capsule Tab 연결** - 수정: - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt` - 구현: @@ -543,9 +550,9 @@ - Run: `./gradlew :app:compileDebugKotlin` - Expected: Capsule Tab 연결 컴파일 성공. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `view_content_ranking_type_tabs.root.setMenus(...)`에 `AudioRankingType.entries` 순서대로 6개 문자열 리소스 라벨을 설정하고, `setOnTabSelectedListener`에서 `AudioRankingType.entries[index]`를 `loadRankings(type)`로 연결했다. `./gradlew :app:compileDebugKotlin` 실행 결과 `BUILD SUCCESSFUL`. -- [ ] **Task 5.6: ranking observer와 adapter submit 연결** +- [x] **Task 5.6: ranking observer와 adapter submit 연결** - 수정: - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt` - 구현: @@ -558,7 +565,7 @@ - Run: `./gradlew :app:compileDebugKotlin` - Expected: Fragment/adapter/ViewModel 연결 컴파일 성공. - 검증 기록: - - 구현 시 실행 결과를 이 아래에 누적한다. + - 2026-06-24: `rv_content_rankings`에 `ContentRankingAdapter.createGridLayoutManager(requireContext())`와 `ContentRankingAdapter`를 연결하고, `rankingStateLiveData`의 `Content`는 `submitItems(state.items)`, `Empty`/`Error`는 `submitItems(emptyList())`로 처리했다. 추천/랭킹 loading 상태는 `updateLoadingDialog()`에서 함께 반영하고, ranking `toastLiveData`는 기존 `showToast` helper를 재사용했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:compileDebugKotlin` 실행 결과 모두 `BUILD SUCCESSFUL`. --- @@ -632,3 +639,9 @@ - 2026-06-24: Kotlin LSP 진단은 환경에 `kotlin-lsp`가 설치되어 있지 않아 실행 불가(`Command not found: kotlin-lsp`). Gradle compile/test/ktlint로 대체 검증했다. - 2026-06-24: 리뷰 게이트 보완 후 `AudioRankingResponse.type` 추가. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 재실행 결과 모두 통과했다. - 2026-06-24: Phase 1~3 코드 리뷰 및 현재 워크트리 기준 재검증을 수행했다. Phase 1 구조 확인용 `rg` 3개 명령에서 기대 참조를 확인했고, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.contentranking.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 실행 결과 모두 통과했다. `./gradlew :app:mergeDebugResources`는 샌드박스 내 Gradle wrapper lock 접근 제한으로 1차 실패 후 권한 상승 실행에서 `BUILD SUCCESSFUL`을 확인했다. +- 2026-06-24: Phase 4~5 구현 중 병렬 Gradle 실행에서 KSP/Kotlin incremental cache 접근 충돌(`NoSuchFileException .../kspCaches/debug/backups`, `Storage ... lookups.tab is already registered`)이 발생했으나, 동일 명령을 순차 재실행해 실제 코드 오류가 아님을 분리했다. +- 2026-06-24: Phase 4~5 구현 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 실행 결과 모두 통과했다. `ktlintCheck`의 `.editorconfig disabled_rules` deprecation 경고는 기존 설정 경고로 남아 있다. +- 2026-06-24: Figma `24:6857`, `24:6882`를 참조해 Text Tab `추천`/`랭킹`/`전체`, Capsule Tab `주간 인기`/`지금 뜨는 중`/`매출`/`판매량`/`댓글수`/`좋아요` 순서와 랭킹 목록 위치를 확인했다. 에뮬레이터 실기 화면 수동 검증은 Phase 6 범위로 남겨두었다. +- 2026-06-24: 리뷰 게이트 보완 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 재실행 결과 모두 통과했다. +- 2026-06-24: Phase 4~5 코드 리뷰에서 stale response loading 조기 종료 문제를 발견해 RED/GREEN 테스트로 보완했다. Figma `24:6857` 스크린샷으로 Text Tab `추천`/`랭킹`/`전체`, Capsule Tab `주간 인기`/`지금 뜨는 중`/`매출`/`판매량`/`댓글수`/`좋아요` 순서와 랭킹 목록 위치를 재확인했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 실행 결과 모두 통과했다. `:app:mergeDebugResources`는 샌드박스 내 Gradle wrapper lock 접근 제한으로 1차 실패 후 권한 상승 실행에서 `BUILD SUCCESSFUL`을 확인했다. `ktlintCheck`의 `.editorconfig disabled_rules` deprecation 경고는 기존 설정 경고로 남아 있다. +- 2026-06-24: Phase 4~5 현재 워크트리 기준 코드 리뷰 및 재검증을 수행했다. `ContentRankingViewModel`의 캐시 재방출, stale response 무시, loading 종료 조건과 `ContentMainFragment`의 Text Tab/Capsule Tab 연결, visibility 전환, ranking adapter 연결, loading dialog 통합, routing guard를 확인했고 추가 결함은 발견하지 못했다. Figma `24:6857`, `24:6882` design context와 screenshot으로 `추천`/`랭킹`/`전체`, `주간 인기`/`지금 뜨는 중`/`매출`/`판매량`/`댓글수`/`좋아요` 순서와 랭킹 목록 위치를 대조했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 실행 결과 모두 통과했다. `:app:mergeDebugResources`는 샌드박스 내 Gradle wrapper lock 접근 제한으로 1차 실패 후 권한 상승 실행에서 `BUILD SUCCESSFUL`을 확인했다.