41 KiB
메인 홈 팔로잉 탭 구현 계획/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이다. Authorizationheader는 optional이며, token이 blank이면 header를 보내지 않는다.- query parameter는 보내지 않는다.
isLoginRequired = true이면 팔로잉 섹션을 표시하지 않는다.- 로그인 유도 화면의 실제 UI, 문구, CTA, 로그인 완료 후 복귀 정책은 별도 확정 후 구현한다.
recentNews시간은visibleFromAtUtc를 디바이스 타임존 기준으로 상대 시간 표시한다.PHOTO_CONTENTlabel은 우선화보로 표시한다.- ranking news의
rank가 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:ktlintCheckgit 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분기는 단위 테스트 중심으로 검증한다.
- ViewModel 상태, optional auth header,
- Phase 4: 필수 참조
- 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식 섹션 배치와 spacing은 Figma
24:5682를 기준으로 확인한다.
- 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식 섹션 배치와 spacing은 Figma
- Phase 5: 필수 참조
- 최종 수동 화면 검증은 PRD의 포함/제외 항목과 실제 화면을 대조한다.
파일 구조
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.ktGET /api/v2/home/followingRetrofit endpoint를 정의한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.ktHomeFollowingTabResponse,FollowingCreatorResponse,FollowingLiveResponse,FollowingScheduleResponse,FollowingNewsResponse,FollowingNewsTypeDTO를 정의한다.
- 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.ktLoading,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로 변환하고,
rank == nullnews 숨김,PHOTO_CONTENTlabel,visibleFromAtUtc시간 기준을 적용한다.
- DTO를 UI model/state로 변환하고,
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingAuthHeader.kt- blank token이면
null, 값이 있으면Bearer {token}을 반환한다.
- blank 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.ktHomeFollowingApi,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.ktHOME_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.ktfollowingCreatorshorizontal profile list를 바인딩한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.ktonAirLiveshorizontal live card list를 바인딩한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.ktrecentChatshorizontal chat card list를 바인딩한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.ktmonthlySchedulesvertical schedule list를 바인딩한다.
- Create:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.ktrecentNewsvertical 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을 추가한다.
- 팔로잉 섹션 title,
- Modify:
app/src/main/res/values-en/strings.xml- 팔로잉 섹션 title,
On Air, photo label, empty/error label을 추가한다.
- 팔로잉 섹션 title,
- Modify:
app/src/main/res/values-ja/strings.xml- 팔로잉 섹션 title,
On Air, photo label, empty/error label을 추가한다.
- 팔로잉 섹션 title,
- 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로 검증한다.
- layout id, adapter/ViewModel 연결,
Phase 1: 기존 구조 확인과 작업 경계 고정
-
Task 1.1: 홈 탭 삽입 지점 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/res/layout/fragment_v2_main_home.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.ktapp/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 제어 지점을 확인했다.
- Run:
- 확인:
-
Task 1.2: 재사용 위젯과 신규 adapter 경계 확정
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeCreatorProfileImageLoader.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/widget/livethumbnail/LiveThumbnailDetailView.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/model/ChatRoomMappers.ktapp/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재사용 후보를 확인했다.
- Run:
- 확인:
-
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_CONTENTlabel 정책을 확인했다.
- Run:
- 확인:
Phase 2: API, DTO, Repository, mapper 추가
-
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}을 반환한다.
- blank token은
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingAuthHeaderTest" - Expected: helper 구현 전 RED 실패.
- Result: RED 확인.
homeFollowingAuthHeader미구현으로compileDebugUnitTestKotlinunresolved reference 실패가 발생했다.
- Run:
- 생성:
-
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}으로 검증됐다.
- Run:
- 생성:
-
Task 2.3: API/DTO/Repository 계약 추가
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.ktapp/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를 등록한다.
- Retrofit endpoint는
- 검증:
- Run:
./gradlew :app:compileDebugKotlin - Expected: 신규 data layer와 DI 등록이 컴파일된다.
- Result: PASS.
HomeFollowingApi, DTO, Repository, API/Repository DI 등록이compileDebugKotlin에서 컴파일됐다.
- Run:
- 생성:
-
Task 2.4: string resource 추가
- 수정:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xml
- 작업:
- 섹션 title 문자열을 추가한다.
screen_home_following_creators_titlescreen_home_following_on_air_titlescreen_home_following_recent_chats_titlescreen_home_following_monthly_schedules_titlescreen_home_following_recent_news_title
- news/category 문자열을 추가한다.
screen_home_following_on_airscreen_home_following_photo_content
- empty/error 문자열을 추가한다.
screen_home_following_emptyscreen_home_following_error
- 섹션 title 문자열을 추가한다.
- 검증:
- Run:
./gradlew :app:mergeDebugResources - Expected: 3개 locale string resource가 중복 없이 merge된다.
- Result: PASS.
values,values-en,values-jastring resource merge가 통과했다.
- Run:
- 수정:
-
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_contentlabel로 매핑된다.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 미구현으로
compileDebugUnitTestKotlinunresolved reference 실패가 발생했다.
- Run:
- 생성:
-
Task 2.6: UI state/model과 mapper 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiState.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt
- 작업:
HomeFollowingUiState는Loading,LoginRequired,Content,Empty,Error를 정의한다.Content에는followingCreators,onAirLives,recentChats,monthlySchedules,recentNewssection을 둔다.Content.isEmptyhelper를 추가해 모든 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_CONTENTlabel, null rank filtering,visibleFromAtUtcformatter 전달이 검증됐다.
- Run:
- 생성:
Phase 3: ViewModel 상태와 API 호출 연결
-
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미구현으로compileDebugUnitTestKotlinunresolved reference 실패가 발생했다.
- Run:
- 생성:
-
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 상태 전환이 검증됐다.
- Run:
- 생성:
-
Task 3.3: data/model/ViewModel 통합 컴파일 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.ktapp/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에서 컴파일됐다.
- Run:
- 검증 기록:
- 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 연결
-
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가 모두 실패했다.
- Run:
- 생성:
-
Task 4.2: 팔로잉 content layout 추가
- 수정:
app/src/main/res/layout/fragment_v2_main_home.xml
- 생성:
app/src/main/res/layout/item_home_following_creator.xmlapp/src/main/res/layout/item_home_following_live.xmlapp/src/main/res/layout/item_home_following_chat.xmlapp/src/main/res/layout/item_home_following_schedule.xmlapp/src/main/res/layout/item_home_following_news_rank.xmlapp/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를 통과했다.
- Run:
- 수정:
-
Task 4.3: 팔로잉 adapter 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingCreatorAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.ktapp/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을 분리한다.
- 각 adapter는
- 검증:
- Run:
./gradlew :app:compileDebugKotlin - Expected: 신규 adapter가 컴파일된다.
- Result: PASS. 팔로잉 creator/live/chat/schedule/news adapter 5개가
compileDebugKotlin에서 컴파일됐다.
- Run:
- 생성:
-
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로 검증됐다.
- Run:
- 수정:
-
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만 받는다.
- creator item click은 기존
- 검증:
- 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으로 유지했다.
- Run:
- 검증 기록:
- 2026-06-25 Phase 4 코드 리뷰: Figma
24:5682와 PRD 기준으로On Air시작 시간, 이달의 스케줄 프로필/타입 label/On Air 상태, 최근 소식 label/title 바인딩 누락을 확인했다. 누락 항목은HomeFollowingFragmentSourceTestRED로 고정한 뒤 adapter/layout 최소 수정으로 보완했다. - 2026-06-25 Phase 4 재코드 리뷰: 현재 워킹트리 기준
HomeMainFragment, 팔로잉 adapter 5개, 팔로잉 layout,HomeFollowingFragmentSourceTest를 Figma24:5682/PRD와 대조했으며 blocking finding은 발견하지 못했다.
- 2026-06-25 Phase 4 코드 리뷰: Figma
- 수정:
Phase 5: 통합 검증과 문서 기록
-
Task 5.1: 팔로잉 관련 단위 테스트 실행
- 검증:
- Run:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" - Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
- Run:
- 검증 기록:
- 2026-06-25 Phase 4 리뷰 보완 후 실행 결과 PASS. 팔로잉 관련 local unit/source test가 모두 통과했다.
- 검증:
-
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 없음.
- Run:
- 검증 기록:
- 2026-06-25 Phase 4 리뷰 보완 후
./gradlew :app:mergeDebugResourcesPASS. 최초 sandbox lock 권한 오류 후 승인 실행으로 통과했다. - 2026-06-25 Phase 4 리뷰 보완 후
./gradlew :app:compileDebugKotlinPASS. - 2026-06-25 Phase 4 리뷰 보완 후
./gradlew :app:ktlintCheckPASS. 기존.editorconfig disabled_rulesdeprecation warning만 출력됐다. - 2026-06-25 Phase 4 리뷰 보완 후
git diff --checkPASS.
- 2026-06-25 Phase 4 리뷰 보완 후
- 검증:
-
Task 5.3: Figma 기준 수동 확인
- 확인:
- Figma
24:5682 app/src/main/res/layout/fragment_v2_main_home.xml
- Figma
- 수동 확인 항목:
팔로잉탭 선택 시 추천/랭킹 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-06-25 Figma
- 확인:
Phase 6: Figma 디자인 재대조 후속 수정
-
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개가 현재 구현과 맞지 않아 실패했다.
- Run:
- 수정:
-
Task 6.2: 팔로잉 크리에이터와 최근 대화 UI 수정
- 수정:
app/src/main/res/layout/fragment_v2_main_home.xmlapp/src/main/res/layout/item_home_following_creator.xmlapp/src/main/res/layout/item_home_following_chat.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/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로 검증됐다.
- Run:
- 수정:
-
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 없음.
- Run:
- 검증 기록:
- 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_rulesdeprecation 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.
- 2026-06-26 후속 변경 검증:
- 검증:
-
Task 6.4: 최근 소식 Feed 위젯 재사용과 chevron 후속 수정
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/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.
- Run:
- 검증 기록:
- 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_rulesdeprecation warning만 출력됐다.
- 2026-06-26 최근 소식 후속 수정: 먼저
- 수정:
Verification Log
- 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_rulesdeprecation 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_rulesdeprecation warning만 출력됐다.