854 lines
75 KiB
Markdown
854 lines
75 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이다.
|
|
- Create: `app/src/main/res/layout/item_home_following_news_rank.xml`
|
|
- ranking news item이다.
|
|
- Create: `app/src/main/res/layout/item_home_following_news_content.xml`
|
|
- audio/photo content news item이다.
|
|
- 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/FeedAdapter.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 전달이 검증됐다.
|
|
|
|
---
|
|
|
|
### 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.
|
|
|
|
---
|
|
|
|
### 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은 발견하지 못했다.
|
|
|
|
---
|
|
|
|
### 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 상태로 남긴다.
|
|
|
|
---
|
|
|
|
### 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만 출력됐다.
|
|
|
|
---
|
|
|
|
### 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 간격 기준을 갖도록 검증됐다.
|
|
|
|
- [ ] **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 파일은 수정하지 않았다.
|
|
|
|
- [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.
|
|
|
|
---
|
|
|
|
### 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만 출력됐다.
|
|
|
|
---
|
|
|
|
### 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만 출력됐다.
|
|
|
|
---
|
|
|
|
### 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을 제거했다.
|
|
|
|
---
|
|
|
|
### 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만 출력됐다.
|
|
|
|
---
|
|
|
|
## Verification Log
|
|
- 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` 호출을 삭제했다.
|