Files
sodalive-android/docs/20260625_메인_홈_팔로잉_탭/plan-task.md

152 KiB
Raw Blame History

메인 홈 팔로잉 탭 구현 계획/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이다.
  • Reuse: app/src/main/res/layout/view_feed_rank.xml, app/src/main/res/layout/view_feed_content.xml, app/src/main/res/layout/view_feed_community.xml
    • 최근 소식은 기존 Feed 위젯 layout을 재사용한다.
  • Delete: app/src/main/res/layout/item_home_following_news_rank.xml, app/src/main/res/layout/item_home_following_news_content.xml
    • Task 6.5에서 Feed 위젯 전환 후 obsolete 팔로잉 전용 최근 소식 layout을 제거했다.
  • Modify: app/src/main/res/values/strings.xml
    • 팔로잉 섹션 title, On Air, 화보, empty/error label을 추가한다.
  • Modify: app/src/main/res/values-en/strings.xml
    • 팔로잉 섹션 title, On Air, photo label, empty/error label을 추가한다.
  • Modify: app/src/main/res/values-ja/strings.xml
    • 팔로잉 섹션 title, On Air, photo label, empty/error label을 추가한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingAuthHeaderTest.kt
    • optional auth header 생성 규칙을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt
    • DTO to UI mapping, login-required, rank null filtering, news label/time 기준을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt
    • API success/error/login-required/loading 상태 전환을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt
    • layout id, adapter/ViewModel 연결, HOME_TAB_FOLLOWING 분기, chevron click callback 연결을 source-level로 검증한다.

Phase 1: 기존 구조 확인과 작업 경계 고정

  • 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/FeedItem.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedRankView.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedContentView.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/model/ChatRoomMappers.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/common/CreatorActivityType.kt
    • 작업:
      • 프로필 이미지 로딩은 HomeCreatorProfileImageLoader 또는 같은 placeholder 정책 재사용으로 고정한다.
      • 채팅 데이터 변환은 ChatRoomListItemResponse.toUiItem() 재사용으로 고정한다.
      • 스케줄 type label은 CreatorActivityType.labelResId 재사용으로 고정한다.
      • Figma와 크기가 맞지 않는 카드들은 팔로잉 전용 adapter/layout 신규 생성으로 고정한다.
    • 검증:
      • Run: rg -n "HomeCreatorProfileImageLoader|class LiveThumbnailDetailView|sealed class FeedItem|fun ChatRoomListItemResponse.toUiItem|enum class CreatorActivityType" app/src/main/java/kr/co/vividnext/sodalive/v2
      • Expected: 재사용 후보 클래스와 함수가 확인된다.
      • Result: PASS. LiveThumbnailDetailView, FeedItem, ChatRoomListItemResponse.toUiItem(), CreatorActivityType 재사용 후보를 확인했다.
  • 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 전달이 검증됐다.

Task 2.7: 미사용 팔로잉 string resource 정리

Goal 실행 P2-R1: 확정 review 항목 REV-P2-001에 따라 후속 UI·오류 처리 변경 후 참조되지 않는 팔로잉 문자열 리소스만 제거한다.

  • 시작 조건: reviews/phase2-following-data-mapper-review.mdREV-P2-001 확정.
  • 완료 증거: 3개 미사용 key의 참조 0건 재확인 → 3개 locale 선언 제거 → resource merge·Kotlin compile·팔로잉 회귀·diff 검증 PASS.
  • 범위 밖: 사용 중인 screen_home_following_on_air, empty·ranking·section title 문자열 변경, 표시 문구 재기획, UI 구조 변경.
  • TDD 예외 사유: 실행 로직이 아닌 미사용 Android resource 선언 삭제이며, 이를 위한 신규 source test는 저장소 테스트 정책에 맞지 않는다.
  • 대체 검증 방법: rg로 참조 0건을 재확인하고 resource merge, Kotlin compile, 기존 팔로잉 회귀 테스트, git diff --check를 실행한다.

Files:

  • Modify: app/src/main/res/values/strings.xml

  • Modify: app/src/main/res/values-en/strings.xml

  • Modify: app/src/main/res/values-ja/strings.xml

  • screen_home_following_creators_title, screen_home_following_on_air_title, screen_home_following_error의 production/test 참조가 0건인지 재확인한다.

  • 3개 locale에서 위 3개 key만 제거하고 사용 중인 팔로잉 문자열은 유지한다.

  • ./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"를 실행한다.

  • git diff --check를 실행하고 검증 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-31: rg -n "screen_home_following_creators_title|screen_home_following_on_air_title|screen_home_following_error" app/src/main/java app/src/main/res/layout app/src/test 결과 선언 외 참조 0건을 확인했다. 3개 locale strings.xml에서 위 3개 key만 제거하고 screen_home_following_on_air, empty, ranking, section title 문자열은 유지했다. ./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin PASS. 이후 팔로잉 전체 회귀 첫 실행에서 R.string id 변경 증분 캐시로 HomeFollowingMapperTest 2건이 실패해 ./gradlew --no-daemon --rerun-tasks :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"로 재빌드했고 PASS. 최종 ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS.

Task 2.8: 미사용 최근 소식 label UI model 정리

Goal 실행 P2-R2: 확정 review 항목 REV-P2-002에 따라 Feed category로 대체된 후 production에서 사용하지 않는 HomeFollowingNewsUiItem.Content.labelResId와 전용 mapper를 제거한다.

  • 시작 조건: reviews/phase2-following-data-mapper-review.mdREV-P2-002 확정.
  • 완료 증거: production 참조 0건 재확인 → UI model·mapper·fixture의 미사용 label 경로 제거 → Feed Photo category의 locale label 회귀·Kotlin compile·ktlint PASS.
  • 범위 밖: FeedContentCategory 구조, screen_home_following_photo_content 문구, 최근 소식 API/DTO·표시·라우팅 변경.
  • TDD 예외 사유: 실행 동작 수정이 아닌 미사용 UI model 필드와 mapper 제거이며, 기존 Feed category 회귀로 동작 무변경을 검증한다.
  • 대체 검증 방법: rg로 production/test 참조를 대조하고 mapper·adapter focused test, Kotlin compile, ktlint, git diff --check를 실행한다.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt

  • rg -n "labelResId|toLabelResId" 결과로 최근 소식 Content.labelResIdFollowingNewsType.toLabelResId()가 production 표시에서 사용되지 않는지 재확인한다.

  • HomeFollowingNewsUiItem.Content.labelResId, mapper 대입과 전용 toLabelResId()만 제거하고 Feed Audio/Photo category mapping은 유지한다.

  • mapper/adapter test fixture에서 제거된 필드를 정리하고 PHOTO_CONTENT 표시가 FeedContentCategory.Photo와 locale string을 사용하는 회귀를 유지한다.

  • mapper·news adapter focused test, 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행하고 결과를 누적한다.

검증 기록:

  • 2026-07-31: rg -n -- "labelResId|toLabelResId" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home app/src/test/java/kr/co/vividnext/sodalive/v2/main/home로 팔로잉 최근 소식 Content.labelResIdFollowingNewsType.toLabelResId()의 production 표시 참조가 없음을 재확인했다. HomeFollowingNewsUiItem.Content.labelResId, mapper 대입, 전용 toLabelResId()를 제거했고, HomeFollowingNewsAdapterTestPHOTO_CONTENT가 실제 FeedContentCategory.Photo locale label로 표시되는 회귀를 추가했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest", 팔로잉 전체 회귀, :app:mergeDebugResources, :app:compileDebugKotlin, :app:ktlintCheck PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.

Phase 3: ViewModel 상태와 API 호출 연결

  • 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.

Task 3.4: ViewModel Loading → 결과 상태 발행 순서 회귀 검증

Goal 실행 P3-R1: 확정 review 항목 REV-P3-001에 따라 loadFollowing()Loading과 최종 상태 발행 순서를 실행 가능한 테스트로 고정한다.

  • 시작 조건: reviews/phase3-following-viewmodel-review.mdREV-P3-001 확정.
  • 완료 증거: 상태 이력 assertion 추가 → Phase 3 focused test와 팔로잉 전체 회귀 PASS.
  • 범위 밖: HomeFollowingViewModel 상태 구조 변경, scheduler 교체, repository/API 변경.
  • TDD 예외 사유: production은 이미 Loading과 결과 상태를 순서대로 발행하며, 확정 항목은 동작 결함이 아니라 기존 테스트의 assertion 누락이므로 의도적인 production 변조 없이 RED를 만들지 않는다.
  • 대체 검증 방법: observer 상태 이력으로 Loading → Content를 직접 assertion하고 기존 error/login-required 테스트와 전체 팔로잉 회귀를 실행한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt

  • observer가 수집한 상태 이력이 LoadingContent를 순서대로 포함하도록 테스트 fixture와 assertion을 최소 보완한다.

  • production 파일 변경이 없고 기존 error/login-required 테스트가 그대로 유지되는지 diff로 확인한다.

  • GREEN 확인: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"를 실행한다.

  • 회귀 검증: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"git diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-30: HomeFollowingViewModelTest.loadFollowing은 loading 후 content 상태를 발행한다에 observer 상태 이력 assertion을 추가해 Loading → Content 순서를 고정했다. production 파일은 변경하지 않았다. focused test와 팔로잉 전체 회귀, git diff --check가 PASS했다.

Phase 4: 팔로잉 탭 UI surface와 adapter 연결

  • 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은 발견하지 못했다.

Task 4.6: On Air·스케줄 item 상세 이동 연결

Goal 실행 P4-R1: 확정 review 항목 REV-P4-001에 따라 On Air와 이달의 스케줄 item 터치가 기존 공통 도메인 액션으로 실제 이동하도록 한다.

  • 시작 조건: reviews/phase4-following-ui-routing-review.mdREV-P4-001 확정과 FollowingLiveResponse.liveId, FollowingScheduleResponse.type/targetId 계약 확인.
  • 완료 증거: no-op 재현 RED → 기존 Live/Creator/Content/Community Action을 재사용한 GREEN → focused test와 공통 액션 회귀 PASS.
  • 범위 밖: 더보기 chevron 목적지, 최근 소식 CONTENT_RANKING·PHOTO_CONTENT 이동, 공통 액션 정책 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • RED: On Air와 schedule type별 공통 액션 호출을 요구하는 테스트를 추가해 현재 Unit 구현에서 실패하는지 확인한다.

  • GREEN: On Air는 liveActionCoordinator.enterLiveRoom(item.liveId)를 사용하고, 스케줄은 CreatorActivityTypetargetId를 기존 최근 활동 route 정책에 맞춰 Live/Content/Community 공통 액션으로 전달한다.

  • 방어 검증: targetId <= 0L과 지원하지 않는 route는 공통 액션 호출 전에 무시하고, 직접 Intent 또는 legacy extra를 만들지 않는지 검증한다.

  • 회귀 검증: home focused test, v2.live.action.*, v2.content.action.*, v2.community.action.*, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행한다.

검증 기록:

  • 2026-07-30 RED: HomeMainFragmentLoginGuardSourceTest에 팔로잉 On Air와 스케줄 type별 공통 Action 호출 assertion을 추가했고, 구현 전 onFollowingLiveClick()/onFollowingScheduleClick()Unit이라 focused test가 실패하는 것을 확인했다.
  • 2026-07-30 GREEN: On Air는 liveActionCoordinator.enterLiveRoom(item.liveId), 스케줄은 targetId > 0L일 때 CreatorActivityType.Live는 live action, LiveReplay/AudioContentActionCommand.AudioDetail, CommunityCommunityActionCommand.PostDetail로 연결했다. 직접 Intent 조립은 추가하지 않았다. focused test, 팔로잉 전체 회귀, 공통 action 테스트, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check PASS.

Task 4.7: LoginRequired 당겨서 새로고침 종료 보장

Goal 실행 P4-R2: 확정 review 항목 REV-P4-002에 따라 팔로잉 새로고침 응답이 LoginRequired여도 spinner와 refresh 추적 상태를 종료한다.

  • 시작 조건: reviews/phase4-following-ui-routing-review.mdREV-P4-002 확정과 기존 finishHomePullRefresh(HOME_TAB_FOLLOWING) 정책 확인.
  • 완료 증거: LoginRequired 분기의 refresh 종료 누락 RED → 최소 분기 보완 GREEN → 팔로잉·홈 refresh 회귀 PASS.
  • 범위 밖: 로그인 유도 UI/문구/CTA, ensureV2Access 정책, ViewModel API 계약 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • Test: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLayoutTest.kt

  • RED: pull refresh 중 LoginRequired 상태가 finishHomePullRefresh(HOME_TAB_FOLLOWING)을 호출하지 않는 현재 분기를 재현한다.

  • GREEN: LoginRequired에서 section 숨김 후 팔로잉 refresh를 종료하고, 일반 진입에서는 불필요한 scroll 변경이 없는 최소 분기를 적용한다.

  • GREEN 확인: 팔로잉 Fragment focused test와 홈 pull-refresh 관련 focused test를 실행한다.

  • 회귀 검증: 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행하고 결과를 누적한다.

검증 기록:

  • 2026-07-30 RED: HomeFollowingFragmentSourceTestLoginRequired 분기에서 finishHomePullRefresh(HOME_TAB_FOLLOWING) 호출을 요구하도록 보강했고, 구현 전 focused test 실패로 누락을 확인했다.
  • 2026-07-30 GREEN: LoginRequired 분기에서 섹션을 숨긴 뒤 팔로잉 pull-refresh 종료를 호출하도록 최소 보완했다. focused test, 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check PASS.

Task 4.8: blank 프로필 이미지의 이전 Coil 요청 해제

Goal 실행 P4-R3: 확정 review 항목 REV-P4-003에 따라 재사용된 프로필 ImageView에 blank URL을 바인딩할 때 이전 Coil 요청을 해제해 다른 item의 이미지가 뒤늦게 표시되지 않도록 한다.

  • 시작 조건: reviews/phase4-following-ui-routing-review.mdREV-P4-003 확정과 PRD의 blank profile image fallback 정책 확인.
  • 완료 증거: 이전 Coil 요청이 남는 재사용 시나리오 RED → blank 분기에서 요청 해제와 drawable 초기화 GREEN → profile loader focused test와 팔로잉 전체 회귀 PASS.
  • 범위 밖: non-blank 이미지 transformation/placeholder 디자인 변경, Coil 전역 설정 변경, adapter 구조 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeCreatorProfileImageLoader.kt

  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorProfileImageLoaderTest.kt

  • Modify: docs/agent-guides/build-test-style.md

  • RED: Robolectric ImageView에 Coil 요청을 연결한 뒤 blank URL을 바인딩하고, 기존 요청이 해제되지 않는 현재 동작을 coil.result 또는 동등한 요청 상태 assertion으로 재현한다.

  • GREEN: loadHomeCreatorProfileImage()의 blank 분기에서 coil.dispose()로 이전 요청을 해제한 뒤 drawable을 비우는 최소 수정만 적용한다.

  • GREEN 확인: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest"를 실행한다.

  • 회귀 검증: 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행하고 신규 테스트 단일 실행 예시를 빌드 가이드에 반영한다.

검증 기록:

  • 2026-07-30 RED: HomeCreatorProfileImageLoaderTest를 추가해 blank profile URL 바인딩 시 기존 Coil Disposable이 해제되어야 함을 검증했고, 구현 전 assertTrue(disposable.isDisposed) 실패로 현재 요청이 남는 동작을 확인했다.
  • 2026-07-30 GREEN: loadHomeCreatorProfileImage() blank 분기에서 dispose()setImageDrawable(null)을 호출하도록 최소 수정했다. focused test, 팔로잉 전체 회귀와 신규 프로필 테스트, :app:mergeDebugResources, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check PASS. build-test-style.mdHomeCreatorProfileImageLoaderTest 단일 실행 예시를 추가했다.

Task 4.9: 팔로잉 공용 source test 이름 규칙 정합성 보완

Goal 실행 P4-R4: 확정 review 항목 REV-P4-004에 따라 HomeFollowingFragmentSourceTest에 남은 영문 문장형 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.

  • 시작 조건: reviews/phase4-following-ui-routing-review.mdREV-P4-004 확정과 docs/agent-guides/build-test-style.md의 신규 Kotlin 테스트명 규칙 확인.
  • 완료 증거: 영문 문장형 테스트명 11개 한글화 → assertion·production 코드 무변경 확인 → Fragment focused test·팔로잉 전체 회귀·ktlint·diff 검증 PASS.
  • 범위 밖: 테스트 assertion 재설계, production 코드·resource 변경, 다른 테스트 클래스의 기존 이름 일괄 수정.
  • TDD 예외 사유: runtime 동작 결함이 아니라 테스트 이름 규칙 정합성 수정이므로 production RED/GREEN을 만들지 않는다.
  • 대체 검증 방법: 대상 파일의 @Test 함수명을 rg로 확인하고 변경 전후 assertion diff, focused test, 팔로잉 전체 회귀를 대조한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • 완전한 영문 문장으로 남은 backtick 테스트명 11개를 각 검증 의도가 드러나는 한글 문장으로 변경한다.

  • 테스트 본문의 assertion과 helper, production 코드·resource가 변경되지 않았는지 diff로 확인한다.

  • ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"를 실행한다.

  • 팔로잉 전체 회귀, :app:ktlintCheck, git diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-31: HomeFollowingFragmentSourceTest의 완전한 영문 문장형 backtick 테스트명 11개를 검증 의도가 드러나는 한글 문장으로 변경했다. Task 9.6의 신규 한글 테스트 추가 외 기존 assertion/helper와 production/resource 변경은 포함하지 않았다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest", 팔로잉 전체 회귀와 :app:compileDebugKotlin, :app:ktlintCheck PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.

Phase 5: 통합 검증과 문서 기록

  • 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 상태로 남긴다.
      • 2026-07-31 Task 2.7 완료 후 재확인: adb devices는 정상 실행됐지만 List of devices attached 아래 연결 기기가 없어 Figma/API 데이터 기반 실기기 수동 확인은 계속 보류한다.
      • 2026-07-31 Task 4.9/9.6 완료 중 재확인: adb devices에서 2cec640c34017ece 연결을 확인했고 ./gradlew --no-daemon :app:installDebug, adb shell monkey -p kr.co.vividnext.sodalive.debug -c android.intent.category.LAUNCHER 1 PASS. 다만 adb exec-out screencap -p로 저장한 /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sodalive-home-after-launch.png와 5초 대기 후 재촬영한 sodalive-home-after-launch-2.png가 모두 검은 화면으로 분석되어 팔로잉 탭 content와 Figma 24:5682 상세 대조는 완료하지 못했다.

Phase 6: Figma 디자인 재대조 후속 수정

  • 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만 출력됐다.

Task 6.5: Feed 위젯 전환 후 obsolete 최근 소식 layout 제거

Goal 실행 P6-R1: 확정 review 항목 REV-P6-001에 따라 runtime에서 사용하지 않는 팔로잉 전용 news layout과 잘못된 preview 참조를 제거한다.

  • 시작 조건: reviews/phase6-figma-follow-up-review.mdREV-P6-001 확정과 HomeFollowingNewsAdapter의 Feed 위젯 사용 확인.
  • 완료 증거: resource 참조 검색 → preview를 실제 Feed layout으로 교체 → obsolete layout 2개 삭제 → resource merge와 source test PASS.
  • 범위 밖: Feed 위젯 UI 재설계, 최근 소식 DTO/mapper/routing 변경.

Files:

  • Delete: app/src/main/res/layout/item_home_following_news_rank.xml

  • Delete: app/src/main/res/layout/item_home_following_news_content.xml

  • Modify: app/src/main/res/layout/fragment_v2_main_home.xml

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • RED: rg와 source test로 두 obsolete layout이 runtime adapter에서 사용되지 않고 tools:listitem만 과거 layout을 가리키는 상태를 고정한다.

  • GREEN: 최근 소식 preview를 실제 view_feed_content로 교체하고 obsolete layout 2개를 삭제한다.

  • GREEN 확인: ./gradlew --no-daemon :app:mergeDebugResourcesHomeFollowingFragmentSourceTest를 실행한다.

  • 회귀 검증: 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행한다.

검증 기록:

  • 2026-07-30 RED: HomeFollowingFragmentSourceTest가 최근 소식 preview tools:listitem과 obsolete layout 파일 제거를 요구하도록 보강했고, 구현 전 과거 layout 참조/파일 존재로 실패했다.
  • 2026-07-30 GREEN: fragment_v2_main_home.xml의 최근 소식 preview를 @layout/view_feed_content로 교체하고 item_home_following_news_rank.xml, item_home_following_news_content.xml을 삭제했다. :app:mergeDebugResources, focused test, 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check PASS.

Task 6.6: 삭제된 최근 소식 layout의 파일 구조 문서 정합성 보완

Goal 실행 P6-R2: 확정 review 항목 REV-P6-002에 따라 상단 파일 구조가 Feed 위젯 전환 후 현재 resource 구성을 정확히 설명하도록 갱신한다.

  • 시작 조건: reviews/phase6-figma-follow-up-review.mdREV-P6-002 확정과 Task 6.5의 obsolete layout 삭제 완료.
  • 완료 증거: 상단 파일 구조에서 삭제된 두 layout의 Create 항목 제거 또는 삭제 이력 명시 → 실제 Feed layout 참조와 문서 대조 → 문서 diff 검증 PASS.
  • 범위 밖: 삭제된 layout 복원, Feed 위젯 UI·adapter·DTO·routing 변경.
  • TDD 예외 사유: 계획 문서의 현재 파일 목록을 바로잡는 문서 전용 수정이므로 실행 가능한 실패 test를 만들지 않는다.
  • 대체 검증 방법: rg로 삭제된 layout의 runtime 참조와 상단 파일 구조 문구를 대조하고 git diff --check를 실행한다.

Files:

  • Modify: docs/20260625_메인_홈_팔로잉_탭/plan-task.md

  • 상단 파일 구조에서 item_home_following_news_rank.xml, item_home_following_news_content.xml을 현재 생성 파일로 오인하지 않도록 정리한다.

  • HomeFollowingNewsAdapter가 재사용하는 view_feed_rank, view_feed_content, view_feed_community와 Task 6.5 삭제 이력을 문서에서 확인 가능하게 유지한다.

  • rg -n "item_home_following_news_(rank|content)|view_feed_(rank|content|community)" docs/20260625_메인_홈_팔로잉_탭/plan-task.md app/src/main으로 문서와 runtime 참조를 대조한다.

  • git diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-30: 상단 파일 구조의 obsolete 최근 소식 layout 2개를 Create 항목에서 제거하고, runtime Feed layout 3종 재사용 및 Task 6.5 삭제 이력을 명시했다. rg -n "item_home_following_news_(rank|content)|view_feed_(rank|content|community)" docs/20260625_메인_홈_팔로잉_탭/plan-task.md app/src/main 대조 결과 runtime adapter는 view_feed_rank, view_feed_content, view_feed_community를 참조하고 삭제 layout은 문서/이력 참조로만 남는 것을 확인했다. git diff --check PASS.

Task 6.7: 최근 대화 고정 폭 LayoutParams 보존

Goal 실행 P6-R3: 확정 review 항목 REV-P6-003에 따라 최근 대화 item의 XML 284dp 폭을 런타임에서도 유지하면서 기존 item 간격을 보존한다.

  • 시작 조건: reviews/phase6-figma-follow-up-review.mdREV-P6-003 확정.
  • 완료 증거: inflate 직후 284dp width를 WRAP_CONTENT로 교체하는 경로 제거 → 기존 layout parameter에 end margin만 적용 → resource merge·Kotlin compile·팔로잉 회귀 PASS → 연결 기기 준비 시 Task 5.3에서 폭/간격 수동 확인.
  • 범위 밖: 최근 대화 카드의 Figma 크기 변경, 공통 helper의 전체 호출부 리팩터링, 다른 팔로잉 adapter 수정.
  • TDD 예외 사유: docs/agent-guides/code-style.md가 View width·margin 같은 UI 표현 속성 테스트 추가를 금지하므로 새 자동 UI 크기 테스트를 작성하지 않는다.
  • 대체 검증 방법: item_home_following_chat.xml, HomeFollowingChatAdapter.kt, HomeRecyclerItemLayoutParams.kt를 정적 대조하고 resource merge·Kotlin compile·기존 팔로잉 로직 회귀 및 가능한 경우 실기기 수동 확인을 수행한다.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt

  • Verify: app/src/main/res/layout/item_home_following_chat.xml

  • HomeFollowingChatAdapter.onCreateViewHolder()가 inflate된 root의 기존 width/height를 보존하고 end margin만 추가하도록 최소 수정한다.

  • 공통 helper나 다른 adapter를 불필요하게 변경하지 않았는지 diff로 확인한다.

  • ./gradlew --no-daemon :app:mergeDebugResources, :app:compileDebugKotlin, 팔로잉 전체 회귀를 실행한다.

  • 연결 기기가 있으면 Task 5.3에서 최근 대화 카드 폭과 item 간격을 확인하고, 없으면 미실행 사유를 기록한다.

검증 기록:

  • 2026-07-30: HomeFollowingChatAdapter.onCreateViewHolder()에서 recyclerItemLayoutParams(parent) 사용을 제거하고 inflate된 RecyclerView.LayoutParams의 width/height를 유지한 채 spacing_12 end margin만 적용했다. 공통 helper와 다른 adapter는 변경하지 않았다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. adb devices에 연결 기기가 없어 최근 대화 카드 폭/간격 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.

Task 6.8: creator·최근 대화 UI 표현 속성 source test 정합성 보완

Goal 실행 P6-R4: 확정 review 항목 REV-P6-004에 따라 creator·최근 대화의 크기·방향·말줄임 값을 XML 문자열로 고정하는 source assertion을 제거하고 허용된 구조·바인딩 검증만 유지한다.

  • 시작 조건: reviews/phase6-figma-follow-up-review.mdREV-P6-004 확정과 docs/agent-guides/code-style.md 테스트 관례 확인.
  • 완료 증거: 금지된 XML 크기·orientation·style·maxLines·ellipsize assertion 제거 → production XML/adapter 무변경 → focused test·resource merge·Kotlin compile·ktlint PASS → 실기기 가능 시 Task 5.3에서 표현 속성 대조.
  • 범위 밖: creator/chat production UI 값 변경, adapter 데이터 바인딩, 다른 기존 Feed 테스트 일괄 정리.
  • TDD 예외 사유: 동작 결함 수정이 아닌 현행 테스트 정책 위반 assertion 제거이며 production은 변경하지 않는다.
  • 대체 검증 방법: Figma와 production XML/adapter는 정적 대조하고, 자동 검증은 section 존재·adapter view type·필수 field 바인딩·click route와 순수 로직으로 한정한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • Verify: app/src/main/res/layout/item_home_following_creator.xml

  • Verify: app/src/main/res/layout/item_home_following_chat.xml

  • Verify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt

  • creator test에서 75dp width/height와 typography style source assertion을 제거하고 creator header 제거 구조 검증은 유지한다.

  • 최근 대화 test에서 XML orientation, 284dp/62dp width/height, maxLines/ellipsize source assertion을 제거한다.

  • Direct badge 노출 결정, 상대 시간 formatter, 필수 field 바인딩과 adapter click 계약 검증은 유지한다.

  • HomeFollowingFragmentSourceTest, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, git diff --check를 실행하고 실기기 표현 확인 가능 여부를 누적한다.

검증 기록:

  • 2026-07-31: creator source test명을 팔로잉 크리에이터 섹션은 header 없이 item layout을 사용한다로, 최근 대화 source test명을 팔로잉 최근 대화 섹션은 RecyclerView와 adapter를 연결한다, 팔로잉 최근 대화 item은 필수 field와 클릭 계약을 바인딩한다로 한글화했다. creator의 75dp/typography, 최근 대화의 XML orientation, 284dp/62dp, maxLines/ellipsize source assertion은 제거하고 creator header 제거, 필수 field id, Direct badge, 상대 시간 formatter, click listener 계약은 유지했다. rg -n -- "75dp|284dp|62dp|android:orientation|android:maxLines|android:ellipsize|Typography.Body5" HomeFollowingFragmentSourceTest.kt 결과 제거 대상 참조 0건을 확인했다. HomeFollowingFragmentSourceTest, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint PASS. adb devices에는 연결 기기가 없어 실기기 표현 확인은 Task 5.3 보류 상태를 유지한다.

Phase 7: 최근 소식 ranking 문장과 오디오 콘텐츠 feed 정합성 후속 수정

  • 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 파일은 수정하지 않았다.
      • 2026-07-30 재검증: 현재 working tree 기준 ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest", ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:compileDebugKotlin, ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.
  • 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.

Task 7.5: ranking 순위 단위 locale 적용

Goal 실행 P7-R1: 확정 review 항목 REV-P7-001에 따라 ranking 문장의 순위 텍스트를 한국어·영어·일본어 locale에 맞게 표시하고 같은 구간을 highlight한다.

  • 시작 조건: reviews/phase7-ranking-content-feed-review.mdREV-P7-001 확정.
  • 완료 증거: 영어·일본어에서도 "위"가 노출되는 RED → locale string format GREEN → 세 locale와 highlight range 회귀 PASS.
  • 범위 밖: ranking message 문장 자체 재기획, FeedRankView 스타일·색상 변경, API rank 계약 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt

  • Modify: app/src/main/res/values/strings.xml

  • Modify: app/src/main/res/values-en/strings.xml

  • Modify: app/src/main/res/values-ja/strings.xml

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • Test: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt

  • Modify: docs/agent-guides/build-test-style.md

  • RED: locale별 ranking message를 생성해 영어·일본어 결과에 한국어 "위"가 포함되는 현재 문제를 재현한다.

  • GREEN: 순위 단위를 locale string resource로 분리해 한국어 , 영어 locale 표기, 일본어 를 사용한다.

  • highlight 검증: locale별 완성 문장에서 순위 문자열 index가 유효하고 FeedRankHighlight 범위가 해당 문자열과 일치하는지 검증한다.

  • 회귀 검증: focused test, Feed view test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, git diff --check를 실행한다.

검증 기록:

  • 2026-07-30 RED: HomeFollowingNewsAdapterTest를 추가해 한국어/영어/일본어 locale별 ranking message와 highlight range를 검증했고, 구현 전 buildHomeFollowingRankingFeedItem 및 locale rank format 부재로 focused test가 실패했다.
  • 2026-07-30 GREEN: screen_home_following_ranking_rank_format을 3개 locale에 추가하고 ranking feed item 생성 helper에서 locale rank 문자열을 만든 뒤 같은 문자열 범위를 FeedRankHighlight로 전달하도록 변경했다. 한국어 7위, 영어 No. 7, 일본어 7位 focused test, Feed view test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, git diff --check PASS.
  • 2026-07-30 리뷰 게이트 후 보완: subject에 같은 rank 문자열이 먼저 포함될 때 highlight가 잘못 잡힐 수 있다는 Minor 지적을 받아 lastIndexOf(rankText)로 삽입된 순위 문자열을 강조하도록 보완했다. 중복 문자열 fixture RED 확인 후 HomeFollowingNewsAdapterTest, focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, git diff --check PASS.

Task 7.6: 신규 ranking 테스트와 실행 가이드 정합성 보완

Goal 실행 P7-R2: 확정 review 항목 REV-P7-002에 따라 신규 ranking 단위 테스트가 저장소의 한글 테스트명과 단일 실행 가이드 규칙을 충족하도록 한다.

  • 시작 조건: reviews/phase7-ranking-content-feed-review.mdREV-P7-002 확정과 HomeFollowingNewsAdapterTest 추가 상태.
  • 완료 증거: 신규 test name 한글화 → build-test-style.md 단일 실행 예시 추가 → focused test·ktlint·문서 diff PASS.
  • 범위 밖: ranking message·highlight production 로직, locale 문구, 기존 영어 테스트명의 일괄 변경.
  • TDD 예외 사유: 동작 결함이 아니라 테스트/가이드 규칙 정합성 수정이므로 production RED를 만들지 않는다.
  • 대체 검증 방법: 테스트 메서드명과 가이드 명령을 rg로 확인하고 focused test와 ktlint를 실행한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt

  • Modify: docs/agent-guides/build-test-style.md

  • HomeFollowingNewsAdapterTest의 신규 backtick 테스트명 2개를 검증 의도가 드러나는 한글 문장으로 변경한다.

  • docs/agent-guides/build-test-style.mdHomeFollowingNewsAdapterTest 클래스 단위 실행 예시를 추가한다.

  • ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"를 실행한다.

  • ./gradlew --no-daemon :app:ktlintCheckgit diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-30: HomeFollowingNewsAdapterTest의 신규 ranking 테스트명 2개를 한글 문장으로 변경하고 build-test-style.md에 클래스 단위 실행 예시를 추가했다. rg로 한글 테스트명과 가이드 예시를 확인했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest", ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.

Task 7.7: content feed UI 표현 속성 source test 정책 정합성 보완

Goal 실행 P7-R3: 확정 review 항목 REV-P7-003에 따라 가이드 제정 이후 추가된 content feed 크기·padding·margin source test를 제거하고 허용된 대체 검증으로 전환한다.

  • 시작 조건: reviews/phase7-ranking-content-feed-review.mdREV-P7-003 확정.
  • 완료 증거: content layout matches figma compact upload notice dimensions 테스트 제거 → production layout/View 코드는 유지 → Feed/팔로잉 회귀·resource merge·Kotlin compile·ktlint PASS.
  • 범위 밖: view_feed_content.xml의 88dp/간격 값 변경, FeedContentView clipping 정책 변경, 다른 기존 Feed 테스트 일괄 정리.
  • TDD 예외 사유: 동작 결함 수정이 아니라 금지된 UI 표현 속성 테스트 제거이므로 RED/GREEN production 변경을 만들지 않는다.
  • 대체 검증 방법: Figma 기준과 view_feed_content.xml/FeedContentView.kt를 정적 대조하고 resource merge·compile 및 Task 5.3 수동 화면 확인을 사용한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedViewTest.kt

  • content layout matches figma compact upload notice dimensions 테스트만 제거하고 production 파일을 변경하지 않는다.

  • FeedViewTest의 로직·접근성·clipping 계약 테스트는 이번 범위에서 유지한다.

  • Feed focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint를 실행한다.

  • Figma 크기/간격 대조 결과와 실기기 확인 가능 여부를 검증 기록에 남긴다.

검증 기록:

  • 2026-07-30: FeedViewTest에서 content feed 크기·padding·margin 문자열을 고정하던 content layout matches figma compact upload notice dimensions 테스트만 제거했다. view_feed_content.xmlFeedContentView.kt production 코드는 변경하지 않았고, content meta row·clipping·접근성 테스트는 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. Figma 크기/간격은 기존 production XML/View 정적 대조로 유지하며, adb devices에 연결 기기가 없어 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.

Phase 8: 팔로잉 크리에이터 전체 버튼 후속 추가

  • 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만 출력됐다.

Task 8.4: 팔로잉 크리에이터 전체 item 테스트 정책 정합성 보완

Goal 실행 P8-R1: 확정 review 항목 REV-P8-001에 따라 adapter의 마지막 전체 item과 이동 로직 검증은 유지하고, 금지된 width·height·padding·visibility source assertion만 제거한다.

  • 시작 조건: reviews/phase8-following-creators-all-review.mdREV-P8-001 확정.
  • 완료 증거: UI 표현 속성 assertion 제거 및 남은 테스트명 한글화 → adapter view type/개수/route 검증 유지 → focused test·resource merge·Kotlin compile·ktlint PASS.
  • 범위 밖: item_home_following_creator_all.xml 디자인 값 변경, adapter 동작 변경, 다른 기존 영문 테스트명 일괄 변경.
  • TDD 예외 사유: production 동작 결함이 아니라 테스트 정책 정합성 수정이므로 production RED를 만들지 않는다.
  • 대체 검증 방법: XML 토큰은 review 시 정적 대조하고 실제 높이·padding·색상은 resource merge와 Task 5.3 수동 화면 확인으로 검증한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • following creators list appends all button as last item에서 layout width·height·padding·visibility·색상 문자열 assertion만 제거한다.

  • adapter의 creator/all view type, item count, 마지막 item, FollowingCreatorActivity route assertion은 유지한다.

  • 수정하는 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.

  • focused test, resource merge, Kotlin compile, ktlint를 실행하고 대체 검증 결과를 누적한다.

검증 기록:

  • 2026-07-30: following creators list appends all button as last item 테스트명을 팔로잉 크리에이터 목록은 전체 item을 마지막에 추가하고 이동을 연결한다로 한글화하고, width/height/padding/visibility/color source assertion만 제거했다. adapter view type, item count, 마지막 all item, FollowingCreatorActivity route, all label string 검증은 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다.

Phase 9: 최근 소식 Response nested payload 계약 반영

  • 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만 출력됐다.

Task 9.5: nullable 최근 소식 이미지의 이전 Coil 요청 해제

Goal 실행 P9-R1: 확정 review 항목 REV-P9-001에 따라 nullable/blank 최근 소식 이미지를 바인딩할 때 재사용된 ImageView의 이전 Coil 요청을 해제해 다른 news item 이미지가 노출되지 않도록 한다.

  • 시작 조건: reviews/phase9-news-nested-payload-review.mdREV-P9-001 확정과 contentImageUrl, imageUrl nullable 계약 확인.
  • 완료 증거: 최근 소식 ViewHolder 재사용 시 이전 요청이 남는 RED → 공통 blank image 분기 요청 해제 GREEN → adapter focused test와 Phase 9 회귀 PASS.
  • 범위 밖: nullable image의 placeholder/숨김 디자인 변경, Feed 위젯 레이아웃 변경, DTO·mapper·routing 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingNewsAdapterTest.kt

  • RED: 완료된 Coil 요청이 연결된 최근 소식 image view를 nullable image item에 재사용한 뒤 요청이 남는 현재 동작을 Robolectric adapter binding test로 재현한다.

  • GREEN: HomeFollowingNewsAdapter.bindImage()의 blank 분기에서 coil.dispose()로 이전 요청을 해제한 뒤 drawable을 비우는 최소 수정만 적용한다.

  • GREEN 확인: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest"를 실행한다.

  • 회귀 검증: mapper와 팔로잉 전체 회귀, :app:mergeDebugResources, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-30 RED: HomeFollowingNewsAdapterTest에 nullable content image ViewHolder 재사용 테스트를 추가했고, 구현 전 assertTrue(disposable.isDisposed) 실패로 이전 Coil 요청이 남는 동작을 확인했다.
  • 2026-07-30 GREEN: HomeFollowingNewsAdapter.bindImage() blank 분기에서 imageView.dispose() 후 drawable을 비우도록 최소 수정했다. focused test, 팔로잉 전체 회귀와 신규 프로필 테스트, :app:mergeDebugResources, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.

Task 9.6: 최근 소식 Feed model의 미제공 creator ID 합성 제거

Goal 실행 P9-R2: 확정 review 항목 REV-P9-002에 따라 API payload에 없는 creatorIdcontentId 또는 postId로 대신 채우지 않고 미제공 값으로 유지한다.

  • 시작 조건: reviews/phase9-news-nested-payload-review.mdREV-P9-002 확정과 FollowingContentNewsResponse, FollowingCommunityPostNewsResponsecreatorId가 없음을 확인.
  • 완료 증거: 잘못된 ID 대입을 고정한 RED → 두 FeedItemcreatorId를 빈 값으로 전달하는 GREEN → 최근 소식 adapter focused test·팔로잉 전체 회귀·Kotlin compile·ktlint PASS.
  • 범위 밖: 서버 DTO에 creatorId 추정 추가, API 스키마 변경, 최근 소식 클릭 routing·Feed 공통 model 구조 변경.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • RED: ContentCommunity binding이 API에 없는 creatorId를 각각 contentId·postId로 합성하지 않도록 요구하는 한글명 회귀 테스트를 추가하고 현재 실패를 확인한다.

  • GREEN:FeedItemcreatorId에 빈 문자열을 전달하고 원본 contentId·postId, creator 이름·이미지, 기존 click callback은 유지한다.

  • GREEN 확인: HomeFollowingFragmentSourceTestHomeFollowingNewsAdapterTest를 실행한다.

  • 회귀 검증: 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-31 RED: HomeFollowingFragmentSourceTest팔로잉 최근 소식 feed model은 미제공 creator id를 합성하지 않는다를 추가했고, 구현 전 creatorId = community.targetId.toString()/creatorId = content.targetId.toString() 때문에 focused test가 17개 중 1개 실패했다.
  • 2026-07-31 GREEN: HomeFollowingNewsAdapterFeedItem.CommunityFeedItem.Content에서 creatorId = ""를 전달하도록 변경했다. postId = community.postId.toString(), contentId = content.contentId.toString(), creator 이름·이미지와 원본 item click callback은 유지했다. rg로 잘못된 targetId 대입 0건과 빈 creatorId 2건, content/post ID 보존을 확인했다. focused test, 팔로잉 전체 회귀, :app:compileDebugKotlin, :app:ktlintCheck PASS. ./gradlew --no-daemon :app:installDebug와 launcher monkey 실행도 PASS했으나, 화면 캡처가 검은 화면이라 실제 팔로잉 탭 터치 대조는 Task 5.3에 미완료로 유지한다.

Task 9.7: 미사용 FeedAdapter 제거와 현재 문서 정리

Goal 실행 P9-R3: 현재 production/test 호출자가 없는 FeedAdapterFeedImageViews를 제거하고, 팔로잉 문서의 재사용 후보 설명을 실제 사용 중인 Feed view/model 기준으로 정리한다.

  • 시작 조건: FeedAdapter/FeedImageViews production/test 호출자 0건 확인.
  • 완료 증거: FeedAdapter.kt 삭제 → 현재 팔로잉 PRD/계획의 FeedAdapter 참조 제거 → Kotlin compile·Feed/팔로잉 회귀·diff 검증 PASS.
  • 범위 밖: 과거 완료 문서의 생성 이력 수정, FeedRankView/FeedContentView/FeedCommunityView/FeedItem 구조 변경, 신규 공용 adapter 재작성.
  • TDD 예외 사유: 실행 동작 추가가 아니라 호출자 0건인 미사용 Kotlin 파일 삭제와 문서 정리이므로 신규 RED 테스트를 만들지 않는다.
  • 대체 검증 방법: codegraph_callersrg로 호출자/참조 0건을 확인하고 Kotlin compile, 관련 unit/source test, git diff --check를 실행한다.

Files:

  • Delete: app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedAdapter.kt

  • Modify: docs/20260625_메인_홈_팔로잉_탭/plan-task.md

  • Modify: docs/20260625_메인_홈_팔로잉_탭/prd.md

  • codegraph_callersrg -n "FeedAdapter|FeedImageViews" app/src/main/java app/src/test/java docs/20260625_메인_홈_팔로잉_탭 docs/agent-guides로 production/test 호출자 0건과 현재 문서 참조만 남았는지 확인한다.

  • FeedAdapter.kt를 삭제한다.

  • 현재 팔로잉 PRD/계획 문서에서 FeedAdapter 재사용 후보 문구를 제거하고 실제 재사용 중인 FeedItem, FeedRankView, FeedContentView, FeedCommunityView 기준으로 정리한다.

  • ./gradlew --no-daemon :app:compileDebugKotlin, ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:ktlintCheck, git diff --check를 실행하고 결과를 누적한다.

검증 기록:

  • 2026-07-31: codegraph_callers 결과 FeedAdapter 호출자 0건을 확인했고, rg -n "FeedAdapter|FeedImageViews" app/src/main/java app/src/test/java docs/20260625_메인_홈_팔로잉_탭 docs/agent-guides 결과 production/test 참조 없이 Task 9.7 문서 참조만 남는 것을 확인했다. app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedAdapter.kt를 삭제하고 현재 팔로잉 PRD/계획 문서의 재사용 후보를 FeedItem, FeedRankView, FeedContentView, FeedCommunityView 기준으로 정리했다. ./gradlew --no-daemon :app:compileDebugKotlin :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" :app:ktlintCheck, git diff --check PASS.

Phase 10: On Air 라이브 item Figma 재구현

  • Task 10.1: Figma 기준 레이아웃 재구현
    • 수정:
      • app/src/main/res/layout/item_home_following_live.xml
    • 생성:
      • app/src/main/res/drawable/bg_home_following_live_capsule.xml
    • 작업:
      • Figma 24:5696 기준으로 item_home_following_live.xml을 썸네일 카드가 아닌 캡슐형 On Air item으로 재구현한다.
      • 기존 HomeFollowingLiveAdapter가 사용하는 표시 id(iv_home_following_live_creator_profile, tv_home_following_live_title, tv_home_following_live_creator_nickname, tv_home_following_live_started_at)는 유지한다.
      • 카드 배경은 gray_900, stroke는 color_3bb9f1, radius는 90dp, 전체 크기는 Figma의 263dp x 100dp 기준으로 맞춘다.
      • LIVE badge, 시작 시간, 제목, 크리에이터명을 Figma 순서와 간격에 맞게 배치한다.
    • 검증:
      • Run: ./gradlew --no-daemon :app:mergeDebugResources
      • Expected: layout/drawable resource merge PASS.
      • Run: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"
      • Expected: 기존 팔로잉 source/unit test PASS.
    • 검증 기록:
      • 2026-07-03 Figma 24:5696 기준으로 item_home_following_live.xml을 기존 썸네일 카드에서 263dp x 100dp 캡슐형 On Air item으로 재구현했다. 기존 adapter id 계약은 유지했고, profile image는 기존 loadHomeCreatorProfileImage()CircleCropTransformation 경로를 그대로 사용한다.
      • 2026-07-03 검증: ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", git diff --check PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
      • 2026-07-03 후속 정리: Figma 캡슐형 item에 필요 없는 iv_home_following_live_thumbnail 숨김 View와 HomeFollowingLiveAdapter의 obsolete thumbnail binding을 제거했다.

Task 10.2: On Air 가로 item 간격 복구

Goal 실행 P10-R1: 확정 review 항목 REV-P10-001에 따라 연속된 On Air 캡슐 item 사이에 기존 홈 가로 리스트와 동일한 간격을 적용한다.

  • 시작 조건: reviews/phase10-on-air-live-item-review.mdREV-P10-001 확정.
  • 완료 증거: item layout parameter에 end margin이 없는 RED → 기존 recyclerItemLayoutParams(parent) 재사용 GREEN → resource/source/팔로잉 회귀 PASS.
  • 범위 밖: 캡슐 크기·색상·내부 spacing 변경, RecyclerView padding 변경, 신규 ItemDecoration 추상화.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • RED: HomeFollowingLiveAdapter.onCreateViewHolder()가 item end margin을 적용하지 않는 현재 상태를 실패 테스트로 고정한다.

  • GREEN: creator/chat adapter와 같은 recyclerItemLayoutParams(parent)를 재사용해 XML 크기를 유지하면서 spacing_12 end margin을 적용한다.

  • GREEN 확인: HomeFollowingFragmentSourceTest와 팔로잉 전체 회귀 테스트를 실행한다.

  • 회귀 검증: resource merge, Kotlin compile, ktlint, git diff --check를 실행하고 결과를 누적한다.

검증 기록:

  • 2026-07-30 RED: HomeFollowingFragmentSourceTest.following live adapter keeps horizontal item spacing을 추가해 live adapter의 recyclerItemLayoutParams(parent) 재사용을 요구했고, 구현 전 helper 미사용으로 focused test가 실패했다.
  • 2026-07-30 GREEN: HomeFollowingLiveAdapter.onCreateViewHolder()에서 inflate한 view에 recyclerItemLayoutParams(parent)를 적용해 기존 XML 크기와 spacing_12 end margin을 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint, git diff --check PASS.

Task 10.3: On Air 간격 검증의 테스트 정책 정합성 보완

Goal 실행 P10-R2: 확정 review 항목 REV-P10-002에 따라 UI margin을 source 문자열로 고정한 신규 테스트를 제거하고 가이드가 허용하는 대체 검증 기록으로 전환한다.

  • 시작 조건: reviews/phase10-on-air-live-item-review.mdREV-P10-002 확정과 Task 10.2 production 수정 완료.
  • 완료 증거: 금지된 UI 표현 속성 source test만 제거 → adapter helper 사용 정적 대조 → resource merge·Kotlin compile·팔로잉 회귀 PASS.
  • 범위 밖: HomeFollowingLiveAdapterrecyclerItemLayoutParams(parent) 적용 제거, item 크기·간격 변경, 기존 source test 일괄 정리.
  • TDD 예외 사유: docs/agent-guides/code-style.md가 View margin 같은 UI 표현 속성 테스트를 금지하므로 실행 명령과 정적 대조를 대체 검증으로 사용한다.
  • 대체 검증 방법: adapter의 기존 helper 재사용을 rg로 확인하고 resource merge, Kotlin compile, 기존 팔로잉 로직 회귀를 실행한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • 신규 following live adapter keeps horizontal item spacing source test만 제거하고 Task 10.2 production 코드는 유지한다.

  • rg -n "recyclerItemLayoutParams\(parent\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt로 기존 helper 재사용을 확인한다.

  • ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", :app:mergeDebugResources, :app:compileDebugKotlin을 실행한다.

  • ./gradlew --no-daemon :app:ktlintCheckgit diff --check를 실행하고 결과를 이 Task 아래에 누적한다.

검증 기록:

  • 2026-07-30: UI margin을 source 문자열로 고정하던 following live adapter keeps horizontal item spacing 테스트만 제거하고 HomeFollowingLiveAdapterrecyclerItemLayoutParams(parent) 적용은 유지했다. rg -n "recyclerItemLayoutParams\(parent\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt로 helper 재사용을 확인했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:compileDebugKotlin, ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.

Task 10.4: On Air 고정 폭 LayoutParams 보존

Goal 실행 P10-R3: 확정 review 항목 REV-P10-003에 따라 On Air item의 XML 263dp 폭을 런타임에서도 유지하면서 spacing_12 end margin을 적용한다.

  • 시작 조건: reviews/phase10-on-air-live-item-review.mdREV-P10-003 확정.
  • 완료 증거: RecyclerView.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) 교체 경로 제거 → inflate된 width/height를 유지한 채 end margin만 적용 → resource merge·Kotlin compile·팔로잉 회귀 PASS → 연결 기기 준비 시 Task 5.3 수동 확인.
  • 범위 밖: 263dp × 100dp 디자인 변경, 공통 helper 전체 리팩터링, On Air 내부 필드/라우팅 변경.
  • TDD 예외 사유: docs/agent-guides/code-style.md가 View width·margin 테스트 추가를 금지하므로 새 자동 UI 크기 테스트를 작성하지 않는다.
  • 대체 검증 방법: inflate 후 기존 layoutParams 보존 여부를 코드로 대조하고 resource merge·compile·기존 팔로잉 회귀 및 가능한 경우 실기기 수동 확인을 수행한다.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt

  • Verify: app/src/main/res/layout/item_home_following_live.xml

  • HomeFollowingLiveAdapter.onCreateViewHolder()가 inflate된 root의 기존 263dp × 100dp layout parameter를 보존하고 end margin만 추가하도록 최소 수정한다.

  • HomeFollowingFragmentSourceTest에 width·margin source assertion을 다시 추가하지 않는다.

  • 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint를 실행한다.

  • 연결 기기가 있으면 연속 On Air item의 폭과 간격을 수동 확인하고, 없으면 Task 5.3과 함께 사유를 기록한다.

검증 기록:

  • 2026-07-30: HomeFollowingLiveAdapter.onCreateViewHolder()에서 recyclerItemLayoutParams(parent) 사용을 제거하고 inflate된 RecyclerView.LayoutParams의 263dp × 100dp 계약을 유지한 채 spacing_12 end margin만 적용했다. width·margin source assertion은 다시 추가하지 않았다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. adb devices에 연결 기기가 없어 On Air 폭/간격 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.

Phase 11: 최근 소식 COMMUNITY_POST 상세 이동

  • 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만 출력됐다.

Task 12.4: empty 상태 UI 표현 속성 source test 정책 정합성 보완

Goal 실행 P12-R1: 확정 review 항목 REV-P12-001에 따라 empty 상태의 mapper/문구/분기 검증은 유지하고, 금지된 layout 크기·정렬·visibility 문자열 assertion을 제거한다.

  • 시작 조건: reviews/phase12-empty-state-review.mdREV-P12-001 확정.
  • 완료 증거: UI 표현 속성 assertion 제거 및 남은 테스트명 한글화 → mapper Empty 조건과 locale 문구/상태 분기 검증 유지 → focused test·resource merge·Kotlin compile·ktlint PASS.
  • 범위 밖: empty 문구/중앙 배치 production 변경, 로그인 유도 UI, 다른 기존 source test 일괄 정리.
  • TDD 예외 사유: production 동작이 아니라 테스트 정책 정합성을 수정하므로 production RED를 만들지 않는다.
  • 대체 검증 방법: 중앙 배치는 fragment_v2_main_home.xml 정적 대조와 Task 5.3 실기기 확인으로 검증하고, Empty 판정은 HomeFollowingMapperTest로 유지한다.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt

  • empty 테스트에서 width·height·gravity·textAlignment·visibility와 직접 View.VISIBLE/GONE 문자열 assertion을 제거한다.

  • 3개 locale 문구 계약, HomeFollowingUiState.Empty 분기, 기존 mapper Empty 테스트는 유지한다.

  • 수정하는 테스트명을 검증 의도가 드러나는 한글 문장으로 변경한다.

  • focused test, resource merge, Kotlin compile, ktlint를 실행하고 Task 5.3 수동 확인 가능 여부를 기록한다.

검증 기록:

  • 2026-07-30: empty source test명을 팔로잉 empty 상태는 다국어 빈 문구와 상태 분기를 유지한다로 한글화하고, width/height/gravity/textAlignment/visibility 및 직접 View.VISIBLE/GONE 문자열 assertion을 제거했다. 3개 locale 문구, empty string 재사용, HomeFollowingUiState.Empty/Error 상태 분기 검증은 유지했다. focused test, 팔로잉 전체 회귀, resource merge, Kotlin compile, ktlint가 PASS했다. adb devices에 연결 기기가 없어 중앙 배치 실기기 확인은 Task 5.3 보류 사유와 함께 유지한다.

Phase 13: 최근 소식 CREATOR_RANKING·AUDIO_CONTENT 터치 이동

Phase 결과: 팔로잉 탭 최근 소식에서 크리에이터 순위 item은 해당 크리에이터 채널로, 오디오 콘텐츠 업로드 item은 해당 오디오 콘텐츠 상세로 이동한다.

선행조건: Phase 11의 기존 COMMUNITY_POST 상세 이동과 공통 Creator/Content Action 경로가 유지되어야 한다.

Phase 완료 조건: CREATOR_RANKING, AUDIO_CONTENT, 기존 COMMUNITY_POST 이동 source test와 팔로잉 회귀 테스트, Kotlin 컴파일이 통과한다.

Task 13.1 최근 소식 이동 RED 테스트 추가

Goal 실행 P13-T1: 현재 실행되지 않는 CREATOR_RANKINGAUDIO_CONTENT 터치 이동을 실패 source test로 재현한다.

  • 시작 조건: PRD의 2026-07-30 CREATOR_RANKING·AUDIO_CONTENT 상세 이동 Requirements 확정.
  • 완료 증거: 구현 전 focused test가 새 이동 호출 부재로 실패한다.
  • 범위 밖: CONTENT_RANKING, PHOTO_CONTENT, On Air, 스케줄 터치 이동.

Files:

  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt

  • RED: onFollowingNewsClick()CREATOR_RANKINGCreatorActionCommand.Profile로, AUDIO_CONTENTContentActionCommand.AudioDetail로 전달하는지 검증하는 source test를 추가한다.

  • RED 확인: ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest"를 실행해 두 공통 액션 호출 부재로 인한 assertion 실패를 확인한다.

  • GREEN: Task 13.2의 최소 구현으로 RED를 통과시킨다.

  • GREEN 확인: 같은 focused test를 다시 실행해 성공을 확인한다.

  • REFACTOR: 새 abstraction 없이 기존 공통 액션을 재사용하고 focused test를 다시 실행한다.

검증 기록:

  • 2026-07-30 RED: focused test를 실행해 11개 중 COMMUNITY_POST 분기 구조, CREATOR_RANKING 채널 이동, AUDIO_CONTENT 상세 이동 3개가 assertion 실패하는 것을 확인했다. 현재 production이 HomeFollowingNewsUiItem.Community 이외의 item을 즉시 반환해 요청 동작이 없는 것이 실패 원인이다.
  • 2026-07-30 GREEN: 동일 focused test를 다시 실행해 11개 모두 PASS했다. 새 abstraction이나 직접 Intent 조립 없이 기존 Creator/Content/Community Action 호출을 검증했다.

Task 13.2 CREATOR_RANKING·AUDIO_CONTENT 이동 구현

Goal 실행 P13-T2: 요청된 두 최근 소식 타입만 기존 공통 액션 경로로 이동시킨다.

  • 시작 조건: P13-T1 RED 확인.
  • 완료 증거: focused test와 Kotlin 컴파일 통과.
  • 범위 밖: API/DTO/mapper/UI 변경, 직접 Intent 조립, 다른 최근 소식 타입의 신규 이동.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt

  • RED: Task 13.1의 실패 source test를 기준으로 한다.

  • RED 확인: Task 13.1 실행 결과를 확인한다.

  • GREEN: onFollowingNewsClick()에서 CREATOR_RANKINGCreatorActionCommand.Profile(item.targetId), AUDIO_CONTENTContentActionCommand.AudioDetail(item.contentId)를 호출하고 기존 COMMUNITY_POST 분기를 유지한다.

  • GREEN 확인: focused test와 ./gradlew --no-daemon :app:compileDebugKotlin을 실행해 성공을 확인한다.

  • REFACTOR: CONTENT_RANKINGPHOTO_CONTENT는 no-op으로 유지하고 이번 변경이 만든 중복만 점검한다.

검증 기록:

  • 2026-07-30: HomeMainFragment.onFollowingNewsClick()을 sealed UI item 분기로 변경했다. CREATOR_RANKINGCreatorActionCommand.Profile(item.targetId), AUDIO_CONTENTContentActionCommand.AudioDetail(item.contentId)로 전달하며 기존 COMMUNITY_POSTpostId 검증과 상세 이동을 유지했다. focused test와 :app:compileDebugKotlin이 PASS했다.

Task 13.3 최근 소식 터치 이동 회귀 검증

Goal 실행 P13-GATE: 요청된 이동과 기존 팔로잉 동작에 회귀가 없는지 판정한다.

  • 시작 조건: P13-T1, P13-T2 완료.

  • 완료 증거: 아래 자동 검증 결과와 수동 검증 가능 여부를 기록한다.

  • 범위 밖: 연결 기기 없는 환경에서 수동 검증을 성공으로 추정하는 행위.

  • ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" PASS.

  • ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" PASS.

  • ./gradlew --no-daemon :app:compileDebugKotlin PASS.

  • ./gradlew --no-daemon :app:ktlintCheck PASS 또는 이번 변경과 무관한 기존 위반만 확인.

  • git diff --check PASS.

  • 연결 기기가 있으면 CREATOR_RANKINGAUDIO_CONTENT item 터치 목적지를 수동 확인하고, 없으면 미실행 사유를 기록한다.

검증 기록:

  • 2026-07-30: focused test, 팔로잉 전체 회귀 테스트, Kotlin 컴파일, ktlint, git diff --check가 모두 PASS했다. ktlint에는 기존 .editorconfigdisabled_rules deprecation 경고만 출력됐다. adb devices 결과 연결된 기기가 없어 실제 item 터치 수동 검증은 실행하지 못했다.

Verification Log

  • 2026-07-31 이번 Phase 1~13 리뷰 재검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 대조했다. 새 확정 발견 사항은 없고 기존 확정 항목은 모두 각 회귀 수정 Task에서 수정 완료 상태다. Phase 9 보고서 범위를 Task 9.7까지 확장해 production/test FeedAdapter|FeedImageViews 호출자 0건과 현재 Feed view/model 직접 재사용을 기록했다. ./gradlew --no-daemon :app:testDebugUnitTest ... :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 16 suites·120 tests, failures/errors/skipped 0, 46 actionable tasks), ./gradlew --no-daemon tasks --all, staged/unstaged git diff --check PASS. adb devices에서 기기 2cec640c34017ece 연결은 확인했지만 테스트 계정·API 데이터 기반 팔로잉 화면 조작과 Figma 대조는 수행하지 않아 REV-P5-001 / Task 5.3 보류를 유지한다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다.
  • 2026-07-31 Phase 1~13 최종 리뷰 검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 REV-P4-004(공용 source test의 영문 문장형 테스트명 11개)와 REV-P9-002(API가 제공하지 않는 creator ID 자리에 content/post ID를 합성)이며, 코드 수정 전에 Phase 4 Task 4.9 / P4-R4, Phase 9 Task 9.6 / P9-R2로 전환했다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다. ./gradlew --no-daemon :app:testDebugUnitTest ... :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 20 suites·306 tests, failures/errors/skipped 0, 46 actionable tasks), ./gradlew --no-daemon tasks --all, staged/unstaged git diff --check도 PASS했다. adb devices는 정상 실행됐으나 연결 기기가 없어 REV-P5-001 / Task 5.3 실기기 수동 확인 보류를 유지한다.
  • 2026-07-31 리뷰 보완 Task 2.8, 6.8 완료 검증: REV-P2-002에 따라 미사용 HomeFollowingNewsUiItem.Content.labelResId와 전용 FollowingNewsType.toLabelResId()를 제거하고, 실제 표시 경로인 FeedContentCategory.Photo label 회귀를 HomeFollowingNewsAdapterTest에 유지했다. REV-P6-004에 따라 creator·최근 대화 source test의 UI 표현 속성 문자열 assertion만 제거하고 구조·필수 field·Direct badge·상대 시간 formatter·click 계약 검증은 유지했다. focused test 3개, 팔로잉 전체 회귀, ./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다. adb devices에는 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
  • 2026-07-31 Phase 1~13 리뷰 재점검: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 REV-P2-002(최근 소식 Content.labelResId와 전용 mapper가 production 표시에서 미사용)와 REV-P6-004(creator·최근 대화 UI 표현 속성을 검증하는 source test가 현재 코드 스타일 정책과 불일치)이며, 구현 전에 Phase 2 Task 2.8 / P2-R2, Phase 6 Task 6.8 / P6-R4로 전환했다. 이번 리뷰에서는 production/test/resource 코드를 수정하지 않았다. ./gradlew --no-daemon :app:testDebugUnitTest --tests 'kr.co.vividnext.sodalive.v2.main.home.*Following*' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest' --tests 'kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest' --tests 'kr.co.vividnext.sodalive.v2.live.action.*' --tests 'kr.co.vividnext.sodalive.v2.content.action.*' --tests 'kr.co.vividnext.sodalive.v2.community.action.*' :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 15 suites·113 tests, failures/errors 0, 46 actionable tasks). ./gradlew --no-daemon tasks --all, staged/unstaged git diff --check도 PASS했다. adb devices에는 연결 기기가 없어 Phase 5 Task 5.3 실기기 수동 확인 보류를 유지한다.
  • 2026-07-31 리뷰 보완 Task 2.7 완료 검증: REV-P2-001에 따라 미사용 팔로잉 string resource screen_home_following_creators_title, screen_home_following_on_air_title, screen_home_following_error를 3개 locale에서 제거했다. 선언 외 참조 0건을 rg로 확인했고 사용 중인 팔로잉 문자열은 유지했다. ./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin, ./gradlew --no-daemon --rerun-tasks :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest", ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. 첫 팔로잉 전체 회귀는 R.string id 변경 증분 캐시로 mapper test 2건이 실패했으나 --rerun-tasks 재빌드 후 focused와 전체 회귀가 PASS했다. adb devices에 연결된 기기가 없어 Phase 5 Task 5.3 실기기 수동 확인은 계속 보류한다.
  • 2026-07-30 Phase 1~13 최종 리뷰 재점검: 현재 working tree의 PRD·계획·production/test 코드·Phase별 리뷰 보고서 13개를 다시 대조했다. 신규 확정 항목은 REV-P2-001(후속 UI·오류 처리 변경 후 3개 locale에 미사용 string resource 3개가 남음) 1건이며 Phase 2 Task 2.7 / P2-R1로 전환했다. 이번 리뷰에서 production/test/resource 코드는 수정하지 않았다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.live.action.*" --tests "kr.co.vividnext.sodalive.v2.content.action.*" --tests "kr.co.vividnext.sodalive.v2.creator.action.*" --tests "kr.co.vividnext.sodalive.v2.community.action.*" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 119 tests, failures/errors 0, 46 actionable tasks), ./gradlew --no-daemon tasks --all, staged/unstaged git diff --check PASS. adb devices는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증 보류를 유지한다.
  • 2026-07-30 리뷰 보완 Task 4.8, 9.5 완료 검증: blank profile image와 nullable 최근 소식 image 바인딩 시 재사용된 ImageView의 이전 Coil 요청을 dispose()로 해제하도록 보완했다. 각 focused test는 구현 전 disposable.isDisposed assertion 실패로 RED를 확인했고, 최소 구현 후 GREEN으로 전환했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeCreatorProfileImageLoaderTest", ./gradlew --no-daemon :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다.
  • 2026-07-30 Phase 1~13 리뷰 최종 재검증: 현재 working tree의 PRD·계획·production/test 코드와 Phase별 리뷰 보고서 13개를 대조했다. 신규 확정 항목은 REV-P4-003(blank profile image binding의 이전 Coil 요청 해제 누락)과 REV-P9-001(nullable 최근 소식 image binding의 이전 Coil 요청 해제 누락)이며, 구현 전에 Phase 4 Task 4.8 / P4-R3, Phase 9 Task 9.5 / P9-R1로 전환했다. 이미 완료된 REV-P6-003 / Task 6.7은 Phase 6 보고서의 상태와 수정 검증 기록을 현재 코드에 맞게 정정했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" --tests "kr.co.vividnext.sodalive.v2.live.action.*" --tests "kr.co.vividnext.sodalive.v2.content.action.*" --tests "kr.co.vividnext.sodalive.v2.community.action.*" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 111 tests, failures/errors 0, 46 actionable tasks), ./gradlew --no-daemon tasks --all PASS, staged/unstaged git diff --check PASS. adb devices는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증 보류를 유지한다. 이번 리뷰에서는 production/test 코드를 수정하지 않았다.
  • 2026-07-30 리뷰 보완 Task 6.7, 7.7, 8.4, 10.4, 12.4 완료 검증: 최근 대화와 On Air adapter는 inflate된 root의 기존 RecyclerView.LayoutParams width/height를 보존하고 spacing_12 end margin만 적용하도록 수정했다. 금지된 UI 표현 속성 source assertion은 Feed content, 팔로잉 크리에이터 전체 item, empty 상태 테스트에서 제거하고, 동작/문구/route 계약 검증은 유지했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest", ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*" --tests "kr.co.vividnext.sodalive.v2.widget.feed.FeedViewTest" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다. adb devices에 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
  • 2026-07-30 Phase 1~13 리뷰 최종 검증: ./gradlew --no-daemon :app:testDebugUnitTest --tests 'kr.co.vividnext.sodalive.v2.main.home.*Following*' --tests 'kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest' --tests 'kr.co.vividnext.sodalive.v2.main.home.FeedViewTest' --tests 'kr.co.vividnext.sodalive.v2.live.action.*' --tests 'kr.co.vividnext.sodalive.v2.content.action.*' --tests 'kr.co.vividnext.sodalive.v2.community.action.*' :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck PASS(BUILD SUCCESSFUL, 46 actionable tasks), git diff --check PASS. adb devices는 정상 실행됐으나 연결 기기가 없어 Phase 5 Task 5.3 수동 검증은 미실행 상태를 유지한다. 자동 검증 PASS는 신규 확정 항목 REV-P6-003, REV-P7-003, REV-P8-001, REV-P10-003, REV-P12-001의 정적 리뷰 결과를 해소하지 않으며, 각 신규 Task에서 후속 조치한다.
  • 2026-07-30 Phase별 추가 재점검: 기존 Phase 1~13 리뷰 보고서, 현재 production/test 코드, docs/agent-guides/code-style.md를 다시 대조했다. 신규 확정 항목은 REV-P6-003(최근 대화 284dp 폭을 adapter가 WRAP_CONTENT로 교체), REV-P7-003(content feed UI 크기·padding·margin source test 정책 위반), REV-P8-001(전체 item UI 표현 속성 source assertion), REV-P10-003(On Air 263dp 폭을 간격 helper가 WRAP_CONTENT로 교체), REV-P12-001(empty 크기·정렬·visibility source assertion)이다. 구현 전에 해당 Phase에 Task 6.7, 7.7, 8.4, 10.4, 12.4를 추가했으며, 이번 재점검에서는 production/test 코드를 수정하지 않았다. Phase 5 Task 5.3의 실기기 수동 확인은 기존 보류 상태를 유지한다.
  • 2026-07-30 리뷰 보완 Task 6.6, 7.6, 10.3 완료 검증: 상단 파일 구조의 obsolete 최근 소식 layout 항목을 Feed layout 재사용/삭제 이력 기준으로 정리하고, HomeFollowingNewsAdapterTest 신규 테스트명 2개를 한글화했으며, build-test-style.md에 클래스 단위 실행 예시를 추가하고, On Air 간격 source test 정책 위반 항목을 제거했다. rg 대체 검증, ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest", ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:compileDebugKotlin, ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. ktlintCheck에서는 기존 .editorconfig disabled_rules deprecation warning만 출력됐다. 리뷰어 재확인 결과 blocking finding 없음.
  • 2026-07-30 Phase별 리뷰 산출물 재점검: 기존 Phase 1~13 보고서와 현재 working tree를 다시 대조했다. 기존 통합 리뷰 기록의 “이번 리뷰에서는 production 코드를 수정하지 않았다”는 문구와 달리, 같은 리뷰 후속 Task 4.6·4.7·6.5·7.5·10.2에서 production 코드가 수정된 상태임을 정정한다. 이번 재점검 자체에서는 production 코드를 변경하지 않았으며, REV-P6-002(삭제된 layout의 상단 파일 구조 잔존), REV-P7-002(신규 테스트명과 단일 실행 가이드 미준수), REV-P10-002(UI margin source test 정책 위반)를 추가 확정해 Task 6.6·7.6·10.3으로 전환했다. *Following* 39개, HomeMainFragmentLoginGuardSourceTest 13개, Feed·Live·Content·Community Action 회귀, resource merge, Kotlin compile, ktlint, ./gradlew --no-daemon tasks --all, staged/unstaged diff whitespace 검증이 PASS했다. adb devices에는 연결 기기가 없어 Task 5.3 실기기 수동 확인 보류를 유지한다.
  • 2026-07-30 리뷰 보완 Task 3.4, 4.6, 4.7, 6.5, 7.3, 7.5, 10.2 완료 검증: RED 단계에서 focused source/unit test 실패를 확인한 뒤 최소 구현으로 GREEN 전환했다. ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingNewsAdapterTest", 팔로잉 전체 회귀 + FeedView + v2.live.action.*/v2.content.action.*/v2.community.action.*, ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:compileDebugKotlin, ./gradlew --no-daemon :app:ktlintCheck, git diff --check PASS. 최종 리뷰 게이트에서 Critical/Important finding은 없었고, ranking highlight 중복 문자열 Minor를 추가 RED/GREEN으로 보완했다. 최초 병렬 Gradle 검증은 mergeDebugResources incremental missing file로 실패했으나 순차 재실행에서 PASS했다. adb devices에 연결 기기가 없어 Task 5.3 실기기 수동 확인은 계속 blocked다.
  • 2026-07-30 Phase 113 통합 리뷰: docs/sample/sample-review.md와 review guide에 따라 Phase별 리뷰 보고서 13개를 reviews/ 아래의 phase1-...-review.md부터 phase13-...-review.md까지 작성했다. 확정 항목은 REV-P3-001(Loading 상태 발행 순서 테스트 누락), REV-P4-001(On Air·스케줄 item 이동 no-op), REV-P4-002(LoginRequired pull refresh 종료 누락), REV-P6-001(Feed 전환 전 obsolete layout), REV-P7-001(영어·일본어 ranking 순위 단위의 한국어 고정), REV-P10-001(On Air item 간격 누락)이며, 코드 수정 전에 해당 Phase에 Task 3.4, 4.6, 4.7, 6.5, 7.5, 10.2를 추가했다. Phase 5 실기기 수동 확인은 adb devices에 연결 기기가 없어 기존 Task 5.3 보류를 유지했다. 검증으로 Phase 23 focused test, Phase 4·6·7·8·9·11·12·13 source/feed focused test, 팔로잉 전체 회귀 36개, :app:mergeDebugResources, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check를 실행했고 모두 PASS했다. Gradle deprecation과 Agora namespace는 기존 경고이며, 이번 리뷰에서는 production 코드를 수정하지 않았다.
  • 2026-07-30 Phase 13 최근 소식 터치 이동 검증: onFollowingNewsClick()CREATOR_RANKING을 기존 CreatorActionCommand.Profile로 전달해 해당 크리에이터 채널로 이동하고, AUDIO_CONTENT를 기존 ContentActionCommand.AudioDetail로 전달해 해당 오디오 콘텐츠 상세로 이동하도록 보완했다. 기존 COMMUNITY_POST 상세 이동은 유지하고 CONTENT_RANKING, PHOTO_CONTENT는 no-op으로 유지했다. RED에서 focused test 11개 중 요청 동작 관련 3개 실패를 확인했고, GREEN에서 11개 모두 PASS했다. 팔로잉 전체 회귀 테스트, :app:compileDebugKotlin, :app:ktlintCheck, git diff --check가 PASS했다. adb devices에 연결된 기기가 없어 실제 터치 수동 검증은 미실행했다.
  • 2026-07-13 Phase 12 전체 empty 문구 중앙 표시 검증: 팔로잉 탭 전체 empty 상태에서 기존 screen_home_following_empty 리소스를 재사용해 아직 팔로잉 소식이 없어요.\n관심 있는 크리에이터를 팔로우해 보세요. 문구를 중앙 표시하도록 구현했다. 영어는 No following updates yet.\nFollow creators you are interested in., 일본어는 フォロー中のお知らせはまだありません。\n気になるクリエイターをフォローしてみましょう。로 갱신했다. HomeFollowingUiState.Empty에서만 empty 문구가 보이고 Content, LoginRequired, Error에서는 숨김 처리된다. 검증으로 ./gradlew --no-daemon :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew --no-daemon :app:mergeDebugResources, ./gradlew --no-daemon :app:compileDebugKotlin, git diff --check 모두 PASS. Gradle 실행 중 기존 deprecation warning만 출력됐다.
  • 2026-07-12 Phase 11 검증: 최근 소식 COMMUNITY_POST 상세 이동 계약을 source test로 추가하고 HomeMainFragment.onFollowingNewsClick()에서 HomeFollowingNewsUiItem.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 호출을 삭제했다.