feat(main): 메인 탭 당겨서 새로고침을 추가한다
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentSourceTest.kt`
|
||||
|
||||
- [ ] **Task 1.1: 홈 layout source test를 먼저 추가한다**
|
||||
- [x] **Task 1.1: 홈 layout source test를 먼저 추가한다**
|
||||
|
||||
`HomeMainFragmentSourceTest.kt`에 아래 검증을 추가한다.
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
Expected: FAIL because `swipe_home_recommendation`, `swipe_home_ranking`, `swipe_home_following` do not exist yet.
|
||||
|
||||
- [ ] **Task 1.2: 홈 layout에 탭별 SwipeRefreshLayout을 추가한다**
|
||||
- [x] **Task 1.2: 홈 layout에 탭별 SwipeRefreshLayout을 추가한다**
|
||||
|
||||
`fragment_v2_main_home.xml`에서 `text_tab_bar_home` 아래 콘텐츠 3개를 각각 아래 구조로 감싼다. 기존 child ID는 유지한다.
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
Expected: BUILD SUCCESSFUL.
|
||||
|
||||
- [ ] **Task 1.3: 홈 Fragment source test를 추가한다**
|
||||
- [x] **Task 1.3: 홈 Fragment source test를 추가한다**
|
||||
|
||||
`HomeMainFragmentSourceTest.kt`에 아래 검증을 추가한다.
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
|
||||
Expected: FAIL because refresh dispatch code does not exist yet.
|
||||
|
||||
- [ ] **Task 1.4: 홈 Fragment에 refresh listener와 visibility 전환을 연결한다**
|
||||
- [x] **Task 1.4: 홈 Fragment에 refresh listener와 visibility 전환을 연결한다**
|
||||
|
||||
`HomeMainFragment.kt`의 `onViewCreated()`에서 `setUpFollowingAdapters()` 다음에 `setUpRefreshLayouts()`를 호출한다.
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Task 1.5: 홈 Phase 검증 기록을 남긴다**
|
||||
- [x] **Task 1.5: 홈 Phase 검증 기록을 남긴다**
|
||||
|
||||
Run:
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
@@ -261,6 +261,12 @@
|
||||
|
||||
검증 성공 후 이 Phase 아래에 `검증 기록`을 한국어로 누적한다.
|
||||
|
||||
검증 기록:
|
||||
- 2026-07-22: `HomeMainFragmentSourceTest.kt`에 홈 layout refresh container 검증과 현재 홈 탭별 refresh dispatch 검증을 먼저 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`를 실행했다. 초기 2회는 Gradle 빌드 시간 초과로 테스트 결과 전 종료되었고, 3회 실행에서 신규 테스트 2개가 `HomeMainFragmentSourceTest.kt:62`, `HomeMainFragmentSourceTest.kt:77`의 `assertTrue` 실패로 RED가 확인되었다.
|
||||
- 2026-07-22: `fragment_v2_main_home.xml`에서 홈 추천/랭킹/팔로잉 콘텐츠 표면을 각각 `swipe_home_recommendation`, `swipe_home_ranking`, `swipe_home_following`으로 감싸고 기존 child ID를 유지했다. `HomeMainFragment.kt`에서 `setUpRefreshLayouts()`, 현재 탭별 재조회, wrapper visibility 전환, loading dismiss 시 indicator 종료를 연결했다.
|
||||
- 2026-07-22: 구현 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`를 실행해 BUILD SUCCESSFUL을 확인했다.
|
||||
- 2026-07-22: Phase 1 최종 검증으로 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`를 실행했고 모두 BUILD SUCCESSFUL이다. Gradle deprecation warning은 기존 빌드 경고로 확인되었고 이번 변경 파일과 직접 관련된 오류는 없었다.
|
||||
|
||||
### Phase 2: 콘텐츠 탭 새로고침 연결
|
||||
|
||||
**Files:**
|
||||
@@ -269,7 +275,7 @@
|
||||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.kt`
|
||||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentRankingViewModelTest.kt`
|
||||
|
||||
- [ ] **Task 2.1: 콘텐츠 layout source test를 먼저 추가한다**
|
||||
- [x] **Task 2.1: 콘텐츠 layout source test를 먼저 추가한다**
|
||||
|
||||
`ContentMainFragmentSourceTest.kt`에 아래 검증을 추가한다.
|
||||
|
||||
@@ -293,7 +299,7 @@
|
||||
|
||||
Expected: FAIL because content refresh wrappers do not exist yet.
|
||||
|
||||
- [ ] **Task 2.2: 콘텐츠 layout에 SwipeRefreshLayout을 추가한다**
|
||||
- [x] **Task 2.2: 콘텐츠 layout에 SwipeRefreshLayout을 추가한다**
|
||||
|
||||
추천 `NestedScrollView`를 `swipe_content_recommendation`으로 감싼다.
|
||||
|
||||
@@ -378,7 +384,7 @@
|
||||
|
||||
Expected: BUILD SUCCESSFUL.
|
||||
|
||||
- [ ] **Task 2.3: 콘텐츠 refresh 분기 source test를 추가한다**
|
||||
- [x] **Task 2.3: 콘텐츠 refresh 분기 source test를 추가한다**
|
||||
|
||||
`ContentMainFragmentSourceTest.kt`에 아래 검증을 추가한다.
|
||||
|
||||
@@ -405,7 +411,7 @@
|
||||
|
||||
Expected: FAIL because refresh dispatch code does not exist yet.
|
||||
|
||||
- [ ] **Task 2.4: 콘텐츠 Fragment에 refresh listener를 연결한다**
|
||||
- [x] **Task 2.4: 콘텐츠 Fragment에 refresh listener를 연결한다**
|
||||
|
||||
`ContentMainFragment.kt`에서 `setUpAdapters()` 다음에 `setUpRefreshLayouts()`를 호출한다.
|
||||
|
||||
@@ -495,7 +501,7 @@
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Task 2.5: 콘텐츠 랭킹 force refresh 테스트를 실행한다**
|
||||
- [x] **Task 2.5: 콘텐츠 랭킹 force refresh 테스트를 실행한다**
|
||||
|
||||
`ContentRankingViewModelTest.kt`에는 이미 아래 테스트가 있으므로 새 테스트를 추가하지 않는다.
|
||||
|
||||
@@ -518,7 +524,7 @@
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Task 2.6: 콘텐츠 Phase 검증 기록을 남긴다**
|
||||
- [x] **Task 2.6: 콘텐츠 Phase 검증 기록을 남긴다**
|
||||
|
||||
Run:
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
@@ -531,6 +537,13 @@
|
||||
|
||||
검증 성공 후 이 Phase 아래에 `검증 기록`을 한국어로 누적한다.
|
||||
|
||||
검증 기록:
|
||||
- 2026-07-22: `ContentMainFragmentSourceTest.kt`에 콘텐츠 layout refresh container 검증과 현재 콘텐츠 탭별 refresh dispatch 검증을 먼저 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"`를 실행했다. 신규 테스트 2개가 각각 `ContentMainFragmentSourceTest.kt:78`, `ContentMainFragmentSourceTest.kt:94`의 `AssertionError`로 실패해 RED를 확인했다.
|
||||
- 2026-07-22: `fragment_v2_main_content.xml`에서 콘텐츠 추천/랭킹/전체 콘텐츠 표면을 각각 `swipe_content_recommendation`, `swipe_content_ranking`, `swipe_content_all`로 감싸고 기존 child ID를 유지했다. 전체 탭은 `swipe_content_all`의 단일 direct child `FrameLayout` 안에 `rv_content_all_items`와 `layout_content_all_empty_error`를 보존했다.
|
||||
- 2026-07-22: `ContentMainFragment.kt`에서 `setUpAdapters()` 다음 `setUpRefreshLayouts()` 호출, 현재 콘텐츠 탭별 재조회, 랭킹 현재 type `force = true` 재조회, wrapper visibility 전환, loading dismiss 시 refresh indicator 종료를 연결했다.
|
||||
- 2026-07-22: 구현 후 `./gradlew :app:mergeDebugResources`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"`를 실행했고 모두 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: Phase 2 최종 검증으로 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest"`를 실행했고 모두 BUILD SUCCESSFUL이다. Gradle deprecation warning은 기존 빌드 경고로 확인되었고 이번 변경 파일과 직접 관련된 오류는 없었다.
|
||||
|
||||
### Phase 3: 대화 탭 새로고침 연결
|
||||
|
||||
**Files:**
|
||||
@@ -539,7 +552,7 @@
|
||||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragmentLayoutTest.kt`
|
||||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainViewModelTest.kt`
|
||||
|
||||
- [ ] **Task 3.1: 대화 layout test를 먼저 추가한다**
|
||||
- [x] **Task 3.1: 대화 layout test를 먼저 추가한다**
|
||||
|
||||
`ChatMainFragmentLayoutTest.kt`에 아래 검증을 추가하고, 기존 `채팅 fragment layout은 title bar tab list만 포함한다`, `채팅 fragment list는 계획된 constraint를 사용한다` 테스트는 `rv_chat_rooms`와 `tv_chat_empty_message`의 부모가 root가 아니라 `swipe_chat_rooms` 내부 `FrameLayout`임을 기준으로 갱신한다.
|
||||
|
||||
@@ -561,7 +574,7 @@
|
||||
|
||||
Expected: FAIL because `swipe_chat_rooms` does not exist yet.
|
||||
|
||||
- [ ] **Task 3.2: 대화 layout에 SwipeRefreshLayout을 추가한다**
|
||||
- [x] **Task 3.2: 대화 layout에 SwipeRefreshLayout을 추가한다**
|
||||
|
||||
`fragment_v2_main_chat.xml`에서 `rv_chat_rooms`와 `tv_chat_empty_message`를 하나의 `SwipeRefreshLayout` 안 `FrameLayout`으로 감싼다.
|
||||
|
||||
@@ -606,7 +619,7 @@
|
||||
|
||||
Expected: BUILD SUCCESSFUL.
|
||||
|
||||
- [ ] **Task 3.3: 대화 Fragment source test를 추가한다**
|
||||
- [x] **Task 3.3: 대화 Fragment source test를 추가한다**
|
||||
|
||||
`ChatMainFragmentLayoutTest.kt`에 아래 검증을 추가한다.
|
||||
|
||||
@@ -627,7 +640,7 @@
|
||||
|
||||
Expected: FAIL because refresh listener code does not exist yet.
|
||||
|
||||
- [ ] **Task 3.4: 대화 Fragment에 refresh listener를 연결한다**
|
||||
- [x] **Task 3.4: 대화 Fragment에 refresh listener를 연결한다**
|
||||
|
||||
`ChatMainFragment.kt`의 `onViewCreated()`에서 `setupChatRooms()` 다음에 `setupRefresh()`를 호출한다.
|
||||
|
||||
@@ -664,7 +677,7 @@
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Task 3.5: 대화 ViewModel 첫 페이지 교체 테스트를 추가한다**
|
||||
- [x] **Task 3.5: 대화 ViewModel 첫 페이지 교체 테스트를 추가한다**
|
||||
|
||||
`ChatMainViewModelTest.kt`에 같은 filter 첫 페이지 재호출이 기존 목록을 교체하고 cursor 없이 요청하는 테스트를 추가한다.
|
||||
|
||||
@@ -693,7 +706,7 @@
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Task 3.6: 대화 Phase 검증 기록을 남긴다**
|
||||
- [x] **Task 3.6: 대화 Phase 검증 기록을 남긴다**
|
||||
|
||||
Run:
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
@@ -705,13 +718,20 @@
|
||||
|
||||
검증 성공 후 이 Phase 아래에 `검증 기록`을 한국어로 누적한다.
|
||||
|
||||
검증 기록:
|
||||
- 2026-07-22: `ChatMainFragmentLayoutTest.kt`에 대화 layout refresh wrapper 검증, 기존 부모/constraint 기대값 갱신, 대화 refresh source 검증을 먼저 추가했다. 초기 1회 실행은 신규 `R.id.swipe_chat_rooms`가 아직 생성되지 않아 테스트 컴파일 오류로 멈춰, 새 ID 부재를 런타임 실패로 검증하도록 기존 helper 기반 조회로 수정했다.
|
||||
- 2026-07-22: 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest"`를 실행했고, `chat layout wraps room list and empty message in swipe refresh`는 `ChatMainFragmentLayoutTest.kt:75`, `chat refresh reloads first page with selected filter`는 `ChatMainFragmentLayoutTest.kt:154`의 `AssertionError`로 실패했다. 갱신된 기존 layout 테스트 2개도 `swipe_chat_rooms` 부재로 실패해 RED를 확인했다.
|
||||
- 2026-07-22: `ChatMainViewModelTest.kt`에 `loadFirstPage with current filter requests first page and replaces existing rooms` 테스트를 추가한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest"`를 실행했고 BUILD SUCCESSFUL을 확인했다. 기존 `loadFirstPage(filter)` 구현이 같은 filter 첫 페이지 재호출, null cursor 요청, 기존 목록 교체를 이미 만족해 ViewModel production 코드는 변경하지 않았다.
|
||||
- 2026-07-22: `fragment_v2_main_chat.xml`에서 `rv_chat_rooms`와 `tv_chat_empty_message`를 `swipe_chat_rooms` 안의 단일 direct child `FrameLayout`로 감싸고, 기존 list/empty constraint는 wrapper로 옮겼다. `ChatMainFragment.kt`에서는 `setupChatRooms()` 다음 `setupRefresh()`를 호출하고, refresh 시 `viewModel.loadFirstPage(selectedFilter)`를 실행하며 loading dismiss 시 `binding.swipeChatRooms.isRefreshing = false`로 indicator를 닫도록 연결했다.
|
||||
- 2026-07-22: Phase 3 최종 검증으로 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest"`를 실행했고 모두 BUILD SUCCESSFUL이다. Gradle deprecation warning은 기존 빌드 경고로 확인되었고 이번 변경 파일과 직접 관련된 오류는 없었다.
|
||||
|
||||
### Phase 4: 통합 검증과 문서 갱신
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/20260722_메인_홈_콘텐츠_대화_탭_당겨서_새로고침/prd.md`
|
||||
- Modify: `docs/20260722_메인_홈_콘텐츠_대화_탭_당겨서_새로고침/plan-task.md`
|
||||
|
||||
- [ ] **Task 4.1: PRD Open Questions 결과를 문서에 반영한다**
|
||||
- [x] **Task 4.1: PRD Open Questions 결과를 문서에 반영한다**
|
||||
|
||||
`prd.md`의 `Open Questions` 아래에 구현 계획 확정 내용을 추가한다.
|
||||
|
||||
@@ -727,7 +747,7 @@
|
||||
|
||||
Expected: added lines are found.
|
||||
|
||||
- [ ] **Task 4.2: 전체 변경 검증을 실행한다**
|
||||
- [x] **Task 4.2: 전체 변경 검증을 실행한다**
|
||||
|
||||
Run:
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
@@ -739,7 +759,7 @@
|
||||
|
||||
Expected: all BUILD SUCCESSFUL. 기존 경고가 있으면 경고 문구와 변경 관련 여부를 `Verification Log`에 기록한다.
|
||||
|
||||
- [ ] **Task 4.3: 수동 QA를 수행한다**
|
||||
- [x] **Task 4.3: 수동 QA를 수행한다**
|
||||
|
||||
연결 기기가 있으면 실행한다.
|
||||
|
||||
@@ -759,7 +779,7 @@
|
||||
|
||||
기기가 없으면 `adb devices` 결과를 `Verification Log`에 남기고 수동 QA는 환경 차단으로 표시한다.
|
||||
|
||||
- [ ] **Task 4.4: 최종 문서 검증 기록을 누적한다**
|
||||
- [x] **Task 4.4: 최종 문서 검증 기록을 누적한다**
|
||||
|
||||
`plan-task.md` 최하단 `Verification Log`와 `prd.md`의 `Verification Log`에 아래 형식으로 실제 실행 결과를 누적한다.
|
||||
|
||||
@@ -776,9 +796,116 @@
|
||||
4. 각 ViewModel의 기존 첫 페이지 재조회/force/cache 동작을 테스트로 고정한다.
|
||||
5. PRD Open Questions 확정 내용을 문서화하고 통합 검증을 수행한다.
|
||||
|
||||
### Phase 5: 요청 경합 방어 보완
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.kt`
|
||||
- Test: 관련 ViewModel/Fragment source tests
|
||||
|
||||
- [x] **Task 5.1: 중복 첫 페이지 요청과 오래된 응답 방어 RED 테스트를 추가한다**
|
||||
|
||||
Home 추천/랭킹/팔로잉, Content 추천 ViewModel은 첫 페이지 로딩 중 같은 load 요청을 무시하고 최신 요청 응답만 반영해야 한다.
|
||||
|
||||
- [x] **Task 5.2: Content 전체 탭 pagination 경합 RED 테스트를 추가한다**
|
||||
|
||||
`loadMore()` pending 중 첫 페이지 refresh가 실패해도 복원 state의 `isLoadingMore`는 `false`가 되어 이후 `loadMore()`가 재시도 가능해야 한다.
|
||||
|
||||
- [x] **Task 5.3: Fragment refresh 소유권 RED 테스트를 추가한다**
|
||||
|
||||
Chat은 refresh 시작 filter, Home/Content는 refresh 시작 tab과 같은 terminal 응답만 pull-refresh 보존/종료/top-scroll 처리를 해야 한다.
|
||||
|
||||
- [x] **Task 5.4: 최소 구현으로 RED 테스트를 통과시킨다**
|
||||
|
||||
전역 LoadingDialog 차단에 의존하지 않고 ViewModel request generation/중복 무시, ContentAll 복원 snapshot 정규화, Fragment refresh 시작 탭/필터 저장을 적용한다. Minor의 indicator 종료 지연은 대상 요청 소유권 기반 종료로 함께 완화한다.
|
||||
|
||||
- [x] **Task 5.5: 최종 검증과 리뷰 재요청을 수행한다**
|
||||
|
||||
관련 targeted unit test, `compileDebugKotlin`, `ktlintCheck`, `git diff --check`를 실행하고 `Verification Log`에 결과를 누적한다.
|
||||
|
||||
### Phase 6: 리뷰 Important 보완
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentRankingViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentAllTabViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt`
|
||||
- Test: 관련 ViewModel/Fragment source tests
|
||||
|
||||
- [x] **Task 6.1: 첫 페이지 in-flight key 중복 방어를 보완한다**
|
||||
|
||||
Content 랭킹/전체, Chat 첫 페이지 요청에서 같은 key가 진행 중이면 중복 호출을 무시하고, 다른 type/filter/sort/dayOfWeek 요청은 허용한다.
|
||||
|
||||
- [x] **Task 6.2: refresh indicator 종료를 대상 wrapper로 한정한다**
|
||||
|
||||
Home/Content loading dialog 집계에서 전체 indicator 종료를 제거하고, `finishHomePullRefresh(tab)` / `finishContentPullRefresh(tab)`에서 대상 wrapper만 종료한다.
|
||||
|
||||
- [x] **Task 6.3: 홈 추천 loading과 follow action loading을 분리한다**
|
||||
|
||||
`HomeRecommendationViewModel`의 추천 목록 loading과 `followCreators()` action loading을 별도 LiveData로 분리한다.
|
||||
|
||||
- [x] **Task 6.4: 콘텐츠 전체 탭 refresh 결과를 명시 신호로 처리한다**
|
||||
|
||||
동일 데이터 정상 응답을 실패 복원으로 오인하지 않도록 데이터 동등성 기반 `isSameAllTabPage()` 판정을 제거하고, ViewModel의 `ContentAllTabRefreshResult`로 성공/실패를 판단한다.
|
||||
|
||||
검증 기록:
|
||||
- 2026-07-22: RED 테스트로 `PullRefreshRequestRaceSourceTest`, `HomeMainFragmentSourceTest`, `ContentMainFragmentSourceTest`, `ContentRankingViewModelTest`, `ContentAllTabViewModelTest`, `ChatMainViewModelTest`, `HomeRecommendationViewModelTest`를 먼저 보강했다. 첫 실행은 `ContentAllTabRefreshResult`, `refreshResultLiveData`, `isFollowLoading` 미구현 컴파일 오류로 실패해 RED를 확인했다.
|
||||
- 2026-07-22: 같은 key 중복 요청 무시, 대상 wrapper indicator 종료, 추천/follow loading 분리, Content 전체 탭 명시 refresh 결과 처리를 최소 구현으로 반영했다. 이후 동일 targeted test 묶음은 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: `./gradlew :app:ktlintCheck` 첫 실행은 신규 source test의 line length/argument wrapping과 unused import로 실패했다. 줄바꿈과 import만 수정한 뒤 `./gradlew :app:ktlintCheck`를 재실행해 BUILD SUCCESSFUL을 확인했다.
|
||||
- 2026-07-22: ktlint 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.PullRefreshRequestRaceSourceTest" --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.ContentRankingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModelTest"`를 재실행했고 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: read-only 리뷰에서 `ContentAllTabViewModel` stale 첫 페이지 응답의 in-flight key 잔류와 Content 전체 탭 실패 시 indicator 미종료 가능성이 지적되었다. `ContentAllTabViewModelTest`에 stale 응답 후 같은 key 재요청 가능 테스트를 추가하고, `ContentMainFragmentSourceTest`는 failure cancel이 대상 wrapper를 끄도록 고정했다. 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`를 실행해 BUILD SUCCESSFUL을 확인했다.
|
||||
|
||||
## Verification Log
|
||||
|
||||
- 2026-07-22: `docs/20260722_메인_홈_콘텐츠_대화_탭_당겨서_새로고침/prd.md`를 기준으로 계획을 작성했다.
|
||||
- 2026-07-22: `app/build.gradle`에서 `androidx.swiperefreshlayout:swiperefreshlayout:1.1.0`이 이미 추가되어 있음을 확인해 신규 의존성 추가 없이 구현하도록 계획했다.
|
||||
- 2026-07-22: `HomeMainFragment`, `ContentMainFragment`, `ChatMainFragment`와 각 ViewModel을 확인해 현재 선택 탭/필터 기준 첫 페이지 재호출 경로를 확정했다.
|
||||
- 2026-07-22: 이번 단계는 plan-task 문서 작성만 수행했으며 구현/빌드/테스트는 실행하지 않는다.
|
||||
- 2026-07-22: `prd.md`의 Open Questions 아래에 구현 계획 확정 내용을 반영했고, `rg "구현 계획 확정|HomeCreatorRankingViewModel|force = true" docs/20260722_메인_홈_콘텐츠_대화_탭_당겨서_새로고침/prd.md`로 추가 내용을 확인했다.
|
||||
- 2026-07-22: 통합 검증으로 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`를 실행했고 결과는 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: `./gradlew :app:ktlintCheck` 최초 재실행에서 새 테스트 파일의 line length/argument wrapping 위반이 발견되어 줄바꿈만 수정했다.
|
||||
- 2026-07-22: `adb devices`를 실행했으나 `List of devices attached` 아래 연결 기기가 없어 `./gradlew :app:installDebug`와 실제 기기 수동 QA는 환경 차단으로 수행하지 못했다.
|
||||
- 2026-07-22: 최종 리뷰에서 pull-to-refresh 실패 시 기존 데이터 보존과 성공 후 최상단 복귀 누락이 지적되어, `HomeMainFragmentSourceTest`, `ContentMainFragmentSourceTest`, `ChatMainFragmentLayoutTest`에 RED 검증을 먼저 추가했다. RED는 각각 `HomeMainFragmentSourceTest.kt:94`, `ContentMainFragmentSourceTest.kt:111`, `ChatMainFragmentLayoutTest.kt:194`의 `AssertionError`로 확인했다.
|
||||
- 2026-07-22: 리뷰 지적 반영 후 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`, `./gradlew :app:ktlintCheck`를 재실행했고 모두 BUILD SUCCESSFUL이다. `git diff --check`도 출력 없이 통과했다. ktlint 실행 중 `.editorconfig`의 `disabled_rules` deprecation warning은 출력되었다.
|
||||
- 2026-07-22: 두 번째 리뷰에서 Chat empty 상태 실패 보존과 Chat/Content 전체 탭 pagination 상태 보존이 지적되어 `ChatMainFragmentLayoutTest`, `ChatMainViewModelTest`, `ContentAllTabViewModelTest`에 RED 검증을 먼저 추가했다. RED는 각각 `ChatMainFragmentLayoutTest.kt:163`의 `AssertionError`, `ChatMainViewModelTest.kt:157`의 `ClassCastException`, `ContentAllTabViewModelTest.kt:534`의 `ClassCastException`으로 확인했다. 수정 후 동일한 최종 Gradle 검증을 다시 실행했고 모두 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: 리뷰 blocker 수정 RED 테스트로 `HomeMainFragmentSourceTest`, `ContentMainFragmentSourceTest`, `ChatMainFragmentLayoutTest`에 pull-refresh 실패 시 기존 표시 데이터 보존과 성공 시 현재 표면 top-scroll source 검증을 먼저 추가했다. 첫 실행은 `HomeMainFragmentSourceTest.kt`의 `assertEquals` import 누락으로 컴파일 실패해 테스트 결함을 수정했고, 재실행에서 `HomeMainFragmentSourceTest.kt:94`, `ContentMainFragmentSourceTest.kt:111`, `ChatMainFragmentLayoutTest.kt:194`의 `AssertionError`로 기대 RED를 확인했다.
|
||||
- 2026-07-22: `HomeMainFragment`, `ContentMainFragment`, `ChatMainFragment` 내부에만 pull-refresh in-progress flag를 추가했다. Home/Content는 pull-refresh error에서 기존 content/list clearing을 건너뛰고, Content 전체 탭은 refresh loading/error 동안 기존 전체 탭 content 상태와 adapter items를 유지하도록 했다. Home/Content 성공 또는 empty terminal state는 현재 탭의 `NestedScrollView`/`RecyclerView`를 top으로 이동시키고 flag를 해제하며, Chat은 refresh error에서 기존 adapter items를 유지한다.
|
||||
- 2026-07-22: 구현 후 RED 대상 묶음 `./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.chat.ChatMainFragmentLayoutTest"`를 실행해 BUILD SUCCESSFUL을 확인했다. 이후 `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`도 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"` 첫 확장 실행은 기존 Home source test 3개가 과거 한 줄 branch 문자열을 고정해 `HomeFollowingFragmentSourceTest.kt:84`, `HomeFollowingFragmentSourceTest.kt:139`, `HomeMainFragmentLayoutTest.kt:1230`에서 실패했다. 테스트 기대값을 새 guard/block 구조로 갱신한 뒤 같은 명령을 재실행해 BUILD SUCCESSFUL을 확인했다.
|
||||
- 2026-07-22: 2차 리뷰 blocker 수정 RED 테스트로 `ChatMainFragmentLayoutTest`에 empty 상태 pull-refresh Loading placeholder 보존 source 검증, `ChatMainViewModelTest`에 같은 filter 첫 페이지 refresh 실패 후 이전 cursor 기반 `loadNextPage()` append 검증, `ContentAllTabViewModelTest`에 첫 페이지 refresh 실패 후 이전 page 기반 `loadMore()` append 검증을 먼저 추가했다. production 변경 전 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest"`를 실행했고 `ChatMainFragmentLayoutTest.kt:163` AssertionError, `ChatMainViewModelTest.kt:157` ClassCastException, `ContentAllTabViewModelTest.kt:534` ClassCastException으로 RED를 확인했다.
|
||||
- 2026-07-22: `ChatMainFragment`는 pull-refresh Loading 중 empty message를 숨기지 않도록 가드했고, `ChatMainViewModel`은 같은 filter 첫 페이지 실패 시 이전 `currentItems`/`nextCursor`/`hasMore`와 Content/Empty 상태를 복원하도록 했다. `ContentAllTabViewModel`은 같은 type/sort/dayOfWeek의 기존 Content가 있는 첫 페이지 refresh 실패에서 이전 Content를 다시 emit해 pagination 상태를 보존하도록 했다.
|
||||
- 2026-07-22: 구현 후 RED 대상 묶음 재실행은 첫 시도에서 `ContentAllTabViewModel.kt`의 `requestContents` 호출부 시그니처 정렬 오류로 `:app:compileDebugKotlin`이 실패했고, 기존 `loadMore()` trailing lambda 호출을 보존하도록 파라미터 순서를 바로잡았다. 이후 같은 RED 대상 묶음 명령을 재실행해 BUILD SUCCESSFUL을 확인했다.
|
||||
- 2026-07-22: 2차 리뷰 blocker 수정 최종 검증으로 `./gradlew :app:compileDebugKotlin`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`를 실행했고 모두 BUILD SUCCESSFUL이다. Gradle deprecation warning은 기존 빌드 경고로 확인되었고 이번 변경 파일 관련 실패는 없었다.
|
||||
- 2026-07-22: Phase 6 리뷰 Important 보완 후 targeted unit test 묶음과 `./gradlew :app:ktlintCheck`를 실행했고 모두 BUILD SUCCESSFUL이다. ktlint의 `.editorconfig disabled_rules` deprecation warning과 Gradle deprecation warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: Phase 6 read-only 리뷰에서 나온 Important 2건을 추가 보완했다. stale 첫 페이지 응답도 in-flight key를 해제하도록 하고, Content/Home error 보존 및 Content 전체 탭 명시 실패 결과가 대상 `SwipeRefreshLayout` indicator를 종료하도록 수정했다. 관련 targeted test는 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: 추가 리뷰에서 `SwipeRefreshLayout` direct child가 `FrameLayout`인 Chat/Content 전체 탭의 child scroll 판정 누락, Chat/Content 전체 탭 refresh 실패 복원 시 top-scroll 발생, Home/Content 일부 ViewModel의 실패 후 terminal state가 `Error`로 남는 문제가 지적되었다. `ChatMainFragmentLayoutTest`와 `ContentMainFragmentSourceTest`에 child scroll callback 및 실패 복원 시 top-scroll 방지 source 검증을 추가했고, production 변경 전 각각 `ChatMainFragmentLayoutTest.kt:155`, `ChatMainFragmentLayoutTest.kt:189`, `ChatMainFragmentLayoutTest.kt:197`, `ContentMainFragmentSourceTest.kt:89`, `ContentMainFragmentSourceTest.kt:129`의 AssertionError로 RED를 확인했다.
|
||||
- 2026-07-22: ViewModel terminal state 보존 RED 검증으로 `ContentRankingViewModelTest`와 `HomeFollowingViewModelTest`에 refresh 실패 시 기존 Content 유지 테스트를 추가했다. production 변경 전 `ContentRankingViewModelTest.kt:170`, `HomeFollowingViewModelTest.kt:124`의 ClassCastException으로 RED를 확인했다.
|
||||
- 2026-07-22: 보완 구현으로 Chat/Content 전체 탭 `SwipeRefreshLayout`에 `setOnChildScrollUpCallback`을 추가했고, Chat은 같은 Content state identity 재방출을 실패 복원으로 판정해 top-scroll을 막았다. Content 전체 탭은 refresh 실패 복원 시 `paginationErrorMessage`를 consume하기 전에 refresh flag를 해제해 top-scroll을 막았다. Home/Content 관련 ViewModel은 이전 terminal state가 있으면 실패 시 그 상태를 되돌리고 toast만 발행하도록 최소 변경했다.
|
||||
- 2026-07-22: 보완 후 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"`를 순차 실행했고 모두 BUILD SUCCESSFUL이다. 병렬 Gradle 실행 중 한 차례 `app/build/tmp/kotlin-classes/debug` 삭제 충돌이 발생해 이후 검증은 순차 실행으로 전환했다.
|
||||
- 2026-07-22: 최종 확장 검증으로 `./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.chat.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. `ktlintCheck`는 새 테스트 긴 줄 위반을 한 차례 발견해 줄바꿈만 수정한 뒤 재실행 통과했고, `.editorconfig disabled_rules` deprecation warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: 리뷰 세션 재검토 결과 이전 fallback `Content` 오분류 blocker는 해결됐고 Critical/Important 이슈는 남아 있지 않다고 확인받았다. 비차단 잔여 위험으로 `ContentAllTabViewModelTest`가 복원 객체의 `assertSame`까지 고정하지는 않는다는 의견이 있었으나, 현재 source test와 ViewModel 테스트 조합은 저장소 테스트 관례 기준 충분하다고 판단되었다.
|
||||
- 2026-07-22: 추가 리뷰 Important 1~4 대응으로 `PullRefreshRequestRaceSourceTest`와 `ContentAllTabViewModelTest` 회귀 테스트를 먼저 추가했다. production 변경 전 `PullRefreshRequestRaceSourceTest.kt:19`, `PullRefreshRequestRaceSourceTest.kt:32`, `ContentAllTabViewModelTest.kt:595`에서 RED를 확인했다.
|
||||
- 2026-07-22: Home 추천/랭킹/팔로잉 및 Content 추천 ViewModel에 로딩 중 동일 첫 페이지 요청 무시와 request generation guard를 추가했다. `ContentAllTabViewModel`은 refresh 실패 복원 snapshot의 `isLoadingMore=false`, `paginationErrorMessage=null` 정규화를 적용했다. Home/Content/Chat Fragment는 refresh 시작 탭/필터를 저장하고 동일 소유자 terminal 응답에만 indicator 종료/top-scroll/실패 보존을 적용하도록 수정했다. Minor의 indicator 종료 지연은 전역 LoadingDialog 차단 대신 대상 요청 소유권 기반 종료로 완화했다.
|
||||
- 2026-07-22: 후속 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.PullRefreshRequestRaceSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest"`, `./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.chat.ChatMainFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. ktlint의 `.editorconfig disabled_rules` deprecation warning과 일부 테스트 deprecated API warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: 재리뷰에서 남은 Content 전체 탭 refresh 실패 복원 top-scroll, Chat filter 전환 후 stale refresh ownership 문제를 추가 보완했다. RED는 `ContentMainFragmentSourceTest.kt:152`, `ChatMainFragmentLayoutTest.kt:205`에서 확인했다. Content 전체 탭은 복원 snapshot copy를 참조 동일성만이 아니라 동일 page/content 비교로도 실패 복원 판정하고, Chat은 filter 변경 시 기존 refresh ownership과 indicator를 취소한다.
|
||||
- 2026-07-22: 최종 재검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.PullRefreshRequestRaceSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. 모두 BUILD SUCCESSFUL 또는 출력 없음으로 통과했다.
|
||||
- 2026-07-22: 최종 재리뷰에서 Critical/Important 이슈가 남아 있지 않다고 확인받았다. Minor로 Content 전체 탭에서 성공 응답 데이터가 기존 page/content와 완전히 동일하면 top-scroll이 생략될 수 있다는 UX 미세 위험이 남았으나 blocker는 아니라고 판단되었다.
|
||||
- 2026-07-22: 후속 리뷰 Important 대응으로 Content 랭킹/전체, Chat의 A→B→A 지연 응답 race 테스트와 Home 추천 refresh/follow 완료 순서 테스트를 먼저 추가했다. production 변경 전 targeted test 실행에서 `ContentRankingViewModelTest.kt:211`, `ContentAllTabViewModelTest.kt:793`, `ChatMainViewModelTest.kt:215`, `HomeRecommendationViewModelTest.kt:102`, `HomeMainFragmentLoginGuardSourceTest.kt:145` 실패로 RED를 확인했다.
|
||||
- 2026-07-22: `Set<key>` 방식 대신 최신 first-page 요청만 나타내는 `activeFirstPageKey`로 세 ViewModel을 보완했고, Home 추천 follow 성공은 refresh 중에도 flag로 보존해 최종 Content에 반영하도록 수정했다. `HomeMainFragmentLoginGuardSourceTest`와 `PullRefreshRequestRaceSourceTest`는 새 source 계약에 맞춰 갱신했다.
|
||||
- 2026-07-22: 후속 수정 검증으로 targeted 묶음 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.PullRefreshRequestRaceSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"`와 전체 `./gradlew :app:testDebugUnitTest`를 실행했고 모두 BUILD SUCCESSFUL이다. Reviewer gate에서도 blocker 없음으로 확인받았다.
|
||||
- 2026-07-22: 추가 리뷰 Important 2건 대응으로 `ContentRankingViewModelTest`에 캐시 반환 경로 active key 고착 회귀 테스트를 추가하고, `HomeRecommendationViewModelTest`에 refresh/follow 실패 완료 순서별 최신 Content 보존 테스트를 추가했다. production 변경 전 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModelTest"` 실행에서 `ContentRankingViewModelTest.kt:254` AssertionError, `HomeRecommendationViewModelTest.kt:144` ClassCastException, `HomeRecommendationViewModelTest.kt:132` AssertionError로 RED를 확인했다.
|
||||
- 2026-07-22: `ContentRankingViewModel`은 캐시 반환 시 `latestRequestId`를 증가시키고 `activeFirstPageKey`를 비워 진행 중 stale 응답과 active key를 무효화하도록 수정했다. `HomeRecommendationViewModel`은 fallback Content에도 follow 완료 상태를 반영하고, follow 실패는 현재 추천 화면을 `Error`로 덮지 않고 toast만 발행하도록 수정했다.
|
||||
- 2026-07-22: 후속 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModelTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.PullRefreshRequestRaceSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentRankingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeRecommendationViewModelTest"`, 전체 `./gradlew :app:testDebugUnitTest`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. Reviewer gate도 blocker 없음으로 확인받았다.
|
||||
- 2026-07-22: Minor 2건 대응으로 `ContentAllTabViewModelTest`와 `ChatMainViewModelTest`의 A→B→A 테스트에 후속 pagination 요청 조건 검증을 추가했다. 이후 `lastRenderedContentAllState`, `stopHomeRefreshIndicators()`, `stopContentRefreshIndicators()`, 호출자 없는 `onStaleResponse` 파라미터를 제거하고 관련 source test 기대값을 갱신했다.
|
||||
- 2026-07-22: Minor 반영 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentAllTabViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`, 전체 `./gradlew :app:testDebugUnitTest`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. 기존 `.editorconfig disabled_rules` deprecation warning과 테스트 deprecated API warning은 남아 있다.
|
||||
- 2026-07-22: 추가 비차단 Minor 대응으로 `ContentMainFragmentSourceTest`에서 `lastRenderedContentAllState` 부재를 고정하던 과거 구현 문자열 assertion 3개를 제거했다. 또한 `code-style.md`의 신규 UI 레이아웃/표현 속성 테스트 금지 규칙에 맞춰 Home/Content/Chat의 신규 refresh XML class/ID/hierarchy assertion 테스트를 제거하고, refresh dispatch와 상태 전이 source 검증은 유지했다.
|
||||
- 2026-07-22: 추가 Minor 정리 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.chat.ChatMainFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentSourceTest"`, 전체 `./gradlew :app:testDebugUnitTest`, `./gradlew :app:ktlintCheck`, `git diff --check`를 실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. 제거 대상 문자열 검색 `rg -n "lastRenderedContentAllState|content layout has refresh containers|chat layout wraps room list|home layout has refresh containers" app/src/test/java/kr/co/vividnext/sodalive/v2/main`도 출력이 없었다.
|
||||
- 2026-07-22: 추가 재검토에서 Chat/Home 테스트에 남아 있던 신규 refresh hierarchy assertion과 unused helper를 더 제거했다. 제거 대상 문자열 검색 `rg -n "swipe_home_recommendation|swipe_home_ranking|swipe_home_following|SwipeRefreshLayout|swipe_chat_rooms|findViewByName|content layout has refresh containers|home layout has refresh containers|chat layout wraps room list|lastRenderedContentAllState" app/src/test/java/kr/co/vividnext/sodalive/v2/main`은 출력이 없었다. 관련 targeted test 묶음은 BUILD SUCCESSFUL이다. 이후 전체 `./gradlew :app:testDebugUnitTest`는 `CreatorChannelHomeViewModelTest.kt:285`의 공유 상태성 실패가 1회 발생했으나 같은 명령 단독 재실행은 BUILD SUCCESSFUL이다. `./gradlew :app:ktlintCheck`는 BUILD SUCCESSFUL, `git diff --check`는 출력 없이 통과했다. 기존 `.editorconfig disabled_rules` deprecation warning과 테스트 deprecated API warning은 남아 있다.
|
||||
|
||||
@@ -169,6 +169,12 @@
|
||||
- 콘텐츠 `랭킹` 탭에서 내부 ranking type이 여러 개일 경우 현재 선택 type 유지 방식은 구현 계획에서 실제 ViewModel 상태를 기준으로 확정한다.
|
||||
- refresh indicator 색상은 별도 디자인 요구가 없으므로 기존 theme 기본값을 우선 사용하고, 필요 시 구현 계획에서 기존 앱 색상과 맞춘다.
|
||||
|
||||
### 2026-07-22 구현 계획 확정
|
||||
- 홈 `랭킹` 탭은 `HomeCreatorRankingViewModel.loadCreatorRankings()`와 `HomeCreatorRankingRepository.getCreatorRankings()` 흐름을 새로고침 대상으로 사용한다.
|
||||
- 홈 `팔로잉` 탭은 기존 `ensureV2Access(AccessRequirement.Login)` 진입 가드를 유지한다. 로그인되지 않은 상태에서는 팔로잉 탭으로 전환되지 않으므로 새로고침도 노출하지 않는다.
|
||||
- 콘텐츠 `랭킹` 탭은 `ContentRankingViewModel.selectedTypeLiveData`의 현재 `AudioRankingType`을 `force = true`로 다시 호출한다.
|
||||
- refresh indicator 색상은 별도 디자인 요구가 없으므로 `SwipeRefreshLayout` 기본 theme 색상을 사용한다.
|
||||
|
||||
---
|
||||
|
||||
## 12. References
|
||||
@@ -188,3 +194,16 @@
|
||||
- 2026-07-22: `ContentAllTabViewModel`, `MainContentAllTabApi`, `ContentOverviewViewModel`, 관련 UI state 구조를 codegraph로 확인해 첫 페이지 재조회와 pagination 분리 요구사항을 문서화했다.
|
||||
- 2026-07-22: 사용자 확인에 따라 적용 범위를 메인 홈 `추천/랭킹/팔로잉`, 콘텐츠 `추천/랭킹/전체`, 대화 `전체/AI 채팅/DM` 현재 선택 상태별 새로고침으로 확정했다.
|
||||
- 2026-07-22: 이번 단계는 PRD 작성만 수행했으며 구현/빌드/테스트는 실행하지 않는다.
|
||||
- 2026-07-22: 당겨서 새로고침 구현 후 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, 홈/콘텐츠/대화 관련 targeted unit test를 실행했고 결과는 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: `adb devices` 결과 연결 기기가 없어 수동 QA는 환경 차단으로 기록했다.
|
||||
- 2026-07-22: 최종 리뷰에서 지적된 pull-to-refresh 실패 시 기존 데이터 보존과 성공 후 최상단 복귀를 Fragment/ViewModel 내부 최소 변경으로 반영한 뒤 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, 홈/콘텐츠/대화 관련 targeted unit test, `./gradlew :app:ktlintCheck`를 재실행했고 결과는 BUILD SUCCESSFUL이다. ktlint 실행 중 `.editorconfig`의 `disabled_rules` deprecation warning은 출력되었다.
|
||||
- 2026-07-22: 추가 리뷰에서 지적된 `SwipeRefreshLayout` child scroll 판정, 실패 복원 시 불필요한 top-scroll, Home/Content ViewModel terminal state 보존을 보완했다. RED는 `ChatMainFragmentLayoutTest`, `ContentMainFragmentSourceTest`, `ContentRankingViewModelTest`, `HomeFollowingViewModelTest`에서 먼저 확인했고, 수정 후 `./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.chat.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`를 재실행했다. 모두 BUILD SUCCESSFUL 또는 출력 없음으로 통과했으며, ktlint의 `.editorconfig disabled_rules` deprecation warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: Home/Content refresh 실패 복원 state identity 판정과 Content 전체 탭 refresh 실패 시 이전 Content state 재방출을 보완한 뒤 `./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.chat.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`를 재실행했다. Gradle 명령은 모두 BUILD SUCCESSFUL이며 `git diff --check`는 출력 없이 통과했다. `ktlintCheck`는 새 테스트 긴 줄 위반을 한 차례 발견해 줄바꿈만 수정한 뒤 재실행 통과했고, `.editorconfig disabled_rules` deprecation warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: 리뷰 세션 재검토 결과 이전 fallback `Content` 오분류 blocker는 해결됐고 Critical/Important 이슈는 남아 있지 않다고 확인받았다. 비차단 잔여 위험으로 `ContentAllTabViewModelTest`가 복원 객체의 `assertSame`까지 고정하지는 않는다는 의견이 있었으나, 현재 source test와 ViewModel 테스트 조합은 저장소 테스트 관례 기준 충분하다고 판단되었다.
|
||||
- 2026-07-22: 추가 리뷰에서 첫 페이지 중복 요청/오래된 응답, pagination 중 refresh 실패 복원, Chat filter 전환 실패, Home/Content 탭 전환 중 refresh 종료 소유권 문제가 지적되었다. 전역 LoadingDialog 차단은 보조 UX일 뿐 correctness를 보장하지 못하므로, ViewModel request generation/중복 무시와 Fragment refresh 시작 탭/필터 저장으로 최소 보완하기로 확정했다.
|
||||
- 2026-07-22: 추가 리뷰 Important 1~4를 반영해 첫 페이지 ViewModel 중복 요청 무시/request generation guard, Content 전체 탭 refresh 실패 복원 snapshot 정규화, Home/Content/Chat refresh 시작 탭/필터 소유권 기반 완료 처리를 적용했다. Minor의 indicator 종료 지연은 전역 UI 차단 대신 대상 요청 소유권 기반 종료로 완화했다. 관련 targeted unit test, `compileDebugKotlin`, `mergeDebugResources`, `ktlintCheck`, `git diff --check`는 모두 통과했다.
|
||||
- 2026-07-22: 재리뷰에서 남은 Content 전체 탭 refresh 실패 복원 top-scroll과 Chat filter 전환 후 stale refresh ownership을 추가 보완했다. Content 전체 탭은 복원 snapshot copy를 동일 page/content 비교로 실패 복원 판정하고, Chat은 filter 변경 시 기존 refresh ownership과 indicator를 취소한다. 관련 targeted unit test, `compileDebugKotlin`, `mergeDebugResources`, `ktlintCheck`, `git diff --check`는 모두 통과했다.
|
||||
- 2026-07-22: 최종 재리뷰에서 Critical/Important 이슈가 남아 있지 않다고 확인받았다. Minor로 Content 전체 탭에서 성공 응답 데이터가 기존 page/content와 완전히 동일하면 top-scroll이 생략될 수 있다는 UX 미세 위험이 남았으나 blocker는 아니라고 판단되었다.
|
||||
- 2026-07-22: 추가 Important 리뷰 지적을 반영해 Content 랭킹/전체/Chat의 첫 페이지 in-flight key 중복 방어, Home/Content 대상 wrapper indicator 종료, Home 추천/follow loading 분리, Content 전체 탭 명시 refresh 결과 신호를 보완했다. RED는 신규/수정 테스트의 미구현 컴파일 오류로 먼저 확인했고, 구현 후 targeted unit test 묶음은 BUILD SUCCESSFUL이다.
|
||||
- 2026-07-22: Phase 6 보완 후 `./gradlew :app:ktlintCheck`와 targeted unit test 묶음을 재실행했고 모두 BUILD SUCCESSFUL이다. ktlint의 `.editorconfig disabled_rules` deprecation warning은 기존 경고로 남아 있다.
|
||||
- 2026-07-22: read-only 리뷰에서 확인된 Content 전체 탭 stale key 잔류와 실패 indicator 미종료 위험을 추가 보완했다. stale 응답 후 같은 key 재요청 가능성과 대상 wrapper indicator 종료를 테스트로 고정했고 관련 targeted unit test는 BUILD SUCCESSFUL이다.
|
||||
|
||||
Reference in New Issue
Block a user