369 lines
30 KiB
Markdown
369 lines
30 KiB
Markdown
# 크리에이터 랭킹 페이지 Plan / Task
|
|
|
|
> **For agentic workers:** 구현 시 `superpowers:subagent-driven-development` 또는 `superpowers:executing-plans`를 사용해 task 단위로 진행한다. 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다.
|
|
|
|
**Goal:** `HomeMainFragment`의 Text Tab bar에서 `랭킹` 선택 시 `GET /api/v2/home/rankings/creators` 응답을 기존 `creatorranking` 위젯으로 표시한다.
|
|
|
|
**Architecture:** 신규 홈 크리에이터 랭킹 API, Repository, ViewModel, DTO/UI model/mapper는 기존 홈 추천 패턴과 같은 `kr.co.vividnext.sodalive.v2.main.home` 하위에 둔다. 화면은 `HomeMainFragment`와 `fragment_v2_main_home.xml`을 최소 확장하고, 기존 `kr.co.vividnext.sodalive.v2.widget.creatorranking` 위젯은 `showRankChange=false` 숨김 옵션만 필요한 만큼 확장한다.
|
|
|
|
**Tech Stack:** Kotlin, Android XML Views, ViewBinding, RecyclerView, RxJava3, Retrofit, Gson, Koin, Coil, Robolectric/local unit test.
|
|
|
|
---
|
|
|
|
## 전제와 성공 기준
|
|
- PRD: `docs/20260608_크리에이터_랭킹_페이지/prd.md`
|
|
- Figma: `24:5654`
|
|
- Capsule Tab bar(`주간 인기`, `지금 뜨는 중`, `남성 인기`, `여성 인기`)는 배치하지 않는다.
|
|
- `rankChange`는 `null`/`0` -> `Stay`, 양수 -> `Increase`, 음수 -> `Decrease`로 매핑한다.
|
|
- `isNew=true`는 `rankChange`보다 우선해 `New`로 매핑한다.
|
|
- `showRankChange=false`이면 모든 rank-num 영역을 완전히 숨긴다.
|
|
- API 응답은 서버에서 `rank` 오름차순으로 내려오지만, 클라이언트에서도 한 번 더 `rank` 기준 오름차순 정렬한다.
|
|
- `creatorId=0`은 차단 관계로 보고 `isBlocked=true`, 클릭 불가로 처리한다.
|
|
- `creatorId>0` item만 `UserProfileActivity`로 이동하며, 별도 analytics/logging은 추가하지 않는다.
|
|
- 구현 완료 후 최소 다음 명령을 실행한다.
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*"`
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- `./gradlew :app:ktlintCheck`
|
|
|
|
---
|
|
|
|
## 파일 구조
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingModels.kt`
|
|
- `HomeCreatorRankingResponse`, `HomeCreatorRankingItemResponse` DTO를 정의한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingApi.kt`
|
|
- `GET /api/v2/home/rankings/creators` Retrofit endpoint를 정의한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingRepository.kt`
|
|
- API 호출을 감싸고 기존 repository 패턴과 동일하게 token을 전달한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeCreatorRankingMappers.kt`
|
|
- API 응답을 `CreatorRankingItem` 목록으로 변환한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeCreatorRankingUiState.kt`
|
|
- `Loading`, `Content`, `Empty`, `Error` 상태를 정의한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingViewModel.kt`
|
|
- 랭킹 API 호출, loading, toast, state를 관리한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
|
- 신규 API, Repository, ViewModel을 Koin에 등록한다.
|
|
- 수정: `app/src/main/res/layout/fragment_v2_main_home.xml`
|
|
- `TextTabBarView` 아래에 랭킹 전용 `RecyclerView`를 추가한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/HomeMainFragment.kt`
|
|
- `랭킹` 탭 전환, 랭킹 adapter, ViewModel observe, 프로필 이동을 연결한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingItem.kt`
|
|
- `showRankChange` 표시 계약을 추가한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingLargeCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingCompactCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingHorizontalCardView.kt`
|
|
- `showRankChange=false`일 때 rank-num 영역을 `GONE` 처리한다.
|
|
- 테스트 수정/생성:
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingItemTest.kt`
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingAdapterLayoutTest.kt`
|
|
- 생성: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingMapperTest.kt`
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
|
|
|
---
|
|
|
|
### Phase 1: 기존 구조 확인과 작업 경계 고정
|
|
|
|
- [x] **Task 1.1: 기존 홈/랭킹 위젯/DI 구조 확인**
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/HomeMainFragment.kt`
|
|
- 확인: `app/src/main/res/layout/fragment_v2_main_home.xml`
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeRecommendationApi.kt`
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt`
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingAdapter.kt`
|
|
- 확인: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingItem.kt`
|
|
- 검증: 기존 추천 content는 `nsv_home_recommendation_content` 아래에 있고, 랭킹 content는 별도 container가 필요함을 확인한다.
|
|
|
|
- [x] **Task 1.2: 구현 제외 범위 재확인**
|
|
- 확인: `docs/20260608_크리에이터_랭킹_페이지/prd.md`
|
|
- 제외:
|
|
- Capsule Tab bar
|
|
- 팔로잉 탭 content
|
|
- analytics/logging
|
|
- ViewPager2/swipe 전환
|
|
- 검증: 계획 문서의 모든 phase가 위 제외 범위를 침범하지 않는지 확인한다.
|
|
|
|
---
|
|
|
|
### Phase 2: `creatorranking` 위젯 rank-num 숨김 계약 확장
|
|
|
|
- [x] **Task 2.1: `CreatorRankingItem` 표시 계약 테스트 추가**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingItemTest.kt`
|
|
- 추가 테스트:
|
|
- 기본 `showRankChange`는 `true`
|
|
- `showRankChange=false`인 item은 rank change 표시 대상이 아님
|
|
- `creatorId=0`, `isBlocked=true` item은 `isTouchable=false`
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingItemTest"`
|
|
- 기대 결과: `showRankChange` 속성 미구현으로 RED 실패.
|
|
|
|
- [x] **Task 2.2: `CreatorRankingItem`에 `showRankChange` 추가**
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingItem.kt`
|
|
- 구현 내용:
|
|
- `val showRankChange: Boolean = true`를 기본값 있는 마지막 파라미터로 추가한다.
|
|
- 기존 테스트/호출부가 깨지지 않도록 기본값을 유지한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingItemTest"`
|
|
- 기대 결과: PASS.
|
|
|
|
- [x] **Task 2.3: rank-num 숨김 view 테스트 추가**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingAdapterLayoutTest.kt`
|
|
- 추가 테스트:
|
|
- `Large`, `Compact`, `Horizontal` card에 `showRankChange=false` item을 bind하면 `ll_creator_ranking_delta`가 `GONE`
|
|
- `showRankChange=true` item을 bind하면 기존처럼 `ll_creator_ranking_delta`가 `VISIBLE`
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`
|
|
- 기대 결과: view bind 미구현으로 RED 실패.
|
|
|
|
- [x] **Task 2.4: rank-num 숨김 구현**
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingLargeCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingCompactCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingHorizontalCardView.kt`
|
|
- 구현 내용:
|
|
- `bindDelta(item)` 시작부에서 `item.showRankChange == false`이면 delta container를 `View.GONE`으로 설정하고 return한다.
|
|
- `item.showRankChange == true`이면 delta container를 `View.VISIBLE`로 복구한 뒤 기존 presentation 적용을 유지한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`
|
|
- 기대 결과: PASS.
|
|
|
|
---
|
|
|
|
### Phase 3: 홈 크리에이터 랭킹 API/DTO/mapper 작성
|
|
|
|
- [x] **Task 3.1: mapper RED 테스트 작성**
|
|
- 생성: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingMapperTest.kt`
|
|
- 테스트 케이스:
|
|
- 응답 item을 `rank` 오름차순으로 재정렬한다.
|
|
- `isNew=true`는 `RankingChangeType.New`, amount `0`으로 매핑한다.
|
|
- `rankChange=null` 또는 `0`은 `RankingChangeType.Stay`, amount `0`으로 매핑한다.
|
|
- `rankChange=5`는 `RankingChangeType.Increase`, amount `5`로 매핑한다.
|
|
- `rankChange=-3`은 `RankingChangeType.Decrease`, amount `3`으로 매핑한다.
|
|
- `showRankChange=false`이면 모든 `CreatorRankingItem.showRankChange=false`로 매핑한다.
|
|
- `creatorId=0`은 `isBlocked=true`, `isTouchable=false`로 매핑한다.
|
|
- `rank < 1` item은 제외한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`
|
|
- 기대 결과: DTO/mapper 미구현으로 RED 실패.
|
|
|
|
- [x] **Task 3.2: API DTO와 mapper 구현**
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingModels.kt`
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeCreatorRankingMappers.kt`
|
|
- 구현 내용:
|
|
- `data class HomeCreatorRankingResponse(val showRankChange: Boolean, val items: List<HomeCreatorRankingItemResponse>)`
|
|
- `data class HomeCreatorRankingItemResponse(val rank: Int, val rankChange: Int?, val isNew: Boolean, val creatorId: Long, val nickname: String, val profileImageUrl: String)`
|
|
- `fun HomeCreatorRankingResponse.toCreatorRankingItems(): List<CreatorRankingItem>`
|
|
- `rank >= 1` filtering, `rank` sorting, `RankingChangeType` mapping, `abs(rankChange)` amount mapping
|
|
- `creatorId == 0L` -> `isBlocked=true`
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`
|
|
- 기대 결과: PASS.
|
|
|
|
- [x] **Task 3.3: API/Repository 작성**
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingApi.kt`
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeCreatorRankingRepository.kt`
|
|
- 구현 내용:
|
|
- `@GET("/api/v2/home/rankings/creators")`
|
|
- `fun getCreatorRankings(@Header("Authorization") authHeader: String): Single<ApiResponse<HomeCreatorRankingResponse>>`
|
|
- repository는 `getCreatorRankings(token: String)`으로 API를 위임한다.
|
|
- 검증 명령: `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과: 신규 API/Repository 컴파일 성공.
|
|
|
|
---
|
|
|
|
### Phase 4: ViewModel과 DI 등록
|
|
|
|
- [x] **Task 4.1: UI state와 ViewModel 작성**
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeCreatorRankingUiState.kt`
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingViewModel.kt`
|
|
- 구현 내용:
|
|
- `HomeCreatorRankingUiState.Loading`
|
|
- `HomeCreatorRankingUiState.Content(val items: List<CreatorRankingItem>)`
|
|
- `HomeCreatorRankingUiState.Empty`
|
|
- `HomeCreatorRankingUiState.Error(val message: String?)`
|
|
- `rankingStateLiveData`, `isLoading`, `toastLiveData`
|
|
- `loadCreatorRankings()`
|
|
- 기존 `HomeRecommendationViewModel`과 동일하게 `SharedPreferenceManager.token`, RxJava scheduler, unknown error toast 패턴 사용
|
|
- 검증 명령: `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과: ViewModel 컴파일 성공.
|
|
|
|
- [x] **Task 4.2: Koin DI 등록**
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
|
- 구현 내용:
|
|
- import 추가: `HomeCreatorRankingApi`, `HomeCreatorRankingRepository`, `HomeCreatorRankingViewModel`
|
|
- `networkModule`에 `single { ApiBuilder().build(get(), HomeCreatorRankingApi::class.java) }`
|
|
- `repositoryModule`에 `factory { HomeCreatorRankingRepository(get()) }`
|
|
- `viewModelModule`에 `viewModel { HomeCreatorRankingViewModel(get()) }`
|
|
- 검증 명령: `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과: Koin 등록과 import 컴파일 성공.
|
|
|
|
---
|
|
|
|
### Phase 5: 홈 레이아웃과 탭 전환 UI 연결
|
|
|
|
- [x] **Task 5.1: 홈 레이아웃에 랭킹 목록 추가 RED 테스트 작성**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
|
- 추가 테스트:
|
|
- `fragment_v2_main_home.xml`에 `rv_home_creator_rankings`가 존재한다.
|
|
- `rv_home_creator_rankings`는 `TextTabBarView` 아래에 직접 constraint 된다.
|
|
- Capsule Tab bar 관련 view id가 존재하지 않는다.
|
|
- 초기 visibility는 `GONE`이다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking layout*"`
|
|
- 기대 결과: 신규 RecyclerView 미존재로 RED 실패.
|
|
|
|
- [x] **Task 5.2: `fragment_v2_main_home.xml`에 랭킹 RecyclerView 추가**
|
|
- 수정: `app/src/main/res/layout/fragment_v2_main_home.xml`
|
|
- 구현 내용:
|
|
- `androidx.recyclerview.widget.RecyclerView` 추가
|
|
- id: `@+id/rv_home_creator_rankings`
|
|
- width/height: `0dp`
|
|
- top: `@id/text_tab_bar_home` bottom
|
|
- bottom/start/end: parent
|
|
- `android:visibility="gone"`
|
|
- `android:clipToPadding="false"`
|
|
- `android:paddingHorizontal="@dimen/spacing_14"`
|
|
- `android:paddingTop="@dimen/spacing_14"`
|
|
- `android:paddingBottom="@dimen/spacing_28"`
|
|
- 제외: Capsule Tab bar view 추가 금지.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking layout*"` 및 `./gradlew :app:mergeDebugResources`
|
|
- 기대 결과: PASS.
|
|
|
|
- [x] **Task 5.3: `HomeMainFragment` 탭 전환/adapter 연결 테스트 작성**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt`
|
|
- 추가 테스트:
|
|
- `CreatorRankingAdapter.createGridLayoutManager()`가 랭킹 RecyclerView에 사용되는 소스 계약
|
|
- `랭킹` index 선택 시 추천 content는 `GONE`, 랭킹 RecyclerView는 `VISIBLE`
|
|
- `추천` index 선택 시 추천 content는 `VISIBLE`, 랭킹 RecyclerView는 `GONE`
|
|
- `creatorId=0` item은 프로필 이동 intent를 만들지 않는다.
|
|
- `creatorId>0` item은 `UserProfileActivity` + `Constants.EXTRA_USER_ID` intent를 만든다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking*"`
|
|
- 기대 결과: Fragment 연결 미구현으로 RED 실패.
|
|
|
|
- [x] **Task 5.4: `HomeMainFragment`에 랭킹 탭 content 연결**
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/HomeMainFragment.kt`
|
|
- 구현 내용:
|
|
- `private val homeCreatorRankingViewModel: HomeCreatorRankingViewModel by viewModel()`
|
|
- `private val creatorRankingAdapter = CreatorRankingAdapter { openCreatorRankingProfile(it) }`
|
|
- `rvHomeCreatorRankings.layoutManager = CreatorRankingAdapter.createGridLayoutManager(requireContext())`
|
|
- `rvHomeCreatorRankings.adapter = creatorRankingAdapter`
|
|
- tab index 상수: `HOME_TAB_RECOMMENDATION = 0`, `HOME_TAB_RANKING = 1`, `HOME_TAB_FOLLOWING = 2`
|
|
- `setOnTabSelectedListener`에서 추천/랭킹만 content 전환 처리한다.
|
|
- 랭킹 최초 선택 시 `homeCreatorRankingViewModel.loadCreatorRankings()`를 호출한다.
|
|
- `HomeCreatorRankingUiState.Content`이면 `creatorRankingAdapter.submitItems(items)`
|
|
- `Empty`/`Error`이면 빈 목록을 submit한다.
|
|
- `openCreatorRankingProfile(item)`은 `item.creatorId > 0`일 때만 `UserProfileActivity`를 `Constants.EXTRA_USER_ID`와 함께 실행한다.
|
|
- 별도 analytics/logging은 추가하지 않는다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking*"` 및 `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과: PASS.
|
|
|
|
---
|
|
|
|
### Phase 6: 통합 검증과 문서 기록
|
|
|
|
- [x] **Task 6.1: targeted test 실행**
|
|
- 실행:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest"`
|
|
- 기대 결과: 모두 `BUILD SUCCESSFUL`.
|
|
- 실패 시: 실패 원인을 이 문서의 Verification Log에 누적하고, 수정 전 관련 task 체크박스를 되돌린다.
|
|
|
|
- [x] **Task 6.2: compile/resource/lint 검증**
|
|
- 실행:
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- `./gradlew :app:ktlintCheck`
|
|
- 기대 결과: 모두 `BUILD SUCCESSFUL`.
|
|
- 참고: 기존 `.editorconfig disabled_rules` deprecation warning은 신규 실패가 아니면 별도 수정하지 않는다.
|
|
|
|
- [x] **Task 6.3: 최종 문서 검증 기록 누적**
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/plan-task.md`
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/prd.md`
|
|
- 구현 내용:
|
|
- 실행한 명령, 성공/실패 결과, 실패 시 원인과 보완 내용을 Verification Log에 누적한다.
|
|
- 기존 Verification Log는 삭제하거나 덮어쓰지 않는다.
|
|
- 검증: PRD와 plan-task의 완료 상태와 실제 구현 상태가 일치한다.
|
|
|
|
|
|
---
|
|
|
|
### Phase 7: 순위 텍스트 고정 박스 정렬 보정
|
|
|
|
- [x] **Task 7.1: 순위 텍스트 박스 정렬 RED 테스트 추가**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingAdapterLayoutTest.kt`
|
|
- 구현 내용:
|
|
- Large, Compact medium, Compact small, Horizontal variant의 순위 영역 박스 크기/위치와 내부 중앙 정렬을 검증한다.
|
|
- Figma margin 값을 임의로 추가하지 않고 기존 박스 크기 안의 정렬만 검증한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`
|
|
- 기대 결과: production 수정 전 중앙 정렬 assertion이 실패한다.
|
|
|
|
- [x] **Task 7.2: 순위 텍스트 박스 내부 정렬 보정**
|
|
- 수정: `app/src/main/res/layout/view_creator_ranking_large_card.xml`
|
|
- 수정: `app/src/main/res/layout/view_creator_ranking_compact_card.xml`
|
|
- 수정: `app/src/main/res/layout/view_creator_ranking_horizontal_card.xml`
|
|
- 구현 내용:
|
|
- 기존 Kotlin 코드의 고정 박스 크기/left/top 계산은 유지한다.
|
|
- 순위 TextView가 박스 내부에서 중앙 정렬되도록 최소 속성만 추가한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`
|
|
- 기대 결과: RED 테스트가 `BUILD SUCCESSFUL`로 전환된다.
|
|
|
|
- [x] **Task 7.3: 후속 검증과 문서 기록 누적**
|
|
- 실행:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/prd.md`
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/plan-task.md`
|
|
- 기대 결과: 모두 `BUILD SUCCESSFUL`, Verification Log 누적.
|
|
|
|
|
|
---
|
|
|
|
### Phase 8: Figma 최신 순위 영역 수치 보정
|
|
|
|
- [x] **Task 8.1: 순위 영역 최신 수치 RED 테스트 추가**
|
|
- 수정: `app/src/test/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingAdapterLayoutTest.kt`
|
|
- 구현 내용:
|
|
- Figma `24:5658` 기준 1위, 2~7위, 8~10위, 11위 이후 순위 숫자 박스 크기와 rank-num 위치를 검증한다.
|
|
- rank-num 상승/하락/유지/New 표시의 기존 노출 정책은 유지한다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`
|
|
- 기대 결과: production 수정 전 1위/8~10위 순위 박스와 1위/8~10위 rank-num 위치 assertion이 실패한다.
|
|
|
|
- [x] **Task 8.2: 순위 숫자 박스와 rank-num 위치 보정**
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingLargeCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingCompactCardView.kt`
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/widget/creatorranking/CreatorRankingHorizontalCardView.kt`
|
|
- 구현 내용:
|
|
- 1위 순위 숫자 박스는 `86x116`, rank-num은 `left=20`, `top=116` 기준으로 맞춘다.
|
|
- 2~7위 순위 숫자 박스는 `56x70`, rank-num은 `left=10`, `top=70` 기준으로 맞춘다.
|
|
- 8~10위 순위 숫자 박스는 `52x50`, `left=0`, rank-num은 `left=10`, `top=50` 기준으로 맞춘다.
|
|
- 11위 이후 순위 텍스트는 rank group 내부에서 `48x52` 기준으로 맞춘다.
|
|
- 검증 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`
|
|
- 기대 결과: RED 테스트가 `BUILD SUCCESSFUL`로 전환된다.
|
|
|
|
- [x] **Task 8.3: 후속 검증과 문서 기록 누적**
|
|
- 실행:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:ktlintCheck`
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/prd.md`
|
|
- 수정: `docs/20260608_크리에이터_랭킹_페이지/plan-task.md`
|
|
- 기대 결과: 모두 `BUILD SUCCESSFUL`, Verification Log 누적.
|
|
|
|
---
|
|
|
|
## Verification Log
|
|
- 2026-06-08: `superpowers:writing-plans` 지침, PRD `docs/20260608_크리에이터_랭킹_페이지/prd.md`, 기존 홈 추천 계획 문서, `fragment_v2_main_home.xml`, `HomeMainFragment`, `AppDI`, 기존 홈 추천 API/ViewModel, `creatorranking` 위젯 구조와 테스트 위치를 확인했다.
|
|
- 2026-06-08: 이번 단계는 계획 문서 작성만 수행했으며 구현/빌드/테스트는 실행하지 않았다.
|
|
- 2026-06-08: Phase 1 범위로 PRD와 기존 `HomeMainFragment`, `fragment_v2_main_home.xml`, `AppDI`, 홈 추천 API/Repository/ViewModel, `creatorranking` 위젯/테스트 구조를 재확인했다. Capsule Tab bar, 팔로잉 탭 content, analytics/logging, ViewPager2/swipe 전환은 Phase 1-3 구현 범위에서 제외했다.
|
|
- 2026-06-08: Phase 2 TDD RED로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingItemTest"`를 실행해 `showRankChange` 미구현 컴파일 실패를 확인했다. 이후 `CreatorRankingItem.showRankChange` 기본값을 추가하고 동일 명령이 `BUILD SUCCESSFUL`로 통과했다.
|
|
- 2026-06-08: Phase 2 rank-num 숨김 TDD RED로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`를 실행했다. 최초 Robolectric 설정 누락으로 Koin 초기화 오류가 발생해 기존 위젯 테스트 패턴과 동일하게 `@Config(sdk = [28], application = Application::class)`를 적용했고, 이후 Large/Compact/Horizontal 숨김 assertion 3건 실패를 확인했다. 세 카드 view에 `showRankChange=false` 시 `ll_creator_ranking_delta`를 `GONE` 처리하도록 구현한 뒤 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`가 `BUILD SUCCESSFUL`로 통과했다.
|
|
- 2026-06-08: Phase 3 mapper TDD RED로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`를 실행해 `HomeCreatorRankingResponse`, `HomeCreatorRankingItemResponse`, `toCreatorRankingItems` 미구현 컴파일 실패를 확인했다. 이후 DTO와 mapper를 추가하고 동일 명령이 `BUILD SUCCESSFUL`로 통과했다.
|
|
- 2026-06-08: Phase 3 API/Repository를 추가한 뒤 `./gradlew :app:compileDebugKotlin`이 `BUILD SUCCESSFUL`로 통과했다. 검증 중 병렬 Gradle 실행 1건에서 `classes.jar` zip header 오류가 발생했으나, 동일 타겟을 단독 재실행해 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck` 모두 `BUILD SUCCESSFUL`을 확인했다. `.editorconfig disabled_rules` deprecation warning은 기존 경고로 보고 수정하지 않았다.
|
|
- 2026-06-08: Phase 4로 `HomeCreatorRankingUiState`, `HomeCreatorRankingViewModel`을 추가하고 `AppDI`에 `HomeCreatorRankingApi`, `HomeCreatorRankingRepository`, `HomeCreatorRankingViewModel`을 등록했다. 검증 명령 `./gradlew :app:compileDebugKotlin`이 `BUILD SUCCESSFUL`로 통과했다. Gradle deprecated feature warning은 기존 빌드 경고로 보고 수정하지 않았다.
|
|
- 2026-06-08: Phase 5.1 RED로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking layout*"`를 실행해 `R.id.rv_home_creator_rankings` 미정의 컴파일 실패를 확인했다. 이후 `fragment_v2_main_home.xml`에 `rv_home_creator_rankings`를 `TextTabBarView` 아래 직접 constraint되는 `GONE` RecyclerView로 추가했고, 동일 테스트와 `./gradlew :app:mergeDebugResources`가 `BUILD SUCCESSFUL`로 통과했다.
|
|
- 2026-06-08: Phase 5.3 RED로 `HomeMainFragmentLayoutTest`에 랭킹 adapter/grid, 추천/랭킹 탭 visibility 전환, 최초 랭킹 load, ranking state observe/submit, `creatorId > 0` 프로필 이동 guard 계약 테스트를 추가하고 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest.home ranking*"`를 실행해 5개 assertion 실패를 확인했다. 이후 `HomeMainFragment`에 `HomeCreatorRankingViewModel`, `CreatorRankingAdapter`, `CreatorRankingAdapter.createGridLayoutManager(requireContext())`, tab index 상수, 최초 로드 guard, ranking observer, `openCreatorRankingProfile` guard를 연결했고 동일 테스트와 `./gradlew :app:compileDebugKotlin`이 `BUILD SUCCESSFUL`로 통과했다. 기존 Gradle deprecated feature warning과 Kotlin/JDK deprecation warning은 신규 실패가 아니므로 수정하지 않았다.
|
|
- 2026-06-08: Phase 6.1 targeted test로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorRankingMapperTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest"`를 순차 실행했고 모두 `BUILD SUCCESSFUL`로 통과했다.
|
|
- 2026-06-08: Phase 6.2 통합 검증으로 `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`를 순차 실행했고 모두 `BUILD SUCCESSFUL`로 통과했다. Gradle deprecated feature warning은 기존 빌드 경고로 보고 수정하지 않았다.
|
|
- 2026-06-08: Phase 6.3으로 PRD와 plan-task 문서의 Verification Log를 누적하고 Phase 6 체크박스를 완료 처리했다. 이번 Phase 6에서는 신규 production code 변경이 없어 별도 RED 테스트 추가 없이 기존 targeted test와 build/lint 검증으로 완료했다.
|
|
- 2026-06-09: 사용자 확인에 따라 순위 숫자에 별도 left margin을 추가하지 않고, Figma의 고정 텍스트 박스 내부 중앙 정렬을 맞추는 Phase 7 후속 작업을 추가했다.
|
|
- 2026-06-09: Phase 7.1 RED로 `CreatorRankingAdapterLayoutTest`에 Large/Compact medium/Compact small/Horizontal 순위 박스 검증을 추가하고 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.CreatorRankingAdapterLayoutTest"`를 실행했다. Large/Compact medium/Compact small 3건이 `Gravity.CENTER` assertion에서 실패해 순위 TextView 내부 정렬 미반영을 확인했다.
|
|
- 2026-06-09: Phase 7.2로 `view_creator_ranking_large_card.xml`, `view_creator_ranking_compact_card.xml`, `view_creator_ranking_horizontal_card.xml`의 순위 TextView에 `android:gravity="center"`를 추가했다. 동일 `CreatorRankingAdapterLayoutTest`가 `BUILD SUCCESSFUL`로 전환됐다.
|
|
- 2026-06-09: Phase 7.3 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`와 `./gradlew :app:mergeDebugResources`를 실행했고 모두 `BUILD SUCCESSFUL`로 통과했다. Gradle deprecated feature warning은 기존 빌드 경고로 보고 수정하지 않았다.
|
|
- 2026-06-09: Figma `24:5658` design context와 screenshot을 재확인했고, 순위 숫자 박스 및 rank-num 위치에서 현재 구현과 다른 최신 기준값을 Phase 8 후속 작업으로 추가했다.
|
|
- 2026-06-09: Phase 8.1 RED로 `CreatorRankingAdapterLayoutTest`의 순위 숫자 박스/순위 변화 표시 위치 기대값을 Figma `24:5658` 기준으로 갱신하고 실행했다. production 수정 전 1위/2~7위/8~10위 순위 박스, 11위 이후 rank group top, 1위/8~10위 rank-num 위치 assertion 6건 실패를 확인했다.
|
|
- 2026-06-09: Phase 8.2로 `CreatorRankingLargeCardView`, `CreatorRankingCompactCardView`, `CreatorRankingHorizontalCardView`의 위치 계산을 Figma 기준으로 보정했다. 동일 `CreatorRankingAdapterLayoutTest`가 `BUILD SUCCESSFUL`로 전환됐다. New badge는 기존 PRD가 이미지 리소스 사용을 요구하고 `ic_rank_new.png`가 36x23으로 Figma 크기와 일치해 유지했다.
|
|
- 2026-06-09: Phase 8.3 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.creatorranking.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:ktlintCheck`를 실행했다. 최초 ktlint에서 테스트 파일 불필요한 빈 줄 2건이 실패해 정리했고, 이후 `CreatorRankingAdapterLayoutTest`와 `ktlintCheck`가 `BUILD SUCCESSFUL`로 통과했다. Gradle deprecated feature warning은 기존 빌드 경고로 보고 수정하지 않았다.
|