1480 lines
152 KiB
Markdown
1480 lines
152 KiB
Markdown
# 메인 홈 팔로잉 탭 구현 계획/TASK
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: 구현 시 `superpowers:subagent-driven-development` 또는 `superpowers:executing-plans`를 사용해 task 단위로 진행한다. 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
|
||
|
||
**Goal:** `GET /api/v2/home/following` 응답을 기반으로 메인 홈 `팔로잉` 탭에 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식을 표시한다.
|
||
|
||
**Architecture:** 기존 `HomeMainFragment`의 title bar, `TextTabBarView`, 추천/랭킹 탭 구조는 유지하고, `팔로잉` 선택 시 전용 content surface를 노출한다. 신규 API/Repository/DTO/UI state/mapper/ViewModel/adapter는 `kr.co.vividnext.sodalive.v2.main.home` 하위에 두며, `ChatRoomListItemResponse`, `CreatorActivityType`, `formatUtcRelativeTimeText`, 기존 feed/live/profile widget을 우선 재사용한다. 로그인 유도 화면은 아직 디자인/문구가 정해지지 않았으므로 이번 구현 계획에서는 `isLoginRequired` 상태 분기와 팔로잉 섹션 숨김까지만 고정한다.
|
||
|
||
**Tech Stack:** Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
|
||
|
||
---
|
||
|
||
## 전제와 성공 기준
|
||
- PRD: `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- Figma: `home_003` 팔로잉 탭 `24:5682`
|
||
- API endpoint는 `GET /api/v2/home/following`이다.
|
||
- `Authorization` header는 optional이며, token이 blank이면 header를 보내지 않는다.
|
||
- query parameter는 보내지 않는다.
|
||
- `isLoginRequired = true`이면 팔로잉 섹션을 표시하지 않는다.
|
||
- 로그인 유도 화면의 실제 UI, 문구, CTA, 로그인 완료 후 복귀 정책은 별도 확정 후 구현한다.
|
||
- `recentNews` 시간은 `visibleFromAtUtc`를 디바이스 타임존 기준으로 상대 시간 표시한다.
|
||
- `PHOTO_CONTENT` label은 우선 `화보`로 표시한다.
|
||
- `recentNews`는 `type`에 대응하는 nested payload가 null이면 해당 news item을 표시하지 않는다.
|
||
- `monthlySchedules`는 서버가 이번 달 범위로 정렬해서 내려주며 앱은 재정렬하지 않는다.
|
||
- 섹션 title chevron은 터치 콜백까지만 연결하고 실제 이동 목적지는 만들지 않는다.
|
||
- 구현 완료 후 최소 다음 명령을 실행한다.
|
||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- `./gradlew :app:mergeDebugResources`
|
||
- `./gradlew :app:compileDebugKotlin`
|
||
- `./gradlew :app:ktlintCheck`
|
||
- `git diff --check`
|
||
|
||
---
|
||
|
||
## Figma 참조 필요 Phase
|
||
- Phase 1: 제한 참조
|
||
- 기존 홈 탭 구조, v2 위젯, DI/API 패턴 확인 중심으로 진행한다.
|
||
- Phase 2: Figma 참조 불필요
|
||
- API/DTO/Repository와 mapper 상태는 PRD 서버 계약과 기존 v2 data layer 패턴을 따른다.
|
||
- Phase 3: Figma 참조 불필요
|
||
- ViewModel 상태, optional auth header, `isLoginRequired` 분기는 단위 테스트 중심으로 검증한다.
|
||
- Phase 4: 필수 참조
|
||
- 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식 섹션 배치와 spacing은 Figma `24:5682`를 기준으로 확인한다.
|
||
- Phase 5: 필수 참조
|
||
- 최종 수동 화면 검증은 PRD의 포함/제외 항목과 실제 화면을 대조한다.
|
||
- Phase 9: Figma 참조 불필요
|
||
- 최근 소식 Response 계약 변경은 DTO/mapper/test fixture 중심으로 진행하고, 기존 Feed 위젯 표시 정책과 레이아웃은 유지한다.
|
||
|
||
---
|
||
|
||
## 파일 구조
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt`
|
||
- `GET /api/v2/home/following` Retrofit endpoint를 정의한다.
|
||
- Create/Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt`
|
||
- `HomeFollowingTabResponse`, `FollowingCreatorResponse`, `FollowingLiveResponse`, `FollowingScheduleResponse`, `FollowingNewsResponse`, `FollowingCreatorRankingNewsResponse`, `FollowingContentNewsResponse`, `FollowingContentRankingNewsResponse`, `FollowingCommunityPostNewsResponse`, `FollowingNewsType` DTO를 정의한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingRepository.kt`
|
||
- API 호출을 repository method로 감싼다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiState.kt`
|
||
- `Loading`, `LoginRequired`, `Content`, `Empty`, `Error` 상태를 정의한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt`
|
||
- 팔로잉 크리에이터, live, chat, schedule, news section/item UI model을 정의한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt`
|
||
- DTO를 UI model/state로 변환하고, matching nested payload null news 숨김, `PHOTO_CONTENT` label, `visibleFromAtUtc` 시간 기준을 적용한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingAuthHeader.kt`
|
||
- blank token이면 `null`, 값이 있으면 `Bearer {token}`을 반환한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt`
|
||
- 팔로잉 탭 API 호출, loading/error/login-required/content 상태를 관리한다.
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- `HomeFollowingApi`, `HomeFollowingRepository`, `HomeFollowingViewModel`을 Koin에 등록한다.
|
||
- Modify: `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- 팔로잉 탭 content surface와 섹션 RecyclerView들을 추가한다.
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- `HOME_TAB_FOLLOWING` 분기, ViewModel observer, adapter binding, section visibility, chevron click callback을 연결한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingCreatorAdapter.kt`
|
||
- `followingCreators` horizontal profile list를 바인딩한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`
|
||
- `onAirLives` horizontal live card list를 바인딩한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt`
|
||
- `recentChats` horizontal chat card list를 바인딩한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.kt`
|
||
- `monthlySchedules` vertical schedule list를 바인딩한다.
|
||
- Create: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- `recentNews` vertical news list를 바인딩한다.
|
||
- Create: `app/src/main/res/layout/item_home_following_creator.xml`
|
||
- 팔로잉 크리에이터 profile item이다.
|
||
- Create: `app/src/main/res/layout/item_home_following_live.xml`
|
||
- On Air live item이다.
|
||
- Create: `app/src/main/res/layout/item_home_following_chat.xml`
|
||
- 최근 대화 compact card item이다.
|
||
- Create: `app/src/main/res/layout/item_home_following_schedule.xml`
|
||
- 이달의 스케줄 item이다.
|
||
- Reuse: `app/src/main/res/layout/view_feed_rank.xml`, `app/src/main/res/layout/view_feed_content.xml`, `app/src/main/res/layout/view_feed_community.xml`
|
||
- 최근 소식은 기존 Feed 위젯 layout을 재사용한다.
|
||
- Delete: `app/src/main/res/layout/item_home_following_news_rank.xml`, `app/src/main/res/layout/item_home_following_news_content.xml`
|
||
- Task 6.5에서 Feed 위젯 전환 후 obsolete 팔로잉 전용 최근 소식 layout을 제거했다.
|
||
- Modify: `app/src/main/res/values/strings.xml`
|
||
- 팔로잉 섹션 title, `On Air`, `화보`, empty/error label을 추가한다.
|
||
- Modify: `app/src/main/res/values-en/strings.xml`
|
||
- 팔로잉 섹션 title, `On Air`, photo label, empty/error label을 추가한다.
|
||
- Modify: `app/src/main/res/values-ja/strings.xml`
|
||
- 팔로잉 섹션 title, `On Air`, photo label, empty/error label을 추가한다.
|
||
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingAuthHeaderTest.kt`
|
||
- optional auth header 생성 규칙을 검증한다.
|
||
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt`
|
||
- DTO to UI mapping, login-required, rank null filtering, news label/time 기준을 검증한다.
|
||
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt`
|
||
- API success/error/login-required/loading 상태 전환을 검증한다.
|
||
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- layout id, adapter/ViewModel 연결, `HOME_TAB_FOLLOWING` 분기, chevron click callback 연결을 source-level로 검증한다.
|
||
|
||
---
|
||
|
||
### Phase 1: 기존 구조 확인과 작업 경계 고정
|
||
|
||
- [x] **Task 1.1: 홈 탭 삽입 지점 확인**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingViewModel.kt`
|
||
- 작업:
|
||
- 기존 `추천`, `랭킹`, `팔로잉` Text Tab bar는 유지한다.
|
||
- `showHomeTab(HOME_TAB_FOLLOWING)` 분기에서 팔로잉 surface를 표시할 위치를 확인한다.
|
||
- 추천/랭킹 API와 ViewModel은 리팩터링하지 않는다.
|
||
- 검증:
|
||
- Run: `rg -n "HOME_TAB_FOLLOWING|showHomeTab|nsvHomeRecommendationContent|rvHomeCreatorRankings|textTabBarHome" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- Expected: 팔로잉 탭 분기와 기존 추천/랭킹 surface visibility 제어 지점이 확인된다.
|
||
- Result: PASS. `HomeMainFragment.kt`에서 `showHomeTab`, `HOME_TAB_FOLLOWING`, 추천/랭킹 visibility 제어 지점을 확인했다.
|
||
|
||
- [x] **Task 1.2: 재사용 위젯과 신규 adapter 경계 확정**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeCreatorProfileImageLoader.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/livethumbnail/LiveThumbnailDetailView.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankView.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedContentView.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/model/ChatRoomMappers.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/common/CreatorActivityType.kt`
|
||
- 작업:
|
||
- 프로필 이미지 로딩은 `HomeCreatorProfileImageLoader` 또는 같은 placeholder 정책 재사용으로 고정한다.
|
||
- 채팅 데이터 변환은 `ChatRoomListItemResponse.toUiItem()` 재사용으로 고정한다.
|
||
- 스케줄 type label은 `CreatorActivityType.labelResId` 재사용으로 고정한다.
|
||
- Figma와 크기가 맞지 않는 카드들은 팔로잉 전용 adapter/layout 신규 생성으로 고정한다.
|
||
- 검증:
|
||
- Run: `rg -n "HomeCreatorProfileImageLoader|class LiveThumbnailDetailView|sealed class FeedItem|fun ChatRoomListItemResponse.toUiItem|enum class CreatorActivityType" app/src/main/java/kr/co/vividnext/sodalive/v2`
|
||
- Expected: 재사용 후보 클래스와 함수가 확인된다.
|
||
- Result: PASS. `LiveThumbnailDetailView`, `FeedItem`, `ChatRoomListItemResponse.toUiItem()`, `CreatorActivityType` 재사용 후보를 확인했다.
|
||
|
||
- [x] **Task 1.3: 제외 범위 확인**
|
||
- 확인:
|
||
- `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- 제외:
|
||
- 로그인 유도 화면 실제 디자인/문구/CTA 구현
|
||
- 더보기 chevron 목적지 이동
|
||
- 팔로잉/언팔로잉 액션
|
||
- 스케줄 월 필터와 앱 내 재정렬
|
||
- 레거시 홈 화면 직접 수정
|
||
- 검증:
|
||
- Run: `rg -n "Non-Goals|로그인 유도|더보기|monthlySchedules|rank|PHOTO_CONTENT|Open Questions" docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- Expected: 제외 범위와 확정 정책이 확인된다.
|
||
- Result: PASS. 로그인 유도 UI 미확정, 더보기 목적지 제외, `monthlySchedules` 정렬 정책, `rank == null` 제외, `PHOTO_CONTENT` label 정책을 확인했다.
|
||
|
||
---
|
||
|
||
### Phase 2: API, DTO, Repository, mapper 추가
|
||
|
||
- [x] **Task 2.1: optional auth header 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingAuthHeaderTest.kt`
|
||
- 테스트 케이스:
|
||
- blank token은 `null`을 반환한다.
|
||
- non-blank token은 `Bearer {token}`을 반환한다.
|
||
- 앞뒤 공백이 있는 token은 trim 후 `Bearer {token}`을 반환한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingAuthHeaderTest"`
|
||
- Expected: helper 구현 전 RED 실패.
|
||
- Result: RED 확인. `homeFollowingAuthHeader` 미구현으로 `compileDebugUnitTestKotlin` unresolved reference 실패가 발생했다.
|
||
|
||
- [x] **Task 2.2: optional auth header helper 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingAuthHeader.kt`
|
||
- 작업:
|
||
- `fun homeFollowingAuthHeader(token: String): String?`를 추가한다.
|
||
- `token.trim().takeIf { it.isNotEmpty() }?.let { "Bearer $it" }` 규칙을 적용한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingAuthHeaderTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. blank/whitespace token은 `null`, non-blank token은 trim 후 `Bearer {token}`으로 검증됐다.
|
||
|
||
- [x] **Task 2.3: API/DTO/Repository 계약 추가**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingRepository.kt`
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- 작업:
|
||
- Retrofit endpoint는 `@GET("/api/v2/home/following")`로 정의한다.
|
||
- `@Header("Authorization") authHeader: String?`를 사용한다.
|
||
- query parameter는 정의하지 않는다.
|
||
- DTO는 PRD의 Android Response Contract 필드를 모두 포함하고 `@Keep`, `@SerializedName`을 사용한다.
|
||
- `recentChats`는 기존 `ChatRoomListItemResponse`를 사용한다.
|
||
- `FollowingScheduleResponse.type`은 기존 `CreatorActivityType`을 사용한다.
|
||
- Koin `networkModule`, `repositoryModule`에 신규 API/Repository를 등록한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: 신규 data layer와 DI 등록이 컴파일된다.
|
||
- Result: PASS. `HomeFollowingApi`, DTO, Repository, API/Repository DI 등록이 `compileDebugKotlin`에서 컴파일됐다.
|
||
|
||
- [x] **Task 2.4: string resource 추가**
|
||
- 수정:
|
||
- `app/src/main/res/values/strings.xml`
|
||
- `app/src/main/res/values-en/strings.xml`
|
||
- `app/src/main/res/values-ja/strings.xml`
|
||
- 작업:
|
||
- 섹션 title 문자열을 추가한다.
|
||
- `screen_home_following_creators_title`
|
||
- `screen_home_following_on_air_title`
|
||
- `screen_home_following_recent_chats_title`
|
||
- `screen_home_following_monthly_schedules_title`
|
||
- `screen_home_following_recent_news_title`
|
||
- news/category 문자열을 추가한다.
|
||
- `screen_home_following_on_air`
|
||
- `screen_home_following_photo_content`
|
||
- empty/error 문자열을 추가한다.
|
||
- `screen_home_following_empty`
|
||
- `screen_home_following_error`
|
||
- 검증:
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: 3개 locale string resource가 중복 없이 merge된다.
|
||
- Result: PASS. `values`, `values-en`, `values-ja` string resource merge가 통과했다.
|
||
|
||
- [x] **Task 2.5: mapper RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt`
|
||
- 테스트 케이스:
|
||
- `isLoginRequired = true` 응답은 `HomeFollowingUiState.LoginRequired`로 매핑된다.
|
||
- `isLoginRequired = false`이고 모든 섹션이 비면 `HomeFollowingUiState.Empty`로 매핑된다.
|
||
- `followingCreators`, `onAirLives`, `recentChats`, `monthlySchedules`, `recentNews`가 section UI model로 매핑된다.
|
||
- `recentChats`는 `ChatRoomListItemResponse.toUiItem()` 결과가 null인 항목을 제외한다.
|
||
- `monthlySchedules`는 서버 응답 순서를 유지한다.
|
||
- `PHOTO_CONTENT`는 `screen_home_following_photo_content` label로 매핑된다.
|
||
- `CREATOR_RANKING`, `CONTENT_RANKING`의 `rank == null` 항목은 제외된다.
|
||
- news 상대 시간은 `visibleFromAtUtc` 값을 formatter에 전달한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"`
|
||
- Expected: UI model/mapper 구현 전 RED 실패.
|
||
- Result: RED 확인. DTO/UI model/mapper/string resource 미구현으로 `compileDebugUnitTestKotlin` unresolved reference 실패가 발생했다.
|
||
|
||
- [x] **Task 2.6: UI state/model과 mapper 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiState.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt`
|
||
- 작업:
|
||
- `HomeFollowingUiState`는 `Loading`, `LoginRequired`, `Content`, `Empty`, `Error`를 정의한다.
|
||
- `Content`에는 `followingCreators`, `onAirLives`, `recentChats`, `monthlySchedules`, `recentNews` section을 둔다.
|
||
- `Content.isEmpty` helper를 추가해 모든 section item이 비었는지 판정한다.
|
||
- mapper는 `UtcRelativeTimeTextFormatter`를 받아 `visibleFromAtUtc` 상대 시간을 생성한다.
|
||
- ranking news의 `rank == null`은 map 단계에서 제외한다.
|
||
- `PHOTO_CONTENT`는 photo label string resource id를 매핑한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. login-required, empty, content mapping, invalid chat 제외, schedule 순서 유지, `PHOTO_CONTENT` label, null rank filtering, `visibleFromAtUtc` formatter 전달이 검증됐다.
|
||
|
||
#### Task 2.7: 미사용 팔로잉 string resource 정리
|
||
|
||
**Goal 실행 `P2-R1`:** 확정 review 항목 `REV-P2-001`에 따라 후속 UI·오류 처리 변경 후 참조되지 않는 팔로잉 문자열 리소스만 제거한다.
|
||
|
||
- **시작 조건:** `reviews/phase2-following-data-mapper-review.md`의 `REV-P2-001` 확정.
|
||
- **완료 증거:** 3개 미사용 key의 참조 0건 재확인 → 3개 locale 선언 제거 → resource merge·Kotlin compile·팔로잉 회귀·diff 검증 PASS.
|
||
- **범위 밖:** 사용 중인 `screen_home_following_on_air`, empty·ranking·section title 문자열 변경, 표시 문구 재기획, UI 구조 변경.
|
||
- **TDD 예외 사유:** 실행 로직이 아닌 미사용 Android resource 선언 삭제이며, 이를 위한 신규 source test는 저장소 테스트 정책에 맞지 않는다.
|
||
- **대체 검증 방법:** `rg`로 참조 0건을 재확인하고 resource merge, Kotlin compile, 기존 팔로잉 회귀 테스트, `git diff --check`를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/res/values/strings.xml`
|
||
- Modify: `app/src/main/res/values-en/strings.xml`
|
||
- Modify: `app/src/main/res/values-ja/strings.xml`
|
||
|
||
- [x] `screen_home_following_creators_title`, `screen_home_following_on_air_title`, `screen_home_following_error`의 production/test 참조가 0건인지 재확인한다.
|
||
- [x] 3개 locale에서 위 3개 key만 제거하고 사용 중인 팔로잉 문자열은 유지한다.
|
||
- [x] `./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin`과 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`를 실행한다.
|
||
- [x] `git diff --check`를 실행하고 검증 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31: `rg -n "screen_home_following_creators_title|screen_home_following_on_air_title|screen_home_following_error" app/src/main/java app/src/main/res/layout app/src/test` 결과 선언 외 참조 0건을 확인했다. 3개 locale `strings.xml`에서 위 3개 key만 제거하고 `screen_home_following_on_air`, empty, ranking, section title 문자열은 유지했다. `./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin` PASS. 이후 팔로잉 전체 회귀 첫 실행에서 `R.string` id 변경 증분 캐시로 `HomeFollowingMapperTest` 2건이 실패해 `./gradlew --no-daemon --rerun-tasks :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"`로 재빌드했고 PASS. 최종 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS.
|
||
|
||
#### Task 2.8: 미사용 최근 소식 label UI model 정리
|
||
|
||
**Goal 실행 `P2-R2`:** 확정 review 항목 `REV-P2-002`에 따라 Feed category로 대체된 후 production에서 사용하지 않는 `HomeFollowingNewsUiItem.Content.labelResId`와 전용 mapper를 제거한다.
|
||
|
||
- **시작 조건:** `reviews/phase2-following-data-mapper-review.md`의 `REV-P2-002` 확정.
|
||
- **완료 증거:** production 참조 0건 재확인 → UI model·mapper·fixture의 미사용 label 경로 제거 → Feed `Photo` category의 locale label 회귀·Kotlin compile·ktlint PASS.
|
||
- **범위 밖:** `FeedContentCategory` 구조, `screen_home_following_photo_content` 문구, 최근 소식 API/DTO·표시·라우팅 변경.
|
||
- **TDD 예외 사유:** 실행 동작 수정이 아닌 미사용 UI model 필드와 mapper 제거이며, 기존 Feed category 회귀로 동작 무변경을 검증한다.
|
||
- **대체 검증 방법:** `rg`로 production/test 참조를 대조하고 mapper·adapter focused test, Kotlin compile, ktlint, `git diff --check`를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt`
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt`
|
||
|
||
- [x] `rg -n "labelResId|toLabelResId"` 결과로 최근 소식 `Content.labelResId`와 `FollowingNewsType.toLabelResId()`가 production 표시에서 사용되지 않는지 재확인한다.
|
||
- [x] `HomeFollowingNewsUiItem.Content.labelResId`, mapper 대입과 전용 `toLabelResId()`만 제거하고 Feed `Audio`/`Photo` category mapping은 유지한다.
|
||
- [x] mapper/adapter test fixture에서 제거된 필드를 정리하고 `PHOTO_CONTENT` 표시가 `FeedContentCategory.Photo`와 locale string을 사용하는 회귀를 유지한다.
|
||
- [x] mapper·news adapter focused test, 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행하고 결과를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31: `rg -n -- "labelResId|toLabelResId" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home app/src/test/java/kr/co/vividnext/sodalive/v2/main/home`로 팔로잉 최근 소식 `Content.labelResId`와 `FollowingNewsType.toLabelResId()`의 production 표시 참조가 없음을 재확인했다. `HomeFollowingNewsUiItem.Content.labelResId`, mapper 대입, 전용 `toLabelResId()`를 제거했고, `HomeFollowingNewsAdapterTest`에 `PHOTO_CONTENT`가 실제 `FeedContentCategory.Photo` locale label로 표시되는 회귀를 추가했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, 팔로잉 전체 회귀, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
---
|
||
|
||
### Phase 3: ViewModel 상태와 API 호출 연결
|
||
|
||
- [x] **Task 3.1: ViewModel RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt`
|
||
- 테스트 케이스:
|
||
- `loadFollowing()`은 loading 후 content 상태를 발행한다.
|
||
- blank token이면 repository에 null auth header를 전달한다.
|
||
- token이 있으면 repository에 `Bearer {token}` auth header를 전달한다.
|
||
- `isLoginRequired = true` 응답은 login-required 상태를 발행한다.
|
||
- API success이지만 data가 null이면 error 상태와 toast를 발행한다.
|
||
- API failure throwable이면 error 상태와 toast를 발행한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"`
|
||
- Expected: ViewModel 구현 전 RED 실패.
|
||
- Result: RED 확인. `HomeFollowingViewModel` 미구현으로 `compileDebugUnitTestKotlin` unresolved reference 실패가 발생했다.
|
||
|
||
- [x] **Task 3.2: HomeFollowingViewModel 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt`
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- 작업:
|
||
- `HomeFollowingViewModel(repository, relativeTimeTextFormatter)`를 추가한다.
|
||
- `SharedPreferenceManager.token`을 `homeFollowingAuthHeader()`로 변환해 repository에 전달한다.
|
||
- RxJava3 `subscribeOn(Schedulers.io())`, `observeOn(AndroidSchedulers.mainThread())` 패턴을 따른다.
|
||
- success data는 mapper로 UI state 변환한다.
|
||
- error는 `HomeFollowingUiState.Error`와 기존 unknown error toast 패턴을 따른다.
|
||
- Koin `viewModelModule`에 `HomeFollowingViewModel(get(), get())`를 등록한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. loading/content, optional auth header, login-required, null data error/toast, throwable error/toast 상태 전환이 검증됐다.
|
||
|
||
- [x] **Task 3.3: data/model/ViewModel 통합 컴파일 확인**
|
||
- 확인:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
||
- 검증:
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: 팔로잉 data/model/ViewModel/DI 코드가 컴파일된다.
|
||
- Result: PASS. 팔로잉 data/model/ViewModel/DI 코드가 `compileDebugKotlin`에서 컴파일됐다.
|
||
- 검증 기록:
|
||
- 2026-06-25 코드 리뷰: Phase 1~3 범위의 API/DTO/Repository/mapper/ViewModel/DI/string/test 변경을 검토했으며, 현재 코드 기준으로 blocking finding은 발견하지 못했다.
|
||
- 2026-06-25 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
|
||
#### Task 3.4: ViewModel Loading → 결과 상태 발행 순서 회귀 검증
|
||
|
||
**Goal 실행 `P3-R1`:** 확정 review 항목 `REV-P3-001`에 따라 `loadFollowing()`의 `Loading`과 최종 상태 발행 순서를 실행 가능한 테스트로 고정한다.
|
||
|
||
- **시작 조건:** `reviews/phase3-following-viewmodel-review.md`의 `REV-P3-001` 확정.
|
||
- **완료 증거:** 상태 이력 assertion 추가 → Phase 3 focused test와 팔로잉 전체 회귀 PASS.
|
||
- **범위 밖:** `HomeFollowingViewModel` 상태 구조 변경, scheduler 교체, repository/API 변경.
|
||
- **TDD 예외 사유:** production은 이미 `Loading`과 결과 상태를 순서대로 발행하며, 확정 항목은 동작 결함이 아니라 기존 테스트의 assertion 누락이므로 의도적인 production 변조 없이 RED를 만들지 않는다.
|
||
- **대체 검증 방법:** observer 상태 이력으로 `Loading → Content`를 직접 assertion하고 기존 error/login-required 테스트와 전체 팔로잉 회귀를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt`
|
||
|
||
- [x] observer가 수집한 상태 이력이 `Loading`과 `Content`를 순서대로 포함하도록 테스트 fixture와 assertion을 최소 보완한다.
|
||
- [x] production 파일 변경이 없고 기존 error/login-required 테스트가 그대로 유지되는지 diff로 확인한다.
|
||
- [x] **GREEN 확인:** `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"`를 실행한다.
|
||
- [x] **회귀 검증:** `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`와 `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `HomeFollowingViewModelTest.loadFollowing은 loading 후 content 상태를 발행한다`에 observer 상태 이력 assertion을 추가해 `Loading → Content` 순서를 고정했다. production 파일은 변경하지 않았다. focused test와 팔로잉 전체 회귀, `git diff --check`가 PASS했다.
|
||
|
||
---
|
||
|
||
### Phase 4: 팔로잉 탭 UI surface와 adapter 연결
|
||
|
||
- [x] **Task 4.1: Fragment source RED 테스트 작성**
|
||
- 생성:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 테스트 케이스:
|
||
- `fragment_v2_main_home.xml`에 `nsv_home_following_content`가 있다.
|
||
- layout에 `rv_home_following_creators`, `rv_home_following_on_air_lives`, `rv_home_following_recent_chats`, `rv_home_following_monthly_schedules`, `rv_home_following_recent_news`가 있다.
|
||
- `HomeMainFragment`가 `HomeFollowingViewModel`을 주입한다.
|
||
- `HOME_TAB_FOLLOWING` 분기에서 팔로잉 surface를 visible 처리한다.
|
||
- section title chevron click listener가 연결되어 있고 실제 route 호출은 없다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: layout/fragment 수정 전 RED 실패.
|
||
- Result: RED 확인. layout ID와 `HomeMainFragment` 팔로잉 wiring 미구현 상태에서 6개 source test가 모두 실패했다.
|
||
|
||
- [x] **Task 4.2: 팔로잉 content layout 추가**
|
||
- 수정:
|
||
- `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- 생성:
|
||
- `app/src/main/res/layout/item_home_following_creator.xml`
|
||
- `app/src/main/res/layout/item_home_following_live.xml`
|
||
- `app/src/main/res/layout/item_home_following_chat.xml`
|
||
- `app/src/main/res/layout/item_home_following_schedule.xml`
|
||
- `app/src/main/res/layout/item_home_following_news_rank.xml`
|
||
- `app/src/main/res/layout/item_home_following_news_content.xml`
|
||
- 작업:
|
||
- `nsv_home_following_content`를 `text_tab_bar_home` 아래에 추가하고 기본 `visibility="gone"`으로 둔다.
|
||
- 섹션 순서는 `followingCreators`, `On Air`, `recentChats`, `monthlySchedules`, `recentNews`로 둔다.
|
||
- 각 섹션 title은 `view_section_title`을 include한다.
|
||
- 로그인 유도 화면 전용 layout은 이번 phase에서 추가하지 않는다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: 신규 layout/resource가 merge된다.
|
||
- Result: PASS. `nsv_home_following_content`, 5개 섹션 RecyclerView, 6개 item layout이 resource merge를 통과했다.
|
||
|
||
- [x] **Task 4.3: 팔로잉 adapter 구현**
|
||
- 생성:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingCreatorAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- 작업:
|
||
- 각 adapter는 `submitItems()`와 item click callback을 제공한다.
|
||
- profile image는 기존 `loadHomeCreatorProfileImage()` 또는 동일 placeholder 정책을 사용한다.
|
||
- recent chat은 `ChatRoomListUiItem`을 바인딩한다.
|
||
- schedule은 `CreatorActivityType.labelResId`, `isOnAir`, `scheduledAtUtc` 표시 모델을 사용한다.
|
||
- news adapter는 rank item과 content item view type을 분리한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: 신규 adapter가 컴파일된다.
|
||
- Result: PASS. 팔로잉 creator/live/chat/schedule/news adapter 5개가 `compileDebugKotlin`에서 컴파일됐다.
|
||
|
||
- [x] **Task 4.4: HomeMainFragment에 팔로잉 탭 연결**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- 작업:
|
||
- `private val homeFollowingViewModel: HomeFollowingViewModel by viewModel()`을 추가한다.
|
||
- 팔로잉 adapter 5개를 초기화한다.
|
||
- `showHomeTab(HOME_TAB_FOLLOWING)`에서 추천/랭킹 surface를 숨기고 팔로잉 surface를 표시한다.
|
||
- 팔로잉 탭 최초 선택 시 `homeFollowingViewModel.loadFollowing()`을 1회 호출한다.
|
||
- `LoginRequired` 상태에서는 팔로잉 섹션 content를 숨긴다.
|
||
- `Content` 상태에서는 각 section item이 비어 있으면 해당 섹션을 숨긴다.
|
||
- section title chevron은 `onFollowingSectionMoreClick(section)` callback까지만 연결하고 route는 호출하지 않는다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. 팔로잉 ViewModel 주입, adapter 연결, 탭 surface 전환, 최초 1회 load, login-required/empty/error 섹션 숨김, content 섹션 binding이 source test로 검증됐다.
|
||
|
||
- [x] **Task 4.5: UI routing skeleton 연결**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- 작업:
|
||
- creator item click은 기존 `openCreatorProfile(creatorId)`를 재사용한다.
|
||
- recent chat item click은 기존 v2 chat/DM 진입 flow를 확인해 재사용 가능한 메서드로 연결한다.
|
||
- live, schedule, news item click은 `type`/`targetId`별 route 함수로 분리한다.
|
||
- 목적지가 확정되지 않은 더보기 chevron은 route 없이 callback만 받는다.
|
||
- 검증:
|
||
- Run: `rg -n "openFollowing|onFollowing|HomeFollowingSection|openCreatorProfile|HOME_TAB_FOLLOWING" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- Expected: 팔로잉 item click과 more click 콜백 함수가 확인된다.
|
||
- Result: PASS. creator click은 `openCreatorProfile`, recent chat click은 기존 AI/DM chat 진입 flow로 연결했고 live/schedule/news/more click은 route 없는 callback skeleton으로 유지했다.
|
||
- 검증 기록:
|
||
- 2026-06-25 Phase 4 코드 리뷰: Figma `24:5682`와 PRD 기준으로 `On Air` 시작 시간, 이달의 스케줄 프로필/타입 label/On Air 상태, 최근 소식 label/title 바인딩 누락을 확인했다. 누락 항목은 `HomeFollowingFragmentSourceTest` RED로 고정한 뒤 adapter/layout 최소 수정으로 보완했다.
|
||
- 2026-06-25 Phase 4 재코드 리뷰: 현재 워킹트리 기준 `HomeMainFragment`, 팔로잉 adapter 5개, 팔로잉 layout, `HomeFollowingFragmentSourceTest`를 Figma `24:5682`/PRD와 대조했으며 blocking finding은 발견하지 못했다.
|
||
|
||
#### Task 4.6: On Air·스케줄 item 상세 이동 연결
|
||
|
||
**Goal 실행 `P4-R1`:** 확정 review 항목 `REV-P4-001`에 따라 On Air와 이달의 스케줄 item 터치가 기존 공통 도메인 액션으로 실제 이동하도록 한다.
|
||
|
||
- **시작 조건:** `reviews/phase4-following-ui-routing-review.md`의 `REV-P4-001` 확정과 `FollowingLiveResponse.liveId`, `FollowingScheduleResponse.type/targetId` 계약 확인.
|
||
- **완료 증거:** no-op 재현 RED → 기존 Live/Creator/Content/Community Action을 재사용한 GREEN → focused test와 공통 액션 회귀 PASS.
|
||
- **범위 밖:** 더보기 chevron 목적지, 최근 소식 `CONTENT_RANKING`·`PHOTO_CONTENT` 이동, 공통 액션 정책 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] **RED:** On Air와 schedule type별 공통 액션 호출을 요구하는 테스트를 추가해 현재 `Unit` 구현에서 실패하는지 확인한다.
|
||
- [x] **GREEN:** On Air는 `liveActionCoordinator.enterLiveRoom(item.liveId)`를 사용하고, 스케줄은 `CreatorActivityType`과 `targetId`를 기존 최근 활동 route 정책에 맞춰 Live/Content/Community 공통 액션으로 전달한다.
|
||
- [x] **방어 검증:** `targetId <= 0L`과 지원하지 않는 route는 공통 액션 호출 전에 무시하고, 직접 `Intent` 또는 legacy extra를 만들지 않는지 검증한다.
|
||
- [x] **회귀 검증:** home focused test, `v2.live.action.*`, `v2.content.action.*`, `v2.community.action.*`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeMainFragmentLoginGuardSourceTest`에 팔로잉 On Air와 스케줄 type별 공통 Action 호출 assertion을 추가했고, 구현 전 `onFollowingLiveClick()`/`onFollowingScheduleClick()`이 `Unit`이라 focused test가 실패하는 것을 확인했다.
|
||
- 2026-07-30 GREEN: On Air는 `liveActionCoordinator.enterLiveRoom(item.liveId)`, 스케줄은 `targetId > 0L`일 때 `CreatorActivityType.Live`는 live action, `LiveReplay`/`Audio`는 `ContentActionCommand.AudioDetail`, `Community`는 `CommunityActionCommand.PostDetail`로 연결했다. 직접 `Intent` 조립은 추가하지 않았다. focused test, 팔로잉 전체 회귀, 공통 action 테스트, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS.
|
||
|
||
#### Task 4.7: LoginRequired 당겨서 새로고침 종료 보장
|
||
|
||
**Goal 실행 `P4-R2`:** 확정 review 항목 `REV-P4-002`에 따라 팔로잉 새로고침 응답이 `LoginRequired`여도 spinner와 refresh 추적 상태를 종료한다.
|
||
|
||
- **시작 조건:** `reviews/phase4-following-ui-routing-review.md`의 `REV-P4-002` 확정과 기존 `finishHomePullRefresh(HOME_TAB_FOLLOWING)` 정책 확인.
|
||
- **완료 증거:** `LoginRequired` 분기의 refresh 종료 누락 RED → 최소 분기 보완 GREEN → 팔로잉·홈 refresh 회귀 PASS.
|
||
- **범위 밖:** 로그인 유도 UI/문구/CTA, `ensureV2Access` 정책, ViewModel API 계약 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
||
|
||
- [x] **RED:** pull refresh 중 `LoginRequired` 상태가 `finishHomePullRefresh(HOME_TAB_FOLLOWING)`을 호출하지 않는 현재 분기를 재현한다.
|
||
- [x] **GREEN:** `LoginRequired`에서 section 숨김 후 팔로잉 refresh를 종료하고, 일반 진입에서는 불필요한 scroll 변경이 없는 최소 분기를 적용한다.
|
||
- [x] **GREEN 확인:** 팔로잉 Fragment focused test와 홈 pull-refresh 관련 focused test를 실행한다.
|
||
- [x] **회귀 검증:** 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행하고 결과를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeFollowingFragmentSourceTest`가 `LoginRequired` 분기에서 `finishHomePullRefresh(HOME_TAB_FOLLOWING)` 호출을 요구하도록 보강했고, 구현 전 focused test 실패로 누락을 확인했다.
|
||
- 2026-07-30 GREEN: `LoginRequired` 분기에서 섹션을 숨긴 뒤 팔로잉 pull-refresh 종료를 호출하도록 최소 보완했다. focused test, 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS.
|
||
|
||
#### Task 4.8: blank 프로필 이미지의 이전 Coil 요청 해제
|
||
|
||
**Goal 실행 `P4-R3`:** 확정 review 항목 `REV-P4-003`에 따라 재사용된 프로필 `ImageView`에 blank URL을 바인딩할 때 이전 Coil 요청을 해제해 다른 item의 이미지가 뒤늦게 표시되지 않도록 한다.
|
||
|
||
- **시작 조건:** `reviews/phase4-following-ui-routing-review.md`의 `REV-P4-003` 확정과 PRD의 blank profile image fallback 정책 확인.
|
||
- **완료 증거:** 이전 Coil 요청이 남는 재사용 시나리오 RED → blank 분기에서 요청 해제와 drawable 초기화 GREEN → profile loader focused test와 팔로잉 전체 회귀 PASS.
|
||
- **범위 밖:** non-blank 이미지 transformation/placeholder 디자인 변경, Coil 전역 설정 변경, adapter 구조 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeCreatorProfileImageLoader.kt`
|
||
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorProfileImageLoaderTest.kt`
|
||
- Modify: `docs/agent-guides/build-test-style.md`
|
||
|
||
- [x] **RED:** Robolectric `ImageView`에 Coil 요청을 연결한 뒤 blank URL을 바인딩하고, 기존 요청이 해제되지 않는 현재 동작을 `coil.result` 또는 동등한 요청 상태 assertion으로 재현한다.
|
||
- [x] **GREEN:** `loadHomeCreatorProfileImage()`의 blank 분기에서 `coil.dispose()`로 이전 요청을 해제한 뒤 drawable을 비우는 최소 수정만 적용한다.
|
||
- [x] **GREEN 확인:** `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest"`를 실행한다.
|
||
- [x] **회귀 검증:** 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행하고 신규 테스트 단일 실행 예시를 빌드 가이드에 반영한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeCreatorProfileImageLoaderTest`를 추가해 blank profile URL 바인딩 시 기존 Coil `Disposable`이 해제되어야 함을 검증했고, 구현 전 `assertTrue(disposable.isDisposed)` 실패로 현재 요청이 남는 동작을 확인했다.
|
||
- 2026-07-30 GREEN: `loadHomeCreatorProfileImage()` blank 분기에서 `dispose()` 후 `setImageDrawable(null)`을 호출하도록 최소 수정했다. focused test, 팔로잉 전체 회귀와 신규 프로필 테스트, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS. `build-test-style.md`에 `HomeCreatorProfileImageLoaderTest` 단일 실행 예시를 추가했다.
|
||
|
||
#### Task 4.9: 팔로잉 공용 source test 이름 규칙 정합성 보완
|
||
|
||
**Goal 실행 `P4-R4`:** 확정 review 항목 `REV-P4-004`에 따라 `HomeFollowingFragmentSourceTest`에 남은 영문 문장형 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||
|
||
- **시작 조건:** `reviews/phase4-following-ui-routing-review.md`의 `REV-P4-004` 확정과 `docs/agent-guides/build-test-style.md`의 신규 Kotlin 테스트명 규칙 확인.
|
||
- **완료 증거:** 영문 문장형 테스트명 11개 한글화 → assertion·production 코드 무변경 확인 → Fragment focused test·팔로잉 전체 회귀·ktlint·diff 검증 PASS.
|
||
- **범위 밖:** 테스트 assertion 재설계, production 코드·resource 변경, 다른 테스트 클래스의 기존 이름 일괄 수정.
|
||
- **TDD 예외 사유:** runtime 동작 결함이 아니라 테스트 이름 규칙 정합성 수정이므로 production RED/GREEN을 만들지 않는다.
|
||
- **대체 검증 방법:** 대상 파일의 `@Test` 함수명을 `rg`로 확인하고 변경 전후 assertion diff, focused test, 팔로잉 전체 회귀를 대조한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] 완전한 영문 문장으로 남은 backtick 테스트명 11개를 각 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||
- [x] 테스트 본문의 assertion과 helper, production 코드·resource가 변경되지 않았는지 diff로 확인한다.
|
||
- [x] `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`를 실행한다.
|
||
- [x] 팔로잉 전체 회귀, `:app:ktlintCheck`, `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31: `HomeFollowingFragmentSourceTest`의 완전한 영문 문장형 backtick 테스트명 11개를 검증 의도가 드러나는 한글 문장으로 변경했다. Task 9.6의 신규 한글 테스트 추가 외 기존 assertion/helper와 production/resource 변경은 포함하지 않았다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, 팔로잉 전체 회귀와 `:app:compileDebugKotlin`, `:app:ktlintCheck` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
---
|
||
|
||
### Phase 5: 통합 검증과 문서 기록
|
||
|
||
- [x] **Task 5.1: 팔로잉 관련 단위 테스트 실행**
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
|
||
- 검증 기록:
|
||
- 2026-06-25 Phase 4 리뷰 보완 후 실행 결과 PASS. 팔로잉 관련 local unit/source test가 모두 통과했다.
|
||
|
||
- [x] **Task 5.2: 리소스/컴파일/린트 검증**
|
||
- 검증:
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: 신규 layout/string resource merge PASS.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `./gradlew :app:ktlintCheck`
|
||
- Expected: ktlint PASS.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-06-25 Phase 4 리뷰 보완 후 `./gradlew :app:mergeDebugResources` PASS. 최초 sandbox lock 권한 오류 후 승인 실행으로 통과했다.
|
||
- 2026-06-25 Phase 4 리뷰 보완 후 `./gradlew :app:compileDebugKotlin` PASS.
|
||
- 2026-06-25 Phase 4 리뷰 보완 후 `./gradlew :app:ktlintCheck` PASS. 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-25 Phase 4 리뷰 보완 후 `git diff --check` PASS.
|
||
|
||
- [ ] **Task 5.3: Figma 기준 수동 확인**
|
||
- 확인:
|
||
- Figma `24:5682`
|
||
- `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- 수동 확인 항목:
|
||
- `팔로잉` 탭 선택 시 추천/랭킹 content가 겹쳐 보이지 않는다.
|
||
- 섹션 순서가 `팔로잉 크리에이터` → `On Air` → `최근 대화` → `이달의 스케줄` → `최근 소식`이다.
|
||
- title bar와 tab bar는 고정되고 팔로잉 content만 세로 스크롤된다.
|
||
- empty section은 숨겨진다.
|
||
- `isLoginRequired` 상태에서 팔로잉 섹션 content는 표시되지 않는다.
|
||
- 더보기 chevron 터치 시 앱이 크래시하지 않고 화면 이동은 발생하지 않는다.
|
||
- 검증 기록:
|
||
- 2026-06-25 Figma `24:5682` 디자인 컨텍스트와 스크린샷 기준 정적 대조를 수행했다. 실제 기기/에뮬레이터에서의 수동 화면 확인은 아직 실행하지 않았다.
|
||
- 2026-06-25 Phase 5 진행: Figma `24:5682` 스크린샷과 `fragment_v2_main_home.xml`, `HomeMainFragment.kt`를 대조했다. 팔로잉 탭 전용 `nsv_home_following_content`가 title bar/tab bar 아래 별도 scroll surface로 배치되어 있고, 팔로잉 선택 시 추천/랭킹 surface를 숨기는 분기, 섹션 순서, empty/login-required 섹션 숨김, 더보기 chevron no-op callback 연결을 정적으로 확인했다.
|
||
- 2026-06-25 Phase 5 진행: 실제 기기 검증을 위해 `adb devices`에서 `2cec640c34017ece` 연결을 확인한 뒤 `./gradlew :app:installDebug`를 실행했으나, 설치 중 디바이스 연결이 해제되어 `device '2cec640c34017ece' not found`로 실패했다. 재확인 시 `adb devices`에 연결된 디바이스가 없어 실제 화면 수동 확인은 blocked 상태로 남긴다.
|
||
- 2026-07-31 Task 2.7 완료 후 재확인: `adb devices`는 정상 실행됐지만 `List of devices attached` 아래 연결 기기가 없어 Figma/API 데이터 기반 실기기 수동 확인은 계속 보류한다.
|
||
- 2026-07-31 Task 4.9/9.6 완료 중 재확인: `adb devices`에서 `2cec640c34017ece` 연결을 확인했고 `./gradlew --no-daemon :app:installDebug`, `adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1` PASS. 다만 `adb exec-out screencap -p`로 저장한 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sodalive-home-after-launch.png`와 5초 대기 후 재촬영한 `sodalive-home-after-launch-2.png`가 모두 검은 화면으로 분석되어 팔로잉 탭 content와 Figma `24:5682` 상세 대조는 완료하지 못했다.
|
||
|
||
---
|
||
|
||
### Phase 6: Figma 디자인 재대조 후속 수정
|
||
|
||
- [x] **Task 6.1: 디자인 불일치 RED 테스트 추가**
|
||
- 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- 팔로잉 크리에이터 섹션에 header include가 없는지 검증한다.
|
||
- 최근 대화 RecyclerView가 horizontal인지 검증한다.
|
||
- 최근 대화 item이 Figma box 카드 폭/프로필/Direct badge/상대시간 바인딩을 갖는지 검증한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: 구현 전 RED 실패.
|
||
- Result: RED 확인. 팔로잉 크리에이터 header 제거, 최근 대화 horizontal box list, 최근 대화 Figma box field 검증 3개가 현재 구현과 맞지 않아 실패했다.
|
||
|
||
- [x] **Task 6.2: 팔로잉 크리에이터와 최근 대화 UI 수정**
|
||
- 수정:
|
||
- `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- `app/src/main/res/layout/item_home_following_creator.xml`
|
||
- `app/src/main/res/layout/item_home_following_chat.xml`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt`
|
||
- 작업:
|
||
- 팔로잉 크리에이터 section header를 제거한다.
|
||
- 팔로잉 크리에이터 profile item을 Figma의 simple profile 크기에 맞춘다.
|
||
- 최근 대화 RecyclerView를 horizontal로 변경한다.
|
||
- 최근 대화 item을 Figma의 box card 형태로 조정하고 Direct badge와 상대시간 표시를 유지한다.
|
||
- 최근 대화 시간은 기존 `formatChatRoomLastMessageTime()`을 유지해 server ISO 시간을 디바이스 timezone 기준 상대시간/날짜로 표시한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. 팔로잉 크리에이터 header 제거, 75dp simple profile item, 최근 대화 horizontal box list, Direct badge/상대시간 바인딩이 source test로 검증됐다.
|
||
|
||
- [x] **Task 6.3: 후속 변경 통합 검증**
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: layout/resource merge PASS.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `./gradlew :app:ktlintCheck`
|
||
- Expected: ktlint PASS.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-06-26 후속 변경 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"` 최초 병렬 실행 중 `HomeFollowingViewModelTest.blank token이면 repository에 null auth header를 전달한다` 1건이 실패했으나, 동일 테스트 단독 재실행과 전체 팔로잉 테스트 단독 재실행은 모두 PASS했다. 실패는 `SharedPreferenceManager` 전역 상태를 쓰는 테스트의 병렬 Gradle 실행 간섭으로 판단했다.
|
||
- 2026-06-26 후속 변경 검증: `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-26 실제 표면 검증: `adb devices`에서 `2cec640c34017ece` 연결을 확인했고, `./gradlew :app:installDebug`로 `kr.co.vividnext.sodalive.debug` 설치 PASS. `adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1`로 런처 실행 PASS. 팔로잉 탭 내부 API 데이터 기반 화면 대조는 자동 조작/테스트 계정 상태가 없어 정적 source test와 설치/실행 검증으로 대체했다.
|
||
- 2026-06-26 리뷰 지적 수정: 최근 대화 adapter가 XML `284dp` 폭을 런타임 `MATCH_PARENT`로 덮는 문제를 확인했다. `HomeFollowingFragmentSourceTest.following recent chat item matches figma box fields`에 RED 검증을 추가했고, `HomeFollowingChatAdapter`를 `recyclerItemLayoutParams(parent)` 사용으로 변경해 XML 폭을 유지했다. 해당 테스트 재실행 PASS.
|
||
- 2026-06-26 테스트 안정화: `SharedPreferenceManager.resetForTest()`가 DataStore 저장값을 지우지 않아 `HomeFollowingViewModelTest` 묶음 실행 시 token이 이전 값으로 복원될 수 있음을 확인했다. 테스트 `setUp()`의 시작 token을 빈 값으로 명시해 테스트 격리를 보강했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"` 재실행 PASS.
|
||
- 2026-06-26 후속 요청 반영: 최근 대화 item의 대화 내용 표시를 한 줄로 제한하고 `ellipsize="end"`를 유지했다. `HomeFollowingFragmentSourceTest.following recent chat item matches figma box fields`에 `maxLines="1"`/ellipsis 검증을 추가해 RED 확인 후 GREEN 전환했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest.following recent chat item matches figma box fields"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` 모두 PASS.
|
||
|
||
- [x] **Task 6.4: 최근 소식 Feed 위젯 재사용과 chevron 후속 수정**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- 최근 소식 section title의 chevron을 표시하지 않는다.
|
||
- `COMMUNITY_POST` 최근 소식은 기존 `v2.widget.feed.FeedCommunityView`와 `view_feed_community.xml`을 재사용한다.
|
||
- ranking 최근 소식은 기존 `v2.widget.feed.FeedRankView`와 `view_feed_rank.xml`을 재사용한다.
|
||
- 오디오/화보 content 최근 소식은 기존 `v2.widget.feed.FeedContentView`와 `view_feed_content.xml`을 재사용한다.
|
||
- 최근 소식 API에 없는 댓글 수, 좋아요 수, 잠금/오너 액션 값은 임의 생성하지 않고 현재 모델 범위에서 0 또는 빈 값으로 둔다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: PASS.
|
||
- 검증 기록:
|
||
- 2026-06-26 최근 소식 후속 수정: 먼저 `HomeFollowingFragmentSourceTest`에 최근 소식 chevron 제거와 Feed 위젯 재사용 검증을 추가해 RED를 확인했다. 이후 `HomeMainFragment`에서 최근 소식 `showMore = true`와 chevron listener를 제거했고, `HomeFollowingNewsAdapter`가 `COMMUNITY_POST`는 `FeedCommunityView`, ranking은 `FeedRankView`, audio/photo content는 `FeedContentView`를 inflate/bind하도록 변경했다.
|
||
- 2026-06-26 최근 소식 후속 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 6.5: Feed 위젯 전환 후 obsolete 최근 소식 layout 제거
|
||
|
||
**Goal 실행 `P6-R1`:** 확정 review 항목 `REV-P6-001`에 따라 runtime에서 사용하지 않는 팔로잉 전용 news layout과 잘못된 preview 참조를 제거한다.
|
||
|
||
- **시작 조건:** `reviews/phase6-figma-follow-up-review.md`의 `REV-P6-001` 확정과 `HomeFollowingNewsAdapter`의 Feed 위젯 사용 확인.
|
||
- **완료 증거:** resource 참조 검색 → preview를 실제 Feed layout으로 교체 → obsolete layout 2개 삭제 → resource merge와 source test PASS.
|
||
- **범위 밖:** Feed 위젯 UI 재설계, 최근 소식 DTO/mapper/routing 변경.
|
||
|
||
**Files:**
|
||
|
||
- Delete: `app/src/main/res/layout/item_home_following_news_rank.xml`
|
||
- Delete: `app/src/main/res/layout/item_home_following_news_content.xml`
|
||
- Modify: `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] **RED:** `rg`와 source test로 두 obsolete layout이 runtime adapter에서 사용되지 않고 `tools:listitem`만 과거 layout을 가리키는 상태를 고정한다.
|
||
- [x] **GREEN:** 최근 소식 preview를 실제 `view_feed_content`로 교체하고 obsolete layout 2개를 삭제한다.
|
||
- [x] **GREEN 확인:** `./gradlew --no-daemon :app:mergeDebugResources`와 `HomeFollowingFragmentSourceTest`를 실행한다.
|
||
- [x] **회귀 검증:** 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeFollowingFragmentSourceTest`가 최근 소식 preview `tools:listitem`과 obsolete layout 파일 제거를 요구하도록 보강했고, 구현 전 과거 layout 참조/파일 존재로 실패했다.
|
||
- 2026-07-30 GREEN: `fragment_v2_main_home.xml`의 최근 소식 preview를 `@layout/view_feed_content`로 교체하고 `item_home_following_news_rank.xml`, `item_home_following_news_content.xml`을 삭제했다. `:app:mergeDebugResources`, focused test, 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS.
|
||
|
||
#### Task 6.6: 삭제된 최근 소식 layout의 파일 구조 문서 정합성 보완
|
||
|
||
**Goal 실행 `P6-R2`:** 확정 review 항목 `REV-P6-002`에 따라 상단 `파일 구조`가 Feed 위젯 전환 후 현재 resource 구성을 정확히 설명하도록 갱신한다.
|
||
|
||
- **시작 조건:** `reviews/phase6-figma-follow-up-review.md`의 `REV-P6-002` 확정과 Task 6.5의 obsolete layout 삭제 완료.
|
||
- **완료 증거:** 상단 파일 구조에서 삭제된 두 layout의 Create 항목 제거 또는 삭제 이력 명시 → 실제 Feed layout 참조와 문서 대조 → 문서 diff 검증 PASS.
|
||
- **범위 밖:** 삭제된 layout 복원, Feed 위젯 UI·adapter·DTO·routing 변경.
|
||
- **TDD 예외 사유:** 계획 문서의 현재 파일 목록을 바로잡는 문서 전용 수정이므로 실행 가능한 실패 test를 만들지 않는다.
|
||
- **대체 검증 방법:** `rg`로 삭제된 layout의 runtime 참조와 상단 파일 구조 문구를 대조하고 `git diff --check`를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `docs/20260625_메인_홈_팔로잉_탭/plan-task.md`
|
||
|
||
- [x] 상단 `파일 구조`에서 `item_home_following_news_rank.xml`, `item_home_following_news_content.xml`을 현재 생성 파일로 오인하지 않도록 정리한다.
|
||
- [x] `HomeFollowingNewsAdapter`가 재사용하는 `view_feed_rank`, `view_feed_content`, `view_feed_community`와 Task 6.5 삭제 이력을 문서에서 확인 가능하게 유지한다.
|
||
- [x] `rg -n "item_home_following_news_(rank|content)|view_feed_(rank|content|community)" docs/20260625_메인_홈_팔로잉_탭/plan-task.md app/src/main`으로 문서와 runtime 참조를 대조한다.
|
||
- [x] `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: 상단 `파일 구조`의 obsolete 최근 소식 layout 2개를 Create 항목에서 제거하고, runtime Feed layout 3종 재사용 및 Task 6.5 삭제 이력을 명시했다. `rg -n "item_home_following_news_(rank|content)|view_feed_(rank|content|community)" docs/20260625_메인_홈_팔로잉_탭/plan-task.md app/src/main` 대조 결과 runtime adapter는 `view_feed_rank`, `view_feed_content`, `view_feed_community`를 참조하고 삭제 layout은 문서/이력 참조로만 남는 것을 확인했다. `git diff --check` PASS.
|
||
|
||
#### Task 6.7: 최근 대화 고정 폭 LayoutParams 보존
|
||
|
||
**Goal 실행 `P6-R3`:** 확정 review 항목 `REV-P6-003`에 따라 최근 대화 item의 XML `284dp` 폭을 런타임에서도 유지하면서 기존 item 간격을 보존한다.
|
||
|
||
- **시작 조건:** `reviews/phase6-figma-follow-up-review.md`의 `REV-P6-003` 확정.
|
||
- **완료 증거:** inflate 직후 `284dp` width를 `WRAP_CONTENT`로 교체하는 경로 제거 → 기존 layout parameter에 end margin만 적용 → resource merge·Kotlin compile·팔로잉 회귀 PASS → 연결 기기 준비 시 Task 5.3에서 폭/간격 수동 확인.
|
||
- **범위 밖:** 최근 대화 카드의 Figma 크기 변경, 공통 helper의 전체 호출부 리팩터링, 다른 팔로잉 adapter 수정.
|
||
- **TDD 예외 사유:** `docs/agent-guides/code-style.md`가 View width·margin 같은 UI 표현 속성 테스트 추가를 금지하므로 새 자동 UI 크기 테스트를 작성하지 않는다.
|
||
- **대체 검증 방법:** `item_home_following_chat.xml`, `HomeFollowingChatAdapter.kt`, `HomeRecyclerItemLayoutParams.kt`를 정적 대조하고 resource merge·Kotlin compile·기존 팔로잉 로직 회귀 및 가능한 경우 실기기 수동 확인을 수행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt`
|
||
- Verify: `app/src/main/res/layout/item_home_following_chat.xml`
|
||
|
||
- [x] `HomeFollowingChatAdapter.onCreateViewHolder()`가 inflate된 root의 기존 width/height를 보존하고 end margin만 추가하도록 최소 수정한다.
|
||
- [x] 공통 helper나 다른 adapter를 불필요하게 변경하지 않았는지 diff로 확인한다.
|
||
- [x] `./gradlew --no-daemon :app:mergeDebugResources`, `:app:compileDebugKotlin`, 팔로잉 전체 회귀를 실행한다.
|
||
- [x] 연결 기기가 있으면 Task 5.3에서 최근 대화 카드 폭과 item 간격을 확인하고, 없으면 미실행 사유를 기록한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `HomeFollowingChatAdapter.onCreateViewHolder()`에서 `recyclerItemLayoutParams(parent)` 사용을 제거하고 inflate된 `RecyclerView.LayoutParams`의 width/height를 유지한 채 `spacing_12` end margin만 적용했다. 공통 helper와 다른 adapter는 변경하지 않았다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. `adb devices`에 연결 기기가 없어 최근 대화 카드 폭/간격 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.
|
||
|
||
#### Task 6.8: creator·최근 대화 UI 표현 속성 source test 정합성 보완
|
||
|
||
**Goal 실행 `P6-R4`:** 확정 review 항목 `REV-P6-004`에 따라 creator·최근 대화의 크기·방향·말줄임 값을 XML 문자열로 고정하는 source assertion을 제거하고 허용된 구조·바인딩 검증만 유지한다.
|
||
|
||
- **시작 조건:** `reviews/phase6-figma-follow-up-review.md`의 `REV-P6-004` 확정과 `docs/agent-guides/code-style.md` 테스트 관례 확인.
|
||
- **완료 증거:** 금지된 XML 크기·orientation·style·maxLines·ellipsize assertion 제거 → production XML/adapter 무변경 → focused test·resource merge·Kotlin compile·ktlint PASS → 실기기 가능 시 Task 5.3에서 표현 속성 대조.
|
||
- **범위 밖:** creator/chat production UI 값 변경, adapter 데이터 바인딩, 다른 기존 Feed 테스트 일괄 정리.
|
||
- **TDD 예외 사유:** 동작 결함 수정이 아닌 현행 테스트 정책 위반 assertion 제거이며 production은 변경하지 않는다.
|
||
- **대체 검증 방법:** Figma와 production XML/adapter는 정적 대조하고, 자동 검증은 section 존재·adapter view type·필수 field 바인딩·click route와 순수 로직으로 한정한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- Verify: `app/src/main/res/layout/item_home_following_creator.xml`
|
||
- Verify: `app/src/main/res/layout/item_home_following_chat.xml`
|
||
- Verify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt`
|
||
|
||
- [x] creator test에서 `75dp` width/height와 typography style source assertion을 제거하고 creator header 제거 구조 검증은 유지한다.
|
||
- [x] 최근 대화 test에서 XML orientation, `284dp`/`62dp` width/height, `maxLines`/`ellipsize` source assertion을 제거한다.
|
||
- [x] Direct badge 노출 결정, 상대 시간 formatter, 필수 field 바인딩과 adapter click 계약 검증은 유지한다.
|
||
- [x] `HomeFollowingFragmentSourceTest`, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, `git diff --check`를 실행하고 실기기 표현 확인 가능 여부를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31: creator source test명을 `팔로잉 크리에이터 섹션은 header 없이 item layout을 사용한다`로, 최근 대화 source test명을 `팔로잉 최근 대화 섹션은 RecyclerView와 adapter를 연결한다`, `팔로잉 최근 대화 item은 필수 field와 클릭 계약을 바인딩한다`로 한글화했다. creator의 `75dp`/typography, 최근 대화의 XML orientation, `284dp`/`62dp`, `maxLines`/`ellipsize` source assertion은 제거하고 creator header 제거, 필수 field id, Direct badge, 상대 시간 formatter, click listener 계약은 유지했다. `rg -n -- "75dp|284dp|62dp|android:orientation|android:maxLines|android:ellipsize|Typography.Body5" HomeFollowingFragmentSourceTest.kt` 결과 제거 대상 참조 0건을 확인했다. `HomeFollowingFragmentSourceTest`, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint PASS. `adb devices`에는 연결 기기가 없어 실기기 표현 확인은 Task 5.3 보류 상태를 유지한다.
|
||
|
||
---
|
||
|
||
### Phase 7: 최근 소식 ranking 문장과 오디오 콘텐츠 feed 정합성 후속 수정
|
||
|
||
- [x] **Task 7.1: Figma 기준과 현재 구현 불일치 RED 테스트 추가**
|
||
- 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt`
|
||
- 작업:
|
||
- 팔로잉 최근 소식 ranking item이 `creatorNickname`, `rank`, 문장형 message, `FeedRankHighlight`를 구성하는지 검증한다.
|
||
- `view_feed_content.xml`이 Figma `1229:27212` 기준 `88dp` 이미지, `14dp` 카드 gap, `6dp` 프로필-타이틀 간격을 갖는지 검증한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`
|
||
- Expected: 구현 전 RED 실패.
|
||
- Result: RED 확인. `HomeFollowingFragmentSourceTest.following adapters bind figma required item fields`, `HomeFollowingFragmentSourceTest.following ranking news builds sentence message and highlights rank text`, `FeedViewTest.content layout matches figma compact upload notice dimensions` 3건이 현재 구현과 맞지 않아 실패했다.
|
||
|
||
- [x] **Task 7.2: 최근 소식 ranking message와 content feed layout 수정**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- `app/src/main/res/layout/view_feed_content.xml`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedContentView.kt`
|
||
- 작업:
|
||
- ranking feed message는 `creatorNickname + rank + 문장`으로 구성하고, 순위 구간만 highlight range로 전달한다.
|
||
- content feed 이미지는 `88dp` 정사각 기준으로 변경하고, 오른쪽 정보 컬럼은 Figma의 프로필/제목/메타 간격을 유지한다.
|
||
- `FeedContentView`의 기본 이미지 크기 계산 기준을 `88dp`로 맞춘다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. ranking news가 크리에이터명/순위를 포함한 문장형 message와 순위 highlight range를 구성하고, content feed가 `88dp` 이미지 및 Figma compact feed 간격 기준을 갖도록 검증됐다.
|
||
|
||
- [x] **Task 7.3: 후속 변경 통합 검증**
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: layout/resource merge PASS.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `./gradlew :app:ktlintCheck`
|
||
- Expected: ktlint PASS.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-06-30 후속 변경 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"` PASS.
|
||
- 2026-06-30 후속 변경 검증: `./gradlew :app:mergeDebugResources`는 최초 병렬 실행 중 Gradle wrapper lock sandbox 권한 오류로 실패했고, 승인 실행 재시도에서 PASS.
|
||
- 2026-06-30 후속 변경 검증: `./gradlew :app:compileDebugKotlin` PASS.
|
||
- 2026-06-30 후속 변경 검증: `git diff --check` PASS.
|
||
- 2026-06-30 후속 변경 검증: `./gradlew :app:ktlintCheck`는 FAIL. 실패 지점은 이번 변경 파일이 아닌 `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingTopCardView.kt:11`의 기존 unused import이며, 저장소 규칙에 따라 unrelated 파일은 수정하지 않았다.
|
||
- 2026-07-30 재검증: 현재 working tree 기준 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
- [x] **Task 7.4: content feed radius clipping 구현 위치 수정**
|
||
- 수정:
|
||
- `app/src/main/res/layout/view_feed_content.xml`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedContentView.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt`
|
||
- 작업:
|
||
- `view_feed_content.xml`의 `android:clipToOutline` 속성을 제거한다.
|
||
- `FeedContentView` root view에서 `clipToOutline = true`와 `ViewOutlineProvider.setRoundRect(...)`를 설정한다.
|
||
- 가이드 `docs/agent-guides/code-style.md`의 radius clipping 규칙을 source test로 고정한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`
|
||
- Expected: RED 확인 후 PASS.
|
||
- Result: RED 확인 후 PASS. `view_feed_content.xml`의 `android:clipToOutline` 제거와 `FeedContentView` root의 Kotlin `clipToOutline`/`outlineProvider` 설정을 검증했다.
|
||
- 검증 기록:
|
||
- 2026-06-30 clipping 위치 수정 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS.
|
||
- 2026-06-30 clipping 위치 수정 후 `./gradlew :app:mergeDebugResources`는 최초 병렬 실행 중 Gradle wrapper lock sandbox 권한 오류로 실패했고, 승인 실행 재시도에서 PASS.
|
||
|
||
#### Task 7.5: ranking 순위 단위 locale 적용
|
||
|
||
**Goal 실행 `P7-R1`:** 확정 review 항목 `REV-P7-001`에 따라 ranking 문장의 순위 텍스트를 한국어·영어·일본어 locale에 맞게 표시하고 같은 구간을 highlight한다.
|
||
|
||
- **시작 조건:** `reviews/phase7-ranking-content-feed-review.md`의 `REV-P7-001` 확정.
|
||
- **완료 증거:** 영어·일본어에서도 `"위"`가 노출되는 RED → locale string format GREEN → 세 locale와 highlight range 회귀 PASS.
|
||
- **범위 밖:** ranking message 문장 자체 재기획, FeedRankView 스타일·색상 변경, API rank 계약 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- Modify: `app/src/main/res/values/strings.xml`
|
||
- Modify: `app/src/main/res/values-en/strings.xml`
|
||
- Modify: `app/src/main/res/values-ja/strings.xml`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt`
|
||
- Modify: `docs/agent-guides/build-test-style.md`
|
||
|
||
- [x] **RED:** locale별 ranking message를 생성해 영어·일본어 결과에 한국어 `"위"`가 포함되는 현재 문제를 재현한다.
|
||
- [x] **GREEN:** 순위 단위를 locale string resource로 분리해 한국어 `위`, 영어 locale 표기, 일본어 `位`를 사용한다.
|
||
- [x] **highlight 검증:** locale별 완성 문장에서 순위 문자열 index가 유효하고 `FeedRankHighlight` 범위가 해당 문자열과 일치하는지 검증한다.
|
||
- [x] **회귀 검증:** focused test, Feed view test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, `git diff --check`를 실행한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeFollowingNewsAdapterTest`를 추가해 한국어/영어/일본어 locale별 ranking message와 highlight range를 검증했고, 구현 전 `buildHomeFollowingRankingFeedItem` 및 locale rank format 부재로 focused test가 실패했다.
|
||
- 2026-07-30 GREEN: `screen_home_following_ranking_rank_format`을 3개 locale에 추가하고 ranking feed item 생성 helper에서 locale rank 문자열을 만든 뒤 같은 문자열 범위를 `FeedRankHighlight`로 전달하도록 변경했다. 한국어 `7위`, 영어 `No. 7`, 일본어 `7位` focused test, Feed view test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, `git diff --check` PASS.
|
||
- 2026-07-30 리뷰 게이트 후 보완: subject에 같은 rank 문자열이 먼저 포함될 때 highlight가 잘못 잡힐 수 있다는 Minor 지적을 받아 `lastIndexOf(rankText)`로 삽입된 순위 문자열을 강조하도록 보완했다. 중복 문자열 fixture RED 확인 후 `HomeFollowingNewsAdapterTest`, focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, `git diff --check` PASS.
|
||
|
||
#### Task 7.6: 신규 ranking 테스트와 실행 가이드 정합성 보완
|
||
|
||
**Goal 실행 `P7-R2`:** 확정 review 항목 `REV-P7-002`에 따라 신규 ranking 단위 테스트가 저장소의 한글 테스트명과 단일 실행 가이드 규칙을 충족하도록 한다.
|
||
|
||
- **시작 조건:** `reviews/phase7-ranking-content-feed-review.md`의 `REV-P7-002` 확정과 `HomeFollowingNewsAdapterTest` 추가 상태.
|
||
- **완료 증거:** 신규 test name 한글화 → `build-test-style.md` 단일 실행 예시 추가 → focused test·ktlint·문서 diff PASS.
|
||
- **범위 밖:** ranking message·highlight production 로직, locale 문구, 기존 영어 테스트명의 일괄 변경.
|
||
- **TDD 예외 사유:** 동작 결함이 아니라 테스트/가이드 규칙 정합성 수정이므로 production RED를 만들지 않는다.
|
||
- **대체 검증 방법:** 테스트 메서드명과 가이드 명령을 `rg`로 확인하고 focused test와 ktlint를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt`
|
||
- Modify: `docs/agent-guides/build-test-style.md`
|
||
|
||
- [x] `HomeFollowingNewsAdapterTest`의 신규 backtick 테스트명 2개를 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||
- [x] `docs/agent-guides/build-test-style.md`에 `HomeFollowingNewsAdapterTest` 클래스 단위 실행 예시를 추가한다.
|
||
- [x] `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`를 실행한다.
|
||
- [x] `./gradlew --no-daemon :app:ktlintCheck`와 `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `HomeFollowingNewsAdapterTest`의 신규 ranking 테스트명 2개를 한글 문장으로 변경하고 `build-test-style.md`에 클래스 단위 실행 예시를 추가했다. `rg`로 한글 테스트명과 가이드 예시를 확인했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 7.7: content feed UI 표현 속성 source test 정책 정합성 보완
|
||
|
||
**Goal 실행 `P7-R3`:** 확정 review 항목 `REV-P7-003`에 따라 가이드 제정 이후 추가된 content feed 크기·padding·margin source test를 제거하고 허용된 대체 검증으로 전환한다.
|
||
|
||
- **시작 조건:** `reviews/phase7-ranking-content-feed-review.md`의 `REV-P7-003` 확정.
|
||
- **완료 증거:** `content layout matches figma compact upload notice dimensions` 테스트 제거 → production layout/View 코드는 유지 → Feed/팔로잉 회귀·resource merge·Kotlin compile·ktlint PASS.
|
||
- **범위 밖:** `view_feed_content.xml`의 88dp/간격 값 변경, `FeedContentView` clipping 정책 변경, 다른 기존 Feed 테스트 일괄 정리.
|
||
- **TDD 예외 사유:** 동작 결함 수정이 아니라 금지된 UI 표현 속성 테스트 제거이므로 RED/GREEN production 변경을 만들지 않는다.
|
||
- **대체 검증 방법:** Figma 기준과 `view_feed_content.xml`/`FeedContentView.kt`를 정적 대조하고 resource merge·compile 및 Task 5.3 수동 화면 확인을 사용한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt`
|
||
|
||
- [x] `content layout matches figma compact upload notice dimensions` 테스트만 제거하고 production 파일을 변경하지 않는다.
|
||
- [x] `FeedViewTest`의 로직·접근성·clipping 계약 테스트는 이번 범위에서 유지한다.
|
||
- [x] Feed focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint를 실행한다.
|
||
- [x] Figma 크기/간격 대조 결과와 실기기 확인 가능 여부를 검증 기록에 남긴다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `FeedViewTest`에서 content feed 크기·padding·margin 문자열을 고정하던 `content layout matches figma compact upload notice dimensions` 테스트만 제거했다. `view_feed_content.xml`과 `FeedContentView.kt` production 코드는 변경하지 않았고, content meta row·clipping·접근성 테스트는 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. Figma 크기/간격은 기존 production XML/View 정적 대조로 유지하며, `adb devices`에 연결 기기가 없어 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.
|
||
|
||
---
|
||
|
||
### Phase 8: 팔로잉 크리에이터 전체 버튼 후속 추가
|
||
|
||
- [x] **Task 8.1: PRD 반영과 RED 테스트 추가**
|
||
- 수정:
|
||
- `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- `docs/20260625_메인_홈_팔로잉_탭/plan-task.md`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- 팔로잉 크리에이터 RecyclerView 마지막 item에 `전체` 버튼을 항상 추가하는 후속 요구사항을 문서화한다.
|
||
- `전체` 버튼 layout이 `wrap_content`, creator item과 동일한 높이, `paddingHorizontal=16dp`, `@color/soda_400`를 갖는지 source test로 고정한다.
|
||
- adapter가 creator item과 all item view type을 분리하고 마지막 item count를 보장하는지 source test로 고정한다.
|
||
- `전체` 버튼 클릭이 기존 `FollowingCreatorActivity`로 연결되는지 source test로 고정한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: 구현 전 RED 실패.
|
||
- Result: RED 확인. `following creators list appends all button as last item`가 `item_home_following_creator_all.xml` 누락으로 실패했다.
|
||
|
||
- [x] **Task 8.2: 팔로잉 크리에이터 전체 버튼 구현**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingCreatorAdapter.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- `app/src/main/res/layout/item_home_following_creator_all.xml`
|
||
- 작업:
|
||
- `HomeFollowingCreatorAdapter`에 creator/all view type을 추가한다.
|
||
- `submitItems()`에 1개 이상 creator가 들어오면 adapter 마지막 item으로 `전체` 버튼을 표시한다.
|
||
- `전체` 버튼은 `width=wrap_content`, creator item과 동일한 높이, `paddingHorizontal=16dp`, `textColor=@color/soda_400`를 XML에 명시한다.
|
||
- `전체` 버튼 클릭 시 `ensureMainV2NavigationAllowed` 안에서 `FollowingCreatorActivity`를 시작한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. adapter creator/all view type, 마지막 `전체` item, `FollowingCreatorActivity` 이동 연결, `전체` layout token이 source test로 검증됐다.
|
||
|
||
- [x] **Task 8.3: 후속 변경 통합 검증**
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: layout/resource merge PASS.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `./gradlew :app:ktlintCheck`
|
||
- Expected: ktlint PASS 또는 기존 unrelated failure만 발생.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 후속 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"` PASS.
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 후속 검증: `./gradlew :app:mergeDebugResources`는 최초 sandbox 실행에서 Gradle wrapper lock 권한 오류로 실패했고, 승인 실행 재시도에서 PASS.
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 후속 검증: `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 높이 보정 후 재검증: creator item 전체 높이에 맞추기 위해 invisible nickname spacer를 추가했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` PASS. `mergeDebugResources`는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했고, `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 8.4: 팔로잉 크리에이터 전체 item 테스트 정책 정합성 보완
|
||
|
||
**Goal 실행 `P8-R1`:** 확정 review 항목 `REV-P8-001`에 따라 adapter의 마지막 `전체` item과 이동 로직 검증은 유지하고, 금지된 width·height·padding·visibility source assertion만 제거한다.
|
||
|
||
- **시작 조건:** `reviews/phase8-following-creators-all-review.md`의 `REV-P8-001` 확정.
|
||
- **완료 증거:** UI 표현 속성 assertion 제거 및 남은 테스트명 한글화 → adapter view type/개수/route 검증 유지 → focused test·resource merge·Kotlin compile·ktlint PASS.
|
||
- **범위 밖:** `item_home_following_creator_all.xml` 디자인 값 변경, adapter 동작 변경, 다른 기존 영문 테스트명 일괄 변경.
|
||
- **TDD 예외 사유:** production 동작 결함이 아니라 테스트 정책 정합성 수정이므로 production RED를 만들지 않는다.
|
||
- **대체 검증 방법:** XML 토큰은 review 시 정적 대조하고 실제 높이·padding·색상은 resource merge와 Task 5.3 수동 화면 확인으로 검증한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] `following creators list appends all button as last item`에서 layout width·height·padding·visibility·색상 문자열 assertion만 제거한다.
|
||
- [x] adapter의 creator/all view type, item count, 마지막 item, `FollowingCreatorActivity` route assertion은 유지한다.
|
||
- [x] 수정하는 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||
- [x] focused test, resource merge, Kotlin compile, ktlint를 실행하고 대체 검증 결과를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `following creators list appends all button as last item` 테스트명을 `팔로잉 크리에이터 목록은 전체 item을 마지막에 추가하고 이동을 연결한다`로 한글화하고, width/height/padding/visibility/color source assertion만 제거했다. adapter view type, item count, 마지막 all item, `FollowingCreatorActivity` route, all label string 검증은 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다.
|
||
|
||
---
|
||
|
||
### Phase 9: 최근 소식 Response nested payload 계약 반영
|
||
|
||
- [x] **Task 9.1: 최근 소식 새 응답 계약 RED 테스트 추가**
|
||
- 수정:
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- `FollowingNewsResponse` fixture를 새 nested payload 구조로 교체한다.
|
||
- `CREATOR_RANKING`은 `creatorRanking.rank`, `creatorRanking.creatorId`, `creatorRanking.nickname`, `creatorRanking.profileImageUrl`로 ranking UI item을 만드는지 검증한다.
|
||
- `CONTENT_RANKING`은 `contentRanking.rank`, `contentRanking.contentId`, `contentRanking.contentImageUrl`, `contentRanking.title`로 ranking UI item을 만드는지 검증한다.
|
||
- `AUDIO_CONTENT`는 `audioContent`, `PHOTO_CONTENT`는 `photoContent` payload로 content UI item을 만드는지 검증한다.
|
||
- `COMMUNITY_POST`는 `communityPost.postId`, `creatorProfileImage`, `creatorNickname`, `imageUrl`, `content`, `createdAt`, `likeCount`, `commentCount`를 UI item에 반영하는지 검증한다.
|
||
- `type`에 대응하는 payload가 null인 최근 소식은 `recentNews` section에서 제외되는지 검증한다.
|
||
- `visibleFromAtUtc`는 기존처럼 `UtcRelativeTimeTextFormatter`에 전달되는지 검증한다.
|
||
- source test로 `HomeFollowingModels.kt`에 기존 평면 필드(`creatorProfileImageUrl`, `creatorNickname`, `title`, `body`, `thumbnailImageUrl`, `targetId`, `occurredAtUtc`, `rank`)가 `FollowingNewsResponse` 직접 필드로 남지 않는지 확인한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: DTO/UI model/mapper 변경 전 RED 실패.
|
||
- Result: RED 확인. `FollowingCommunityPostNewsResponse`, `FollowingContentNewsResponse`, `FollowingContentRankingNewsResponse`, `FollowingCreatorRankingNewsResponse` 미정의와 `FollowingNewsResponse` nested parameter 미지원, 신규 UI item 필드 부재로 `compileDebugUnitTestKotlin`이 실패했다.
|
||
|
||
- [x] **Task 9.2: DTO와 UI model을 nested payload 기준으로 갱신**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt`
|
||
- `app/src/main/res/values/strings.xml`
|
||
- `app/src/main/res/values-en/strings.xml`
|
||
- `app/src/main/res/values-ja/strings.xml`
|
||
- 작업:
|
||
- `FollowingNewsResponse` 직접 필드는 `newsId`, `type`, `visibleFromAtUtc`, `creatorRanking`, `audioContent`, `photoContent`, `contentRanking`, `communityPost`만 유지한다.
|
||
- `FollowingCreatorRankingNewsResponse`, `FollowingContentNewsResponse`, `FollowingContentRankingNewsResponse`, `FollowingCommunityPostNewsResponse` DTO를 추가하고 모두 `@Keep`, `@SerializedName`을 적용한다.
|
||
- `HomeFollowingNewsUiItem`은 새 payload 차이를 표현할 수 있도록 ranking/content/community variant를 분리하거나 기존 variant를 최소 확장한다.
|
||
- ranking UI item은 `rank`, target id, 표시 이미지 URL, 표시 제목/이름, `visibleFromAtUtc`, `visibleFromText`를 가진다.
|
||
- content UI item은 `contentId`, `contentImageUrl`, `title`, `creatorProfileImageUrl`, `creatorNickname`, label resource id, `visibleFromAtUtc`, `visibleFromText`를 가진다.
|
||
- community UI item은 `postId`, `creatorProfileImageUrl`, `creatorNickname`, `imageUrl`, `content`, `createdAt`, `likeCount`, `commentCount`, `visibleFromAtUtc`, `visibleFromText`를 가진다.
|
||
- `CONTENT_RANKING` 문장형 message가 필요하면 `screen_home_following_content_ranking_news_message`를 3개 locale string resource에 추가한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: string resource가 중복 없이 merge된다.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: DTO/UI model 변경 후 컴파일 오류가 mapper/adapter 미반영 지점으로만 제한된다.
|
||
- Result: PASS. `FollowingNewsResponse` 직접 필드를 nested payload 계약으로 교체하고, ranking/content/community UI model variant 및 3개 locale content ranking message string을 추가한 뒤 최종 `mergeDebugResources`, `compileDebugKotlin`이 통과했다.
|
||
|
||
- [x] **Task 9.3: mapper와 최근 소식 adapter 바인딩 갱신**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- mapper는 `type`별 matching payload만 읽고, payload가 null이면 `null`을 반환해 해당 item을 제외한다.
|
||
- `CREATOR_RANKING` adapter binding은 기존 `screen_home_following_creator_ranking_news_message`와 `FeedRankHighlight` 정책을 유지하되 `creatorRanking.nickname`, `creatorRanking.rank`, `creatorRanking.profileImageUrl`을 사용한다.
|
||
- `CONTENT_RANKING` adapter binding은 content title, rank, content image로 `FeedRankView`를 구성하고 순위 텍스트만 highlight한다.
|
||
- `AUDIO_CONTENT`, `PHOTO_CONTENT` adapter binding은 `FollowingContentNewsResponse` payload의 content/creator 필드를 `FeedContentView`에 전달한다.
|
||
- `COMMUNITY_POST` adapter binding은 `FeedCommunityView`에 `postId`, creator profile/nickname, content, image, `likeCount`, `commentCount`를 전달한다.
|
||
- 최근 소식 item click target id는 `creatorId`, `contentId`, `postId` 중 payload에 맞는 값을 사용한다.
|
||
- `visibleFromAtUtc` 기준 상대 시간 표시 정책은 유지한다.
|
||
- 코드 리뷰에서 확인한 `contentImageUrl` nullable 계약을 DTO/UI model에 반영하고, Feed 위젯에는 빈 문자열 fallback으로 전달한다.
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected: PASS.
|
||
- Result: PASS. 최초 병렬 Gradle 실행 중 Kotlin incremental cache 충돌과 source-level test 기대 문자열 불일치 2건을 확인했다. Gradle을 `--no-daemon` 순차 실행으로 전환하고 source test 기대 문자열을 새 `HomeFollowingNewsUiItem.Community` 분기 구조에 맞춘 뒤 focused test가 PASS했다.
|
||
- 2026-06-30 코드 리뷰 후 보완 검증: `contentImageUrl = null`인 `CONTENT_RANKING`, `AUDIO_CONTENT` 최근 소식이 유지되는 mapper test를 추가했다. RED: DTO `contentImageUrl`이 non-null `String`이라 `compileDebugUnitTestKotlin` type mismatch 실패. GREEN: DTO/UI model을 `String?`로 변경하고 adapter에서 `.orEmpty()` fallback을 적용한 뒤 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"` PASS.
|
||
|
||
- [x] **Task 9.4: 최근 소식 Response 변경 통합 검증**
|
||
- 검증:
|
||
- Run: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
|
||
- Run: `./gradlew :app:mergeDebugResources`
|
||
- Expected: layout/resource merge PASS.
|
||
- Run: `./gradlew :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `./gradlew :app:ktlintCheck`
|
||
- Expected: ktlint PASS 또는 기존 unrelated failure만 발생.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-06-30 Phase 9 focused 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"` PASS.
|
||
- 2026-06-30 Phase 9 통합 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 Phase 9 검증 참고: 최초 병렬 Gradle 검증은 Kotlin incremental cache `Storage ... is already registered` 충돌과 timeout으로 중단되어, 이후 모든 Gradle 검증은 `--no-daemon` 순차 실행으로 재검증했다.
|
||
- 2026-06-30 Phase 9 코드 리뷰 후 재검증: `contentImageUrl` nullable 계약을 보완한 뒤 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 9.5: nullable 최근 소식 이미지의 이전 Coil 요청 해제
|
||
|
||
**Goal 실행 `P9-R1`:** 확정 review 항목 `REV-P9-001`에 따라 nullable/blank 최근 소식 이미지를 바인딩할 때 재사용된 `ImageView`의 이전 Coil 요청을 해제해 다른 news item 이미지가 노출되지 않도록 한다.
|
||
|
||
- **시작 조건:** `reviews/phase9-news-nested-payload-review.md`의 `REV-P9-001` 확정과 `contentImageUrl`, `imageUrl` nullable 계약 확인.
|
||
- **완료 증거:** 최근 소식 ViewHolder 재사용 시 이전 요청이 남는 RED → 공통 blank image 분기 요청 해제 GREEN → adapter focused test와 Phase 9 회귀 PASS.
|
||
- **범위 밖:** nullable image의 placeholder/숨김 디자인 변경, Feed 위젯 레이아웃 변경, DTO·mapper·routing 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt`
|
||
|
||
- [x] **RED:** 완료된 Coil 요청이 연결된 최근 소식 image view를 nullable image item에 재사용한 뒤 요청이 남는 현재 동작을 Robolectric adapter binding test로 재현한다.
|
||
- [x] **GREEN:** `HomeFollowingNewsAdapter.bindImage()`의 blank 분기에서 `coil.dispose()`로 이전 요청을 해제한 뒤 drawable을 비우는 최소 수정만 적용한다.
|
||
- [x] **GREEN 확인:** `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`를 실행한다.
|
||
- [x] **회귀 검증:** mapper와 팔로잉 전체 회귀, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeFollowingNewsAdapterTest`에 nullable content image ViewHolder 재사용 테스트를 추가했고, 구현 전 `assertTrue(disposable.isDisposed)` 실패로 이전 Coil 요청이 남는 동작을 확인했다.
|
||
- 2026-07-30 GREEN: `HomeFollowingNewsAdapter.bindImage()` blank 분기에서 `imageView.dispose()` 후 drawable을 비우도록 최소 수정했다. focused test, 팔로잉 전체 회귀와 신규 프로필 테스트, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 9.6: 최근 소식 Feed model의 미제공 creator ID 합성 제거
|
||
|
||
**Goal 실행 `P9-R2`:** 확정 review 항목 `REV-P9-002`에 따라 API payload에 없는 `creatorId`를 `contentId` 또는 `postId`로 대신 채우지 않고 미제공 값으로 유지한다.
|
||
|
||
- **시작 조건:** `reviews/phase9-news-nested-payload-review.md`의 `REV-P9-002` 확정과 `FollowingContentNewsResponse`, `FollowingCommunityPostNewsResponse`에 `creatorId`가 없음을 확인.
|
||
- **완료 증거:** 잘못된 ID 대입을 고정한 RED → 두 `FeedItem`의 `creatorId`를 빈 값으로 전달하는 GREEN → 최근 소식 adapter focused test·팔로잉 전체 회귀·Kotlin compile·ktlint PASS.
|
||
- **범위 밖:** 서버 DTO에 `creatorId` 추정 추가, API 스키마 변경, 최근 소식 클릭 routing·Feed 공통 model 구조 변경.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] **RED:** `Content`와 `Community` binding이 API에 없는 `creatorId`를 각각 `contentId`·`postId`로 합성하지 않도록 요구하는 한글명 회귀 테스트를 추가하고 현재 실패를 확인한다.
|
||
- [x] **GREEN:** 두 `FeedItem`의 `creatorId`에 빈 문자열을 전달하고 원본 `contentId`·`postId`, creator 이름·이미지, 기존 click callback은 유지한다.
|
||
- [x] **GREEN 확인:** `HomeFollowingFragmentSourceTest`와 `HomeFollowingNewsAdapterTest`를 실행한다.
|
||
- [x] **회귀 검증:** 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31 RED: `HomeFollowingFragmentSourceTest`에 `팔로잉 최근 소식 feed model은 미제공 creator id를 합성하지 않는다`를 추가했고, 구현 전 `creatorId = community.targetId.toString()`/`creatorId = content.targetId.toString()` 때문에 focused test가 17개 중 1개 실패했다.
|
||
- 2026-07-31 GREEN: `HomeFollowingNewsAdapter`의 `FeedItem.Community`와 `FeedItem.Content`에서 `creatorId = ""`를 전달하도록 변경했다. `postId = community.postId.toString()`, `contentId = content.contentId.toString()`, creator 이름·이미지와 원본 item click callback은 유지했다. `rg`로 잘못된 targetId 대입 0건과 빈 creatorId 2건, content/post ID 보존을 확인했다. focused test, 팔로잉 전체 회귀, `:app:compileDebugKotlin`, `:app:ktlintCheck` PASS. `./gradlew --no-daemon :app:installDebug`와 launcher monkey 실행도 PASS했으나, 화면 캡처가 검은 화면이라 실제 팔로잉 탭 터치 대조는 Task 5.3에 미완료로 유지한다.
|
||
|
||
#### Task 9.7: 미사용 FeedAdapter 제거와 현재 문서 정리
|
||
|
||
**Goal 실행 `P9-R3`:** 현재 production/test 호출자가 없는 `FeedAdapter`와 `FeedImageViews`를 제거하고, 팔로잉 문서의 재사용 후보 설명을 실제 사용 중인 Feed view/model 기준으로 정리한다.
|
||
|
||
- **시작 조건:** `FeedAdapter`/`FeedImageViews` production/test 호출자 0건 확인.
|
||
- **완료 증거:** `FeedAdapter.kt` 삭제 → 현재 팔로잉 PRD/계획의 `FeedAdapter` 참조 제거 → Kotlin compile·Feed/팔로잉 회귀·diff 검증 PASS.
|
||
- **범위 밖:** 과거 완료 문서의 생성 이력 수정, `FeedRankView`/`FeedContentView`/`FeedCommunityView`/`FeedItem` 구조 변경, 신규 공용 adapter 재작성.
|
||
- **TDD 예외 사유:** 실행 동작 추가가 아니라 호출자 0건인 미사용 Kotlin 파일 삭제와 문서 정리이므로 신규 RED 테스트를 만들지 않는다.
|
||
- **대체 검증 방법:** `codegraph_callers`와 `rg`로 호출자/참조 0건을 확인하고 Kotlin compile, 관련 unit/source test, `git diff --check`를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Delete: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedAdapter.kt`
|
||
- Modify: `docs/20260625_메인_홈_팔로잉_탭/plan-task.md`
|
||
- Modify: `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
|
||
- [x] `codegraph_callers`와 `rg -n "FeedAdapter|FeedImageViews" app/src/main/java app/src/test/java docs/20260625_메인_홈_팔로잉_탭 docs/agent-guides`로 production/test 호출자 0건과 현재 문서 참조만 남았는지 확인한다.
|
||
- [x] `FeedAdapter.kt`를 삭제한다.
|
||
- [x] 현재 팔로잉 PRD/계획 문서에서 `FeedAdapter` 재사용 후보 문구를 제거하고 실제 재사용 중인 `FeedItem`, `FeedRankView`, `FeedContentView`, `FeedCommunityView` 기준으로 정리한다.
|
||
- [x] `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check`를 실행하고 결과를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-31: `codegraph_callers` 결과 `FeedAdapter` 호출자 0건을 확인했고, `rg -n "FeedAdapter|FeedImageViews" app/src/main/java app/src/test/java docs/20260625_메인_홈_팔로잉_탭 docs/agent-guides` 결과 production/test 참조 없이 Task 9.7 문서 참조만 남는 것을 확인했다. `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedAdapter.kt`를 삭제하고 현재 팔로잉 PRD/계획 문서의 재사용 후보를 `FeedItem`, `FeedRankView`, `FeedContentView`, `FeedCommunityView` 기준으로 정리했다. `./gradlew --no-daemon :app:compileDebugKotlin :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" :app:ktlintCheck`, `git diff --check` PASS.
|
||
|
||
---
|
||
|
||
### Phase 10: On Air 라이브 item Figma 재구현
|
||
|
||
- [x] **Task 10.1: Figma 기준 레이아웃 재구현**
|
||
- 수정:
|
||
- `app/src/main/res/layout/item_home_following_live.xml`
|
||
- 생성:
|
||
- `app/src/main/res/drawable/bg_home_following_live_capsule.xml`
|
||
- 작업:
|
||
- Figma `24:5696` 기준으로 `item_home_following_live.xml`을 썸네일 카드가 아닌 캡슐형 On Air item으로 재구현한다.
|
||
- 기존 `HomeFollowingLiveAdapter`가 사용하는 표시 id(`iv_home_following_live_creator_profile`, `tv_home_following_live_title`, `tv_home_following_live_creator_nickname`, `tv_home_following_live_started_at`)는 유지한다.
|
||
- 카드 배경은 `gray_900`, stroke는 `color_3bb9f1`, radius는 `90dp`, 전체 크기는 Figma의 `263dp x 100dp` 기준으로 맞춘다.
|
||
- LIVE badge, 시작 시간, 제목, 크리에이터명을 Figma 순서와 간격에 맞게 배치한다.
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:mergeDebugResources`
|
||
- Expected: layout/drawable resource merge PASS.
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 기존 팔로잉 source/unit test PASS.
|
||
- 검증 기록:
|
||
- 2026-07-03 Figma `24:5696` 기준으로 `item_home_following_live.xml`을 기존 썸네일 카드에서 `263dp x 100dp` 캡슐형 On Air item으로 재구현했다. 기존 adapter id 계약은 유지했고, profile image는 기존 `loadHomeCreatorProfileImage()`의 `CircleCropTransformation` 경로를 그대로 사용한다.
|
||
- 2026-07-03 검증: `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `git diff --check` PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
|
||
- 2026-07-03 후속 정리: Figma 캡슐형 item에 필요 없는 `iv_home_following_live_thumbnail` 숨김 View와 `HomeFollowingLiveAdapter`의 obsolete thumbnail binding을 제거했다.
|
||
|
||
#### Task 10.2: On Air 가로 item 간격 복구
|
||
|
||
**Goal 실행 `P10-R1`:** 확정 review 항목 `REV-P10-001`에 따라 연속된 On Air 캡슐 item 사이에 기존 홈 가로 리스트와 동일한 간격을 적용한다.
|
||
|
||
- **시작 조건:** `reviews/phase10-on-air-live-item-review.md`의 `REV-P10-001` 확정.
|
||
- **완료 증거:** item layout parameter에 end margin이 없는 RED → 기존 `recyclerItemLayoutParams(parent)` 재사용 GREEN → resource/source/팔로잉 회귀 PASS.
|
||
- **범위 밖:** 캡슐 크기·색상·내부 spacing 변경, RecyclerView padding 변경, 신규 ItemDecoration 추상화.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] **RED:** `HomeFollowingLiveAdapter.onCreateViewHolder()`가 item end margin을 적용하지 않는 현재 상태를 실패 테스트로 고정한다.
|
||
- [x] **GREEN:** creator/chat adapter와 같은 `recyclerItemLayoutParams(parent)`를 재사용해 XML 크기를 유지하면서 `spacing_12` end margin을 적용한다.
|
||
- [x] **GREEN 확인:** `HomeFollowingFragmentSourceTest`와 팔로잉 전체 회귀 테스트를 실행한다.
|
||
- [x] **회귀 검증:** resource merge, Kotlin compile, ktlint, `git diff --check`를 실행하고 결과를 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: `HomeFollowingFragmentSourceTest.following live adapter keeps horizontal item spacing`을 추가해 live adapter의 `recyclerItemLayoutParams(parent)` 재사용을 요구했고, 구현 전 helper 미사용으로 focused test가 실패했다.
|
||
- 2026-07-30 GREEN: `HomeFollowingLiveAdapter.onCreateViewHolder()`에서 inflate한 view에 `recyclerItemLayoutParams(parent)`를 적용해 기존 XML 크기와 `spacing_12` end margin을 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, `git diff --check` PASS.
|
||
|
||
#### Task 10.3: On Air 간격 검증의 테스트 정책 정합성 보완
|
||
|
||
**Goal 실행 `P10-R2`:** 확정 review 항목 `REV-P10-002`에 따라 UI margin을 source 문자열로 고정한 신규 테스트를 제거하고 가이드가 허용하는 대체 검증 기록으로 전환한다.
|
||
|
||
- **시작 조건:** `reviews/phase10-on-air-live-item-review.md`의 `REV-P10-002` 확정과 Task 10.2 production 수정 완료.
|
||
- **완료 증거:** 금지된 UI 표현 속성 source test만 제거 → adapter helper 사용 정적 대조 → resource merge·Kotlin compile·팔로잉 회귀 PASS.
|
||
- **범위 밖:** `HomeFollowingLiveAdapter`의 `recyclerItemLayoutParams(parent)` 적용 제거, item 크기·간격 변경, 기존 source test 일괄 정리.
|
||
- **TDD 예외 사유:** `docs/agent-guides/code-style.md`가 View margin 같은 UI 표현 속성 테스트를 금지하므로 실행 명령과 정적 대조를 대체 검증으로 사용한다.
|
||
- **대체 검증 방법:** adapter의 기존 helper 재사용을 `rg`로 확인하고 resource merge, Kotlin compile, 기존 팔로잉 로직 회귀를 실행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] 신규 `following live adapter keeps horizontal item spacing` source test만 제거하고 Task 10.2 production 코드는 유지한다.
|
||
- [x] `rg -n "recyclerItemLayoutParams\(parent\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`로 기존 helper 재사용을 확인한다.
|
||
- [x] `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `:app:mergeDebugResources`, `:app:compileDebugKotlin`을 실행한다.
|
||
- [x] `./gradlew --no-daemon :app:ktlintCheck`와 `git diff --check`를 실행하고 결과를 이 Task 아래에 누적한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: UI margin을 source 문자열로 고정하던 `following live adapter keeps horizontal item spacing` 테스트만 제거하고 `HomeFollowingLiveAdapter`의 `recyclerItemLayoutParams(parent)` 적용은 유지했다. `rg -n "recyclerItemLayoutParams\(parent\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`로 helper 재사용을 확인했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
|
||
#### Task 10.4: On Air 고정 폭 LayoutParams 보존
|
||
|
||
**Goal 실행 `P10-R3`:** 확정 review 항목 `REV-P10-003`에 따라 On Air item의 XML `263dp` 폭을 런타임에서도 유지하면서 `spacing_12` end margin을 적용한다.
|
||
|
||
- **시작 조건:** `reviews/phase10-on-air-live-item-review.md`의 `REV-P10-003` 확정.
|
||
- **완료 증거:** `RecyclerView.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)` 교체 경로 제거 → inflate된 width/height를 유지한 채 end margin만 적용 → resource merge·Kotlin compile·팔로잉 회귀 PASS → 연결 기기 준비 시 Task 5.3 수동 확인.
|
||
- **범위 밖:** 263dp × 100dp 디자인 변경, 공통 helper 전체 리팩터링, On Air 내부 필드/라우팅 변경.
|
||
- **TDD 예외 사유:** `docs/agent-guides/code-style.md`가 View width·margin 테스트 추가를 금지하므로 새 자동 UI 크기 테스트를 작성하지 않는다.
|
||
- **대체 검증 방법:** inflate 후 기존 `layoutParams` 보존 여부를 코드로 대조하고 resource merge·compile·기존 팔로잉 회귀 및 가능한 경우 실기기 수동 확인을 수행한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt`
|
||
- Verify: `app/src/main/res/layout/item_home_following_live.xml`
|
||
|
||
- [x] `HomeFollowingLiveAdapter.onCreateViewHolder()`가 inflate된 root의 기존 263dp × 100dp layout parameter를 보존하고 end margin만 추가하도록 최소 수정한다.
|
||
- [x] `HomeFollowingFragmentSourceTest`에 width·margin source assertion을 다시 추가하지 않는다.
|
||
- [x] 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint를 실행한다.
|
||
- [x] 연결 기기가 있으면 연속 On Air item의 폭과 간격을 수동 확인하고, 없으면 Task 5.3과 함께 사유를 기록한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `HomeFollowingLiveAdapter.onCreateViewHolder()`에서 `recyclerItemLayoutParams(parent)` 사용을 제거하고 inflate된 `RecyclerView.LayoutParams`의 263dp × 100dp 계약을 유지한 채 `spacing_12` end margin만 적용했다. width·margin source assertion은 다시 추가하지 않았다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. `adb devices`에 연결 기기가 없어 On Air 폭/간격 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.
|
||
|
||
---
|
||
|
||
### Phase 11: 최근 소식 COMMUNITY_POST 상세 이동
|
||
|
||
- [x] **Task 11.1: 문서와 RED 테스트로 커뮤니티 상세 이동 계약 고정**
|
||
- 수정:
|
||
- `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- `docs/20260625_메인_홈_팔로잉_탭/plan-task.md`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt`
|
||
- 작업:
|
||
- 최근 소식 `COMMUNITY_POST` item 터치 시 `CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)`를 사용한다는 계약을 문서화한다.
|
||
- `HomeMainFragment.onFollowingNewsClick()`이 `HomeFollowingNewsUiItem.Community`만 처리하고, `postId <= 0L`은 가드보다 먼저 무시하는지 source test로 고정한다.
|
||
- legacy `CreatorCommunityAllActivity` 또는 `Constants.EXTRA_COMMUNITY_POST_ID` 직접 extra 조립을 사용하지 않는지 검증한다.
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"`
|
||
- Expected RED: 구현 전 `onFollowingNewsClick()` no-op 때문에 새 source test가 실패한다.
|
||
|
||
- [x] **Task 11.2: COMMUNITY_POST 상세 이동 구현**
|
||
- 수정:
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- 작업:
|
||
- `onFollowingNewsClick()`에서 `HomeFollowingNewsUiItem.Community`가 아니면 즉시 return한다.
|
||
- `community.postId`가 0 이하이면 즉시 return한다.
|
||
- 유효한 `postId`는 `ensureMainV2NavigationAllowed` 안에서 `CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)`로 시작한다.
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"`
|
||
- Expected GREEN: source test PASS.
|
||
|
||
- [x] **Task 11.3: 최근 소식 이동 회귀 검증**
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test PASS.
|
||
- Run: `./gradlew --no-daemon :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
|
||
|
||
---
|
||
|
||
### Phase 12: 전체 empty 문구 중앙 표시
|
||
|
||
- [x] **Task 12.1: 문서와 source test로 empty 문구 계약 고정**
|
||
- 수정:
|
||
- `docs/20260625_메인_홈_팔로잉_탭/prd.md`
|
||
- `docs/20260625_메인_홈_팔로잉_탭/plan-task.md`
|
||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
- 작업:
|
||
- 기존 `screen_home_following_empty` string resource를 재사용한다는 계약을 문서화한다.
|
||
- `fragment_v2_main_home.xml`에 `tv_home_following_empty`가 있고 `@string/screen_home_following_empty`를 사용하는지 source test로 고정한다.
|
||
- empty `TextView`가 팔로잉 탭 전체 영역에서 가운데 배치되고 텍스트도 가운데 정렬되는지 source test로 고정한다.
|
||
- `HomeFollowingUiState.Empty`는 empty 문구를 표시하고, `Content`, `LoginRequired`, `Error`는 숨김 경로를 갖는지 source test로 고정한다.
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected RED: 구현 전 `tv_home_following_empty`와 empty visibility 분기 부재로 실패한다.
|
||
- 검증 기록:
|
||
- 2026-07-13: 기존 `screen_home_following_empty` string resource를 재사용해 전체 empty 문구를 표시하는 요구사항을 PRD와 계획 문서에 추가했다. `HomeFollowingFragmentSourceTest`에 `tv_home_following_empty` id, string 재사용, 중앙 정렬 속성, `HomeFollowingUiState.Empty` 전용 표시 분기를 고정하는 source test를 추가했다.
|
||
|
||
- [x] **Task 12.2: 다국어 string과 empty UI 구현**
|
||
- 수정:
|
||
- `app/src/main/res/values/strings.xml`
|
||
- `app/src/main/res/values-en/strings.xml`
|
||
- `app/src/main/res/values-ja/strings.xml`
|
||
- `app/src/main/res/layout/fragment_v2_main_home.xml`
|
||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
- 작업:
|
||
- 기존 `screen_home_following_empty` 값을 한국어/영어/일본어 문구로 갱신한다.
|
||
- 팔로잉 탭 content 영역에 `tv_home_following_empty`를 추가하고 중앙 배치/중앙 정렬한다.
|
||
- `HomeFollowingUiState.Empty`에서 empty 문구를 표시하고 섹션 adapter는 비운다.
|
||
- `HomeFollowingUiState.Content`, `LoginRequired`, `Error`에서는 empty 문구를 숨긴다.
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`
|
||
- Expected GREEN: source test PASS.
|
||
- 검증 기록:
|
||
- 2026-07-13: `values`, `values-en`, `values-ja`의 기존 `screen_home_following_empty` 값을 한국어/영어/일본어 2줄 문구로 갱신했다. `fragment_v2_main_home.xml`에 `tv_home_following_empty`를 추가하고 `@string/screen_home_following_empty`, `match_parent`, `gravity=center`, `textAlignment=center`, 기본 `gone`을 적용했다. `HomeMainFragment`에서는 `HomeFollowingUiState.Empty`만 empty 문구를 표시하고, `Content`, `LoginRequired`, `Error`는 숨기도록 분기했다.
|
||
|
||
- [x] **Task 12.3: empty 문구 회귀 검증 기록**
|
||
- 검증:
|
||
- Run: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`
|
||
- Expected: 팔로잉 관련 local unit/source test PASS.
|
||
- Run: `./gradlew --no-daemon :app:mergeDebugResources`
|
||
- Expected: string/layout resource merge PASS.
|
||
- Run: `./gradlew --no-daemon :app:compileDebugKotlin`
|
||
- Expected: Kotlin compile PASS.
|
||
- Run: `git diff --check`
|
||
- Expected: whitespace error 없음.
|
||
- 검증 기록:
|
||
- 2026-07-13: `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `git diff --check` 모두 PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
|
||
|
||
#### Task 12.4: empty 상태 UI 표현 속성 source test 정책 정합성 보완
|
||
|
||
**Goal 실행 `P12-R1`:** 확정 review 항목 `REV-P12-001`에 따라 empty 상태의 mapper/문구/분기 검증은 유지하고, 금지된 layout 크기·정렬·visibility 문자열 assertion을 제거한다.
|
||
|
||
- **시작 조건:** `reviews/phase12-empty-state-review.md`의 `REV-P12-001` 확정.
|
||
- **완료 증거:** UI 표현 속성 assertion 제거 및 남은 테스트명 한글화 → mapper Empty 조건과 locale 문구/상태 분기 검증 유지 → focused test·resource merge·Kotlin compile·ktlint PASS.
|
||
- **범위 밖:** empty 문구/중앙 배치 production 변경, 로그인 유도 UI, 다른 기존 source test 일괄 정리.
|
||
- **TDD 예외 사유:** production 동작이 아니라 테스트 정책 정합성을 수정하므로 production RED를 만들지 않는다.
|
||
- **대체 검증 방법:** 중앙 배치는 `fragment_v2_main_home.xml` 정적 대조와 Task 5.3 실기기 확인으로 검증하고, Empty 판정은 `HomeFollowingMapperTest`로 유지한다.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt`
|
||
|
||
- [x] empty 테스트에서 width·height·gravity·textAlignment·visibility와 직접 `View.VISIBLE/GONE` 문자열 assertion을 제거한다.
|
||
- [x] 3개 locale 문구 계약, `HomeFollowingUiState.Empty` 분기, 기존 mapper Empty 테스트는 유지한다.
|
||
- [x] 수정하는 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.
|
||
- [x] focused test, resource merge, Kotlin compile, ktlint를 실행하고 Task 5.3 수동 확인 가능 여부를 기록한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: empty source test명을 `팔로잉 empty 상태는 다국어 빈 문구와 상태 분기를 유지한다`로 한글화하고, width/height/gravity/textAlignment/visibility 및 직접 `View.VISIBLE/GONE` 문자열 assertion을 제거했다. 3개 locale 문구, empty string 재사용, `HomeFollowingUiState.Empty`/Error 상태 분기 검증은 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. `adb devices`에 연결 기기가 없어 중앙 배치 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.
|
||
|
||
---
|
||
|
||
### Phase 13: 최근 소식 CREATOR_RANKING·AUDIO_CONTENT 터치 이동
|
||
|
||
**Phase 결과:** 팔로잉 탭 최근 소식에서 크리에이터 순위 item은 해당 크리에이터 채널로, 오디오 콘텐츠 업로드 item은 해당 오디오 콘텐츠 상세로 이동한다.
|
||
|
||
**선행조건:** Phase 11의 기존 `COMMUNITY_POST` 상세 이동과 공통 Creator/Content Action 경로가 유지되어야 한다.
|
||
|
||
**Phase 완료 조건:** `CREATOR_RANKING`, `AUDIO_CONTENT`, 기존 `COMMUNITY_POST` 이동 source test와 팔로잉 회귀 테스트, Kotlin 컴파일이 통과한다.
|
||
|
||
#### Task 13.1 최근 소식 이동 RED 테스트 추가
|
||
|
||
**Goal 실행 `P13-T1`:** 현재 실행되지 않는 `CREATOR_RANKING`과 `AUDIO_CONTENT` 터치 이동을 실패 source test로 재현한다.
|
||
|
||
- **시작 조건:** PRD의 `2026-07-30 CREATOR_RANKING·AUDIO_CONTENT 상세 이동 Requirements` 확정.
|
||
- **완료 증거:** 구현 전 focused test가 새 이동 호출 부재로 실패한다.
|
||
- **범위 밖:** `CONTENT_RANKING`, `PHOTO_CONTENT`, On Air, 스케줄 터치 이동.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt`
|
||
|
||
- [x] **RED:** `onFollowingNewsClick()`이 `CREATOR_RANKING`을 `CreatorActionCommand.Profile`로, `AUDIO_CONTENT`를 `ContentActionCommand.AudioDetail`로 전달하는지 검증하는 source test를 추가한다.
|
||
- [x] **RED 확인:** `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"`를 실행해 두 공통 액션 호출 부재로 인한 assertion 실패를 확인한다.
|
||
- [x] **GREEN:** Task 13.2의 최소 구현으로 RED를 통과시킨다.
|
||
- [x] **GREEN 확인:** 같은 focused test를 다시 실행해 성공을 확인한다.
|
||
- [x] **REFACTOR:** 새 abstraction 없이 기존 공통 액션을 재사용하고 focused test를 다시 실행한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30 RED: focused test를 실행해 11개 중 `COMMUNITY_POST` 분기 구조, `CREATOR_RANKING` 채널 이동, `AUDIO_CONTENT` 상세 이동 3개가 assertion 실패하는 것을 확인했다. 현재 production이 `HomeFollowingNewsUiItem.Community` 이외의 item을 즉시 반환해 요청 동작이 없는 것이 실패 원인이다.
|
||
- 2026-07-30 GREEN: 동일 focused test를 다시 실행해 11개 모두 PASS했다. 새 abstraction이나 직접 `Intent` 조립 없이 기존 Creator/Content/Community Action 호출을 검증했다.
|
||
|
||
#### Task 13.2 CREATOR_RANKING·AUDIO_CONTENT 이동 구현
|
||
|
||
**Goal 실행 `P13-T2`:** 요청된 두 최근 소식 타입만 기존 공통 액션 경로로 이동시킨다.
|
||
|
||
- **시작 조건:** `P13-T1` RED 확인.
|
||
- **완료 증거:** focused test와 Kotlin 컴파일 통과.
|
||
- **범위 밖:** API/DTO/mapper/UI 변경, 직접 `Intent` 조립, 다른 최근 소식 타입의 신규 이동.
|
||
|
||
**Files:**
|
||
|
||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt`
|
||
|
||
- [x] **RED:** Task 13.1의 실패 source test를 기준으로 한다.
|
||
- [x] **RED 확인:** Task 13.1 실행 결과를 확인한다.
|
||
- [x] **GREEN:** `onFollowingNewsClick()`에서 `CREATOR_RANKING`은 `CreatorActionCommand.Profile(item.targetId)`, `AUDIO_CONTENT`는 `ContentActionCommand.AudioDetail(item.contentId)`를 호출하고 기존 `COMMUNITY_POST` 분기를 유지한다.
|
||
- [x] **GREEN 확인:** focused test와 `./gradlew --no-daemon :app:compileDebugKotlin`을 실행해 성공을 확인한다.
|
||
- [x] **REFACTOR:** `CONTENT_RANKING`과 `PHOTO_CONTENT`는 no-op으로 유지하고 이번 변경이 만든 중복만 점검한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: `HomeMainFragment.onFollowingNewsClick()`을 sealed UI item 분기로 변경했다. `CREATOR_RANKING`만 `CreatorActionCommand.Profile(item.targetId)`, `AUDIO_CONTENT`만 `ContentActionCommand.AudioDetail(item.contentId)`로 전달하며 기존 `COMMUNITY_POST`의 `postId` 검증과 상세 이동을 유지했다. focused test와 `:app:compileDebugKotlin`이 PASS했다.
|
||
|
||
#### Task 13.3 최근 소식 터치 이동 회귀 검증
|
||
|
||
**Goal 실행 `P13-GATE`:** 요청된 이동과 기존 팔로잉 동작에 회귀가 없는지 판정한다.
|
||
|
||
- **시작 조건:** `P13-T1`, `P13-T2` 완료.
|
||
- **완료 증거:** 아래 자동 검증 결과와 수동 검증 가능 여부를 기록한다.
|
||
- **범위 밖:** 연결 기기 없는 환경에서 수동 검증을 성공으로 추정하는 행위.
|
||
|
||
- [x] `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"` PASS.
|
||
- [x] `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"` PASS.
|
||
- [x] `./gradlew --no-daemon :app:compileDebugKotlin` PASS.
|
||
- [x] `./gradlew --no-daemon :app:ktlintCheck` PASS 또는 이번 변경과 무관한 기존 위반만 확인.
|
||
- [x] `git diff --check` PASS.
|
||
- [x] 연결 기기가 있으면 `CREATOR_RANKING`과 `AUDIO_CONTENT` item 터치 목적지를 수동 확인하고, 없으면 미실행 사유를 기록한다.
|
||
|
||
검증 기록:
|
||
|
||
- 2026-07-30: focused test, 팔로잉 전체 회귀 테스트, Kotlin 컴파일, ktlint, `git diff --check`가 모두 PASS했다. ktlint에는 기존 `.editorconfig`의 `disabled_rules` deprecation 경고만 출력됐다. `adb devices` 결과 연결된 기기가 없어 실제 item 터치 수동 검증은 실행하지 못했다.
|
||
|
||
---
|
||
|
||
## Verification Log
|
||
- 2026-07-31 이번 Phase 1~13 리뷰 재검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 대조했다. 새 확정 발견 사항은 없고 기존 확정 항목은 모두 각 회귀 수정 Task에서 수정 완료 상태다. Phase 9 보고서 범위를 Task 9.7까지 확장해 production/test `FeedAdapter|FeedImageViews` 호출자 0건과 현재 Feed view/model 직접 재사용을 기록했다. `./gradlew --no-daemon :app:testDebugUnitTest ... :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 16 suites·120 tests, failures/errors/skipped 0, 46 actionable tasks), `./gradlew --no-daemon tasks --all`, staged/unstaged `git diff --check` PASS. `adb devices`에서 기기 `2cec640c34017ece` 연결은 확인했지만 테스트 계정·API 데이터 기반 팔로잉 화면 조작과 Figma 대조는 수행하지 않아 `REV-P5-001` / Task 5.3 보류를 유지한다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다.
|
||
- 2026-07-31 Phase 1~13 최종 리뷰 검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 `REV-P4-004`(공용 source test의 영문 문장형 테스트명 11개)와 `REV-P9-002`(API가 제공하지 않는 creator ID 자리에 content/post ID를 합성)이며, 코드 수정 전에 Phase 4 Task 4.9 / `P4-R4`, Phase 9 Task 9.6 / `P9-R2`로 전환했다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다. `./gradlew --no-daemon :app:testDebugUnitTest ... :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 20 suites·306 tests, failures/errors/skipped 0, 46 actionable tasks), `./gradlew --no-daemon tasks --all`, staged/unstaged `git diff --check`도 PASS했다. `adb devices`는 정상 실행됐으나 연결 기기가 없어 `REV-P5-001` / Task 5.3 실기기 수동 확인 보류를 유지한다.
|
||
- 2026-07-31 리뷰 보완 Task 2.8, 6.8 완료 검증: `REV-P2-002`에 따라 미사용 `HomeFollowingNewsUiItem.Content.labelResId`와 전용 `FollowingNewsType.toLabelResId()`를 제거하고, 실제 표시 경로인 `FeedContentCategory.Photo` label 회귀를 `HomeFollowingNewsAdapterTest`에 유지했다. `REV-P6-004`에 따라 creator·최근 대화 source test의 UI 표현 속성 문자열 assertion만 제거하고 구조·필수 field·Direct badge·상대 시간 formatter·click 계약 검증은 유지했다. focused test 3개, 팔로잉 전체 회귀, `./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다. `adb devices`에는 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
|
||
- 2026-07-31 Phase 1~13 리뷰 재점검: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 `REV-P2-002`(최근 소식 `Content.labelResId`와 전용 mapper가 production 표시에서 미사용)와 `REV-P6-004`(creator·최근 대화 UI 표현 속성을 검증하는 source test가 현재 코드 스타일 정책과 불일치)이며, 구현 전에 Phase 2 Task 2.8 / `P2-R2`, Phase 6 Task 6.8 / `P6-R4`로 전환했다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다. `./gradlew --no-daemon :app:testDebugUnitTest --tests 'kr.co.vividnext.sodalive.v2.main.home.*Following*' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest' --tests 'kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest' --tests 'kr.co.vividnext.sodalive.v2.live.action.*' --tests 'kr.co.vividnext.sodalive.v2.content.action.*' --tests 'kr.co.vividnext.sodalive.v2.community.action.*' :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 15 suites·113 tests, failures/errors 0, 46 actionable tasks). `./gradlew --no-daemon tasks --all`, staged/unstaged `git diff --check`도 PASS했다. `adb devices`에는 연결 기기가 없어 Phase 5 Task 5.3 실기기 수동 확인 보류를 유지한다.
|
||
- 2026-07-31 리뷰 보완 Task 2.7 완료 검증: `REV-P2-001`에 따라 미사용 팔로잉 string resource `screen_home_following_creators_title`, `screen_home_following_on_air_title`, `screen_home_following_error`를 3개 locale에서 제거했다. 선언 외 참조 0건을 `rg`로 확인했고 사용 중인 팔로잉 문자열은 유지했다. `./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin`, `./gradlew --no-daemon --rerun-tasks :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. 첫 팔로잉 전체 회귀는 `R.string` id 변경 증분 캐시로 mapper test 2건이 실패했으나 `--rerun-tasks` 재빌드 후 focused와 전체 회귀가 PASS했다. `adb devices`에 연결된 기기가 없어 Phase 5 Task 5.3 실기기 수동 확인은 계속 보류한다.
|
||
- 2026-07-30 Phase 1~13 최종 리뷰 재점검: 현재 working tree의 PRD·계획·production/test 코드·Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 `REV-P2-001`(후속 UI·오류 처리 변경 후 3개 locale에 미사용 string resource 3개가 남음) 1건이며 Phase 2 Task 2.7 / `P2-R1`로 전환했다. 이번 리뷰에서 production/test/resource 코드는 수정하지 않았다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.live.action.*" --tests "kr.co.vividnext.sodalive.v2.content.action.*" --tests "kr.co.vividnext.sodalive.v2.creator.action.*" --tests "kr.co.vividnext.sodalive.v2.community.action.*" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 119 tests, failures/errors 0, 46 actionable tasks), `./gradlew --no-daemon tasks --all`, staged/unstaged `git diff --check` PASS. `adb devices`는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증 보류를 유지한다.
|
||
- 2026-07-30 리뷰 보완 Task 4.8, 9.5 완료 검증: blank profile image와 nullable 최근 소식 image 바인딩 시 재사용된 `ImageView`의 이전 Coil 요청을 `dispose()`로 해제하도록 보완했다. 각 focused test는 구현 전 `disposable.isDisposed` assertion 실패로 RED를 확인했고, 최소 구현 후 GREEN으로 전환했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest"`, `./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-07-30 Phase 1~13 리뷰 최종 재검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 대조했다. 신규 확정 항목은 `REV-P4-003`(blank profile image binding의 이전 Coil 요청 해제 누락)과 `REV-P9-001`(nullable 최근 소식 image binding의 이전 Coil 요청 해제 누락)이며, 구현 전에 Phase 4 Task 4.8 / `P4-R3`, Phase 9 Task 9.5 / `P9-R1`로 전환했다. 이미 완료된 `REV-P6-003` / Task 6.7은 Phase 6 보고서의 상태와 수정 검증 기록을 현재 코드에 맞게 정정했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.live.action.*" --tests "kr.co.vividnext.sodalive.v2.content.action.*" --tests "kr.co.vividnext.sodalive.v2.community.action.*" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 111 tests, failures/errors 0, 46 actionable tasks), `./gradlew --no-daemon tasks --all` PASS, staged/unstaged `git diff --check` PASS. `adb devices`는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증 보류를 유지한다. 이번 리뷰에서는 production/test 코드를 수정하지 않았다.
|
||
- 2026-07-30 리뷰 보완 Task 6.7, 7.7, 8.4, 10.4, 12.4 완료 검증: 최근 대화와 On Air adapter는 inflate된 root의 기존 `RecyclerView.LayoutParams` width/height를 보존하고 `spacing_12` end margin만 적용하도록 수정했다. 금지된 UI 표현 속성 source assertion은 Feed content, 팔로잉 크리에이터 전체 item, empty 상태 테스트에서 제거하고, 동작/문구/route 계약 검증은 유지했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다. `adb devices`에 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
|
||
- 2026-07-30 Phase 1~13 리뷰 최종 검증: `./gradlew --no-daemon :app:testDebugUnitTest --tests 'kr.co.vividnext.sodalive.v2.main.home.*Following*' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest' --tests 'kr.co.vividnext.sodalive.v2.main.home.FeedViewTest' --tests 'kr.co.vividnext.sodalive.v2.live.action.*' --tests 'kr.co.vividnext.sodalive.v2.content.action.*' --tests 'kr.co.vividnext.sodalive.v2.community.action.*' :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck` PASS(`BUILD SUCCESSFUL`, 46 actionable tasks), `git diff --check` PASS. `adb devices`는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증은 미실행 상태를 유지한다. 자동 검증 PASS는 신규 확정 항목 `REV-P6-003`, `REV-P7-003`, `REV-P8-001`, `REV-P10-003`, `REV-P12-001`의 정적 리뷰 결과를 해소하지 않으며, 각 신규 Task에서 후속 조치한다.
|
||
- 2026-07-30 Phase별 추가 재점검: 기존 Phase 1~13 리뷰 보고서, 현재 production/test 코드, `docs/agent-guides/code-style.md`를 다시 대조했다. 신규 확정 항목은 `REV-P6-003`(최근 대화 `284dp` 폭을 adapter가 `WRAP_CONTENT`로 교체), `REV-P7-003`(content feed UI 크기·padding·margin source test 정책 위반), `REV-P8-001`(`전체` item UI 표현 속성 source assertion), `REV-P10-003`(On Air `263dp` 폭을 간격 helper가 `WRAP_CONTENT`로 교체), `REV-P12-001`(empty 크기·정렬·visibility source assertion)이다. 구현 전에 해당 Phase에 Task 6.7, 7.7, 8.4, 10.4, 12.4를 추가했으며, 이번 재점검에서는 production/test 코드를 수정하지 않았다. Phase 5 Task 5.3의 실기기 수동 확인은 기존 보류 상태를 유지한다.
|
||
- 2026-07-30 리뷰 보완 Task 6.6, 7.6, 10.3 완료 검증: 상단 파일 구조의 obsolete 최근 소식 layout 항목을 Feed layout 재사용/삭제 이력 기준으로 정리하고, `HomeFollowingNewsAdapterTest` 신규 테스트명 2개를 한글화했으며, `build-test-style.md`에 클래스 단위 실행 예시를 추가하고, On Air 간격 source test 정책 위반 항목을 제거했다. `rg` 대체 검증, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다. 리뷰어 재확인 결과 blocking finding 없음.
|
||
- 2026-07-30 Phase별 리뷰 산출물 재점검: 기존 Phase 1~13 보고서와 현재 working tree를 다시 대조했다. 기존 통합 리뷰 기록의 “이번 리뷰에서는 production 코드를 수정하지 않았다”는 문구와 달리, 같은 리뷰 후속 Task 4.6·4.7·6.5·7.5·10.2에서 production 코드가 수정된 상태임을 정정한다. 이번 재점검 자체에서는 production 코드를 변경하지 않았으며, `REV-P6-002`(삭제된 layout의 상단 파일 구조 잔존), `REV-P7-002`(신규 테스트명과 단일 실행 가이드 미준수), `REV-P10-002`(UI margin source test 정책 위반)를 추가 확정해 Task 6.6·7.6·10.3으로 전환했다. `*Following*` 39개, `HomeMainFragmentLoginGuardSourceTest` 13개, Feed·Live·Content·Community Action 회귀, resource merge, Kotlin compile, ktlint, `./gradlew --no-daemon tasks --all`, staged/unstaged diff whitespace 검증이 PASS했다. `adb devices`에는 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
|
||
- 2026-07-30 리뷰 보완 Task 3.4, 4.6, 4.7, 6.5, 7.3, 7.5, 10.2 완료 검증: RED 단계에서 focused source/unit test 실패를 확인한 뒤 최소 구현으로 GREEN 전환했다. `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"`, 팔로잉 전체 회귀 + FeedView + `v2.live.action.*`/`v2.content.action.*`/`v2.community.action.*`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS. 최종 리뷰 게이트에서 Critical/Important finding은 없었고, ranking highlight 중복 문자열 Minor를 추가 RED/GREEN으로 보완했다. 최초 병렬 Gradle 검증은 `mergeDebugResources` incremental missing file로 실패했으나 순차 재실행에서 PASS했다. `adb devices`에 연결 기기가 없어 Task 5.3 실기기 수동 확인은 계속 blocked다.
|
||
- 2026-07-30 Phase 1~13 통합 리뷰: `docs/sample/sample-review.md`와 review guide에 따라 Phase별 리뷰 보고서 13개를 `reviews/` 아래의 `phase1-...-review.md`부터 `phase13-...-review.md`까지 작성했다. 확정 항목은 `REV-P3-001`(Loading 상태 발행 순서 테스트 누락), `REV-P4-001`(On Air·스케줄 item 이동 no-op), `REV-P4-002`(LoginRequired pull refresh 종료 누락), `REV-P6-001`(Feed 전환 전 obsolete layout), `REV-P7-001`(영어·일본어 ranking 순위 단위의 한국어 고정), `REV-P10-001`(On Air item 간격 누락)이며, 코드 수정 전에 해당 Phase에 Task 3.4, 4.6, 4.7, 6.5, 7.5, 10.2를 추가했다. Phase 5 실기기 수동 확인은 `adb devices`에 연결 기기가 없어 기존 Task 5.3 보류를 유지했다. 검증으로 Phase 2~3 focused test, Phase 4·6·7·8·9·11·12·13 source/feed focused test, 팔로잉 전체 회귀 36개, `:app:mergeDebugResources`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`를 실행했고 모두 PASS했다. Gradle deprecation과 Agora namespace는 기존 경고이며, 이번 리뷰에서는 production 코드를 수정하지 않았다.
|
||
- 2026-07-30 Phase 13 최근 소식 터치 이동 검증: `onFollowingNewsClick()`이 `CREATOR_RANKING`을 기존 `CreatorActionCommand.Profile`로 전달해 해당 크리에이터 채널로 이동하고, `AUDIO_CONTENT`를 기존 `ContentActionCommand.AudioDetail`로 전달해 해당 오디오 콘텐츠 상세로 이동하도록 보완했다. 기존 `COMMUNITY_POST` 상세 이동은 유지하고 `CONTENT_RANKING`, `PHOTO_CONTENT`는 no-op으로 유지했다. RED에서 focused test 11개 중 요청 동작 관련 3개 실패를 확인했고, GREEN에서 11개 모두 PASS했다. 팔로잉 전체 회귀 테스트, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`가 PASS했다. `adb devices`에 연결된 기기가 없어 실제 터치 수동 검증은 미실행했다.
|
||
- 2026-07-13 Phase 12 전체 empty 문구 중앙 표시 검증: 팔로잉 탭 전체 empty 상태에서 기존 `screen_home_following_empty` 리소스를 재사용해 `아직 팔로잉 소식이 없어요.\n관심 있는 크리에이터를 팔로우해 보세요.` 문구를 중앙 표시하도록 구현했다. 영어는 `No following updates yet.\nFollow creators you are interested in.`, 일본어는 `フォロー中のお知らせはまだありません。\n気になるクリエイターをフォローしてみましょう。`로 갱신했다. `HomeFollowingUiState.Empty`에서만 empty 문구가 보이고 `Content`, `LoginRequired`, `Error`에서는 숨김 처리된다. 검증으로 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `git diff --check` 모두 PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
|
||
- 2026-07-12 Phase 11 검증: 최근 소식 `COMMUNITY_POST` 상세 이동 계약을 source test로 추가하고 `HomeMainFragment.onFollowingNewsClick()`에서 `HomeFollowingNewsUiItem.Community`만 `CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)`로 이동하도록 구현했다. RED 시도: 구현 전 targeted test `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"`는 120초/300초 timeout으로 assertion 결과까지 도달하지 못했지만, 당시 production은 no-op 상태였고 새 source test는 `ensureMainV2NavigationAllowed`와 상세 이동 호출 문자열을 요구하도록 추가되어 실패 조건을 고정했다. GREEN: 동일 targeted test PASS. 회귀 검증 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:compileDebugKotlin`, `git diff --check` PASS. `./gradlew --no-daemon :app:ktlintCheck`는 이번 변경과 무관한 기존 legacy/source 위반(`explorer/profile/creator_community` package underscore, `LiveRoomActivity.kt` formatting, `NicknameUpdateViewModel.kt` indentation 등)으로 FAIL. 실기기 수동 QA는 `adb devices` 결과 연결된 device가 없어 blocked.
|
||
- 2026-06-25 Phase 1-3 구현 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
- 2026-06-25 Phase 1-3 코드 리뷰 및 재검증: blocking finding 없음. `./gradlew :app:mergeDebugResources`는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했고, 나머지 검증 명령도 PASS.
|
||
- 2026-06-25 Phase 4 코드 리뷰 및 검증: Figma `24:5682` 기준 UI 필드 바인딩 누락을 보완했고, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS.
|
||
- 2026-06-25 Phase 4 재코드 리뷰 및 검증: blocking finding 없음. Figma `24:5682` 디자인 컨텍스트/스크린샷과 현재 Phase 4 변경을 정적 대조했고, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS. `mergeDebugResources`는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했다.
|
||
- 2026-06-25 Phase 5 진행 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS. `./gradlew :app:installDebug`는 실행 중 Android 디바이스 연결 해제로 실패했고, 재확인 시 연결된 디바이스가 없어 실제 화면 수동 확인은 blocked 상태다.
|
||
- 2026-06-26 Phase 6 후속 디자인 수정 검증: Figma `24:5682` 기준 팔로잉 크리에이터 header 제거, 최근 대화 horizontal box list, Direct badge/상대시간 표시를 RED 테스트로 고정한 뒤 GREEN 확인했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`, `./gradlew :app:installDebug`, `adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1` 모두 PASS.
|
||
- 2026-06-26 Phase 6 리뷰 지적 후 재검증: 최근 대화 item 폭 override를 제거하고 source test를 보강했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest.following recent chat item matches figma box fields"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`, `./gradlew :app:installDebug` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-26 최근 대화 한 줄 제한 후속 검증: `item_home_following_chat.xml`의 `tv_home_following_chat_message`를 `maxLines="1"`로 변경하고 기존 `ellipsize="end"`를 유지했다. RED/GREEN source test, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` 모두 PASS.
|
||
- 2026-06-26 최근 소식 Feed 위젯 재사용 후속 검증: 최근 소식 chevron 제거와 `FeedCommunityView`/`FeedRankView`/`FeedContentView` 재사용을 source test로 고정했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 최근 소식 ranking/content 후속 검증: Figma `24:5717`, `1229:27212` 기준으로 ranking 문장형 message와 content feed `88dp` 이미지/간격을 RED 테스트로 고정한 뒤 GREEN 확인했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. `./gradlew :app:ktlintCheck`는 이번 변경과 무관한 `CreatorRankingTopCardView.kt:11` 기존 unused import로 FAIL.
|
||
- 2026-06-30 content feed clipping 위치 수정 검증: `docs/agent-guides/code-style.md`의 XML `android:clipToOutline` 금지 규칙에 맞춰 `view_feed_content.xml` 속성을 제거하고 `FeedContentView` Kotlin 코드에서 root `clipToOutline`/`outlineProvider`를 설정했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS.
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 후속 검증: 팔로잉 크리에이터 RecyclerView 마지막에 `전체` item을 추가하고 `FollowingCreatorActivity` 이동을 연결했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"` RED/GREEN, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` PASS. `mergeDebugResources`는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했고, `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 팔로잉 크리에이터 `전체` 버튼 높이 보정 후 재검증: `전체` item root는 `wrap_content` 높이로 두고 invisible nickname spacer로 creator item 전체 높이를 맞췄다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check` PASS. `mergeDebugResources`는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했고, `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 Phase 9 최근 소식 Response nested payload 계약 반영 검증: `FollowingNewsResponse`를 `newsId`, `type`, `visibleFromAtUtc`, type별 nested payload만 갖는 구조로 갱신하고 mapper/adapter를 matching payload 기준으로 변경했다. RED: 새 nested DTO/UI 필드 미구현으로 `compileDebugUnitTestKotlin` 실패. GREEN: focused test PASS. 통합 검증 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` 모두 PASS. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐다.
|
||
- 2026-06-30 Phase 9 코드 리뷰 후 nullable 계약 보완 검증: PRD의 `contentImageUrl: String?` 계약과 구현의 non-null DTO/UI model 불일치를 수정했다. RED: null image fixture 추가 시 `compileDebugUnitTestKotlin` type mismatch 실패. GREEN: `contentImageUrl` nullable DTO/UI model과 adapter `.orEmpty()` fallback 적용 후 focused mapper test PASS. 통합 재검증 `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:compileDebugKotlin`, `./gradlew --no-daemon :app:ktlintCheck`, `git diff --check` PASS.
|
||
- 2026-07-03 Phase 10 On Air 라이브 item Figma 재구현 검증: Figma `24:5696` 기준으로 `item_home_following_live.xml`을 캡슐형 카드로 재구현하고 `bg_home_following_live_capsule.xml`을 추가했다. `./gradlew --no-daemon :app:mergeDebugResources`, `./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"`, `git diff --check` PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
|
||
- 2026-07-03 Phase 10 후속 정리: `item_home_following_live.xml`에서 불필요한 `iv_home_following_live_thumbnail`을 제거하고, `HomeFollowingLiveAdapter`의 해당 id 조회와 `loadUrl` 호출을 삭제했다.
|