Files

75 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이다.
  • Authorization header는 optional이며, token이 blank이면 header를 보내지 않는다.
  • query parameter는 보내지 않는다.
  • isLoginRequired = true이면 팔로잉 섹션을 표시하지 않는다.
  • 로그인 유도 화면의 실제 UI, 문구, CTA, 로그인 완료 후 복귀 정책은 별도 확정 후 구현한다.
  • recentNews 시간은 visibleFromAtUtc를 디바이스 타임존 기준으로 상대 시간 표시한다.
  • PHOTO_CONTENT label은 우선 화보로 표시한다.
  • recentNewstype에 대응하는 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: 기존 구조 확인과 작업 경계 고정

  • 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 제어 지점을 확인했다.
  • 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 재사용 후보를 확인했다.
  • 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 추가

  • 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 실패가 발생했다.
  • 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}으로 검증됐다.
  • 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에서 컴파일됐다.
  • 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가 통과했다.
  • 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로 매핑된다.
      • recentChatsChatRoomListItemResponse.toUiItem() 결과가 null인 항목을 제외한다.
      • monthlySchedules는 서버 응답 순서를 유지한다.
      • PHOTO_CONTENTscreen_home_following_photo_content label로 매핑된다.
      • CREATOR_RANKING, CONTENT_RANKINGrank == 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 실패가 발생했다.
  • 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
    • 작업:
      • HomeFollowingUiStateLoading, 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 호출 연결

  • 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 실패가 발생했다.
  • 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.tokenhomeFollowingAuthHeader()로 변환해 repository에 전달한다.
      • RxJava3 subscribeOn(Schedulers.io()), observeOn(AndroidSchedulers.mainThread()) 패턴을 따른다.
      • success data는 mapper로 UI state 변환한다.
      • error는 HomeFollowingUiState.Error와 기존 unknown error toast 패턴을 따른다.
      • Koin viewModelModuleHomeFollowingViewModel(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 상태 전환이 검증됐다.
  • 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 연결

  • Task 4.1: Fragment source RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt
    • 테스트 케이스:
      • fragment_v2_main_home.xmlnsv_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가 있다.
      • HomeMainFragmentHomeFollowingViewModel을 주입한다.
      • 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가 모두 실패했다.
  • 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_contenttext_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를 통과했다.
  • 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에서 컴파일됐다.
  • 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로 검증됐다.
  • 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: 통합 검증과 문서 기록

  • 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가 모두 통과했다.
  • 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 디자인 재대조 후속 수정

  • 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개가 현재 구현과 맞지 않아 실패했다.
  • 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로 검증됐다.
  • 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:installDebugkr.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 검증을 추가했고, HomeFollowingChatAdapterrecyclerItemLayoutParams(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 fieldsmaxLines="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.
  • 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.FeedCommunityViewview_feed_community.xml을 재사용한다.
      • ranking 최근 소식은 기존 v2.widget.feed.FeedRankViewview_feed_rank.xml을 재사용한다.
      • 오디오/화보 content 최근 소식은 기존 v2.widget.feed.FeedContentViewview_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를 제거했고, HomeFollowingNewsAdapterCOMMUNITY_POSTFeedCommunityView, 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 정합성 후속 수정

  • 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건이 현재 구현과 맞지 않아 실패했다.
  • 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 파일은 수정하지 않았다.
  • 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.xmlandroid:clipToOutline 속성을 제거한다.
      • FeedContentView root view에서 clipToOutline = trueViewOutlineProvider.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.xmlandroid: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: 팔로잉 크리에이터 전체 버튼 후속 추가

  • 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 itemitem_home_following_creator_all.xml 누락으로 실패했다.
  • 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로 검증됐다.
  • 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 계약 반영

  • 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_RANKINGcreatorRanking.rank, creatorRanking.creatorId, creatorRanking.nickname, creatorRanking.profileImageUrl로 ranking UI item을 만드는지 검증한다.
      • CONTENT_RANKINGcontentRanking.rank, contentRanking.contentId, contentRanking.contentImageUrl, contentRanking.title로 ranking UI item을 만드는지 검증한다.
      • AUDIO_CONTENTaudioContent, PHOTO_CONTENTphotoContent payload로 content UI item을 만드는지 검증한다.
      • COMMUNITY_POSTcommunityPost.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이 실패했다.
  • 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이 통과했다.
  • 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_messageFeedRankHighlight 정책을 유지하되 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은 FeedCommunityViewpostId, 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 = nullCONTENT_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.
  • 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 재구현

  • 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 상세 이동

  • 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가 실패한다.
  • 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한다.
      • 유효한 postIdensureMainV2NavigationAllowed 안에서 CreatorChannelCommunityDetailActivity.newIntent(requireContext(), postId)로 시작한다.
    • 검증:
      • Run: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"
      • Expected GREEN: source test PASS.
  • 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 문구 중앙 표시

  • 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.xmltv_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와 계획 문서에 추가했다. HomeFollowingFragmentSourceTesttv_home_following_empty id, string 재사용, 중앙 정렬 속성, HomeFollowingUiState.Empty 전용 표시 분기를 고정하는 source test를 추가했다.
  • 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.xmltv_home_following_empty를 추가하고 @string/screen_home_following_empty, match_parent, gravity=center, textAlignment=center, 기본 gone을 적용했다. HomeMainFragment에서는 HomeFollowingUiState.Empty만 empty 문구를 표시하고, Content, LoginRequired, Error는 숨기도록 분기했다.
  • 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.CommunityCreatorChannelCommunityDetailActivity.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.xmltv_home_following_chat_messagemaxLines="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 계약 반영 검증: FollowingNewsResponsenewsId, 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 호출을 삭제했다.