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

28 KiB

메인 홈 팔로잉 탭 구현 계획/TASK

For agentic workers: REQUIRED SUB-SKILL: 구현 시 superpowers:subagent-driven-development 또는 superpowers:executing-plans를 사용해 task 단위로 진행한다. 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시 - [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.

Goal: GET /api/v2/home/following 응답을 기반으로 메인 홈 팔로잉 탭에 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식을 표시한다.

Architecture: 기존 HomeMainFragment의 title bar, TextTabBarView, 추천/랭킹 탭 구조는 유지하고, 팔로잉 선택 시 전용 content surface를 노출한다. 신규 API/Repository/DTO/UI state/mapper/ViewModel/adapter는 kr.co.vividnext.sodalive.v2.main.home 하위에 두며, ChatRoomListItemResponse, CreatorActivityType, formatUtcRelativeTimeText, 기존 feed/live/profile widget을 우선 재사용한다. 로그인 유도 화면은 아직 디자인/문구가 정해지지 않았으므로 이번 구현 계획에서는 isLoginRequired 상태 분기와 팔로잉 섹션 숨김까지만 고정한다.

Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.


전제와 성공 기준

  • PRD: docs/20260625_메인_홈_팔로잉_탭/prd.md
  • Figma: home_003 팔로잉 탭 24:5682
  • API endpoint는 GET /api/v2/home/following이다.
  • Authorization header는 optional이며, token이 blank이면 header를 보내지 않는다.
  • query parameter는 보내지 않는다.
  • isLoginRequired = true이면 팔로잉 섹션을 표시하지 않는다.
  • 로그인 유도 화면의 실제 UI, 문구, CTA, 로그인 완료 후 복귀 정책은 별도 확정 후 구현한다.
  • recentNews 시간은 visibleFromAtUtc를 디바이스 타임존 기준으로 상대 시간 표시한다.
  • PHOTO_CONTENT label은 우선 화보로 표시한다.
  • ranking news의 rank가 null이면 해당 news item은 표시하지 않는다.
  • monthlySchedules는 서버가 이번 달 범위로 정렬해서 내려주며 앱은 재정렬하지 않는다.
  • 섹션 title chevron은 터치 콜백까지만 연결하고 실제 이동 목적지는 만들지 않는다.
  • 구현 완료 후 최소 다음 명령을 실행한다.
    • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"
    • ./gradlew :app:mergeDebugResources
    • ./gradlew :app:compileDebugKotlin
    • ./gradlew :app: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의 포함/제외 항목과 실제 화면을 대조한다.

파일 구조

  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt
    • GET /api/v2/home/following Retrofit endpoint를 정의한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt
    • HomeFollowingTabResponse, FollowingCreatorResponse, FollowingLiveResponse, FollowingScheduleResponse, FollowingNewsResponse, 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로 변환하고, rank == null news 숨김, PHOTO_CONTENT label, visibleFromAtUtc 시간 기준을 적용한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingAuthHeader.kt
    • blank token이면 null, 값이 있으면 Bearer {token}을 반환한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt
    • 팔로잉 탭 API 호출, loading/error/login-required/content 상태를 관리한다.
  • Modify: app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • HomeFollowingApi, HomeFollowingRepository, HomeFollowingViewModel을 Koin에 등록한다.
  • Modify: app/src/main/res/layout/fragment_v2_main_home.xml
    • 팔로잉 탭 content surface와 섹션 RecyclerView들을 추가한다.
  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
    • HOME_TAB_FOLLOWING 분기, ViewModel observer, adapter binding, section visibility, chevron click callback을 연결한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingCreatorAdapter.kt
    • followingCreators horizontal profile list를 바인딩한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt
    • onAirLives horizontal live card list를 바인딩한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingChatAdapter.kt
    • recentChats horizontal chat card list를 바인딩한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.kt
    • monthlySchedules vertical schedule list를 바인딩한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt
    • recentNews vertical news list를 바인딩한다.
  • Create: app/src/main/res/layout/item_home_following_creator.xml
    • 팔로잉 크리에이터 profile item이다.
  • Create: app/src/main/res/layout/item_home_following_live.xml
    • On Air live item이다.
  • Create: app/src/main/res/layout/item_home_following_chat.xml
    • 최근 대화 compact card item이다.
  • Create: app/src/main/res/layout/item_home_following_schedule.xml
    • 이달의 스케줄 item이다.
  • Create: app/src/main/res/layout/item_home_following_news_rank.xml
    • ranking news item이다.
  • Create: app/src/main/res/layout/item_home_following_news_content.xml
    • audio/photo content news item이다.
  • Modify: app/src/main/res/values/strings.xml
    • 팔로잉 섹션 title, On Air, 화보, empty/error label을 추가한다.
  • Modify: app/src/main/res/values-en/strings.xml
    • 팔로잉 섹션 title, On Air, photo label, empty/error label을 추가한다.
  • Modify: app/src/main/res/values-ja/strings.xml
    • 팔로잉 섹션 title, On Air, photo label, empty/error label을 추가한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingAuthHeaderTest.kt
    • optional auth header 생성 규칙을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt
    • DTO to UI mapping, login-required, rank null filtering, news label/time 기준을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt
    • API success/error/login-required/loading 상태 전환을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt
    • layout id, adapter/ViewModel 연결, HOME_TAB_FOLLOWING 분기, chevron click callback 연결을 source-level로 검증한다.

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

  • Task 1.1: 홈 탭 삽입 지점 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
      • app/src/main/res/layout/fragment_v2_main_home.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeRecommendationViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeCreatorRankingViewModel.kt
    • 작업:
      • 기존 추천, 랭킹, 팔로잉 Text Tab bar는 유지한다.
      • showHomeTab(HOME_TAB_FOLLOWING) 분기에서 팔로잉 surface를 표시할 위치를 확인한다.
      • 추천/랭킹 API와 ViewModel은 리팩터링하지 않는다.
    • 검증:
      • Run: rg -n "HOME_TAB_FOLLOWING|showHomeTab|nsvHomeRecommendationContent|rvHomeCreatorRankings|textTabBarHome" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home app/src/main/res/layout/fragment_v2_main_home.xml
      • Expected: 팔로잉 탭 분기와 기존 추천/랭킹 surface visibility 제어 지점이 확인된다.
      • Result: PASS. HomeMainFragment.kt에서 showHomeTab, HOME_TAB_FOLLOWING, 추천/랭킹 visibility 제어 지점을 확인했다.
  • Task 1.2: 재사용 위젯과 신규 adapter 경계 확정

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

    • 확인:
      • docs/20260625_메인_홈_팔로잉_탭/prd.md
    • 제외:
      • 로그인 유도 화면 실제 디자인/문구/CTA 구현
      • 더보기 chevron 목적지 이동
      • 팔로잉/언팔로잉 액션
      • 스케줄 월 필터와 앱 내 재정렬
      • 레거시 홈 화면 직접 수정
    • 검증:
      • Run: rg -n "Non-Goals|로그인 유도|더보기|monthlySchedules|rank|PHOTO_CONTENT|Open Questions" docs/20260625_메인_홈_팔로잉_탭/prd.md
      • Expected: 제외 범위와 확정 정책이 확인된다.
      • Result: PASS. 로그인 유도 UI 미확정, 더보기 목적지 제외, monthlySchedules 정렬 정책, rank == null 제외, PHOTO_CONTENT label 정책을 확인했다.

Phase 2: API, DTO, Repository, mapper 추가

  • Task 2.1: optional auth header 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingAuthHeaderTest.kt
    • 테스트 케이스:
      • blank token은 null을 반환한다.
      • non-blank token은 Bearer {token}을 반환한다.
      • 앞뒤 공백이 있는 token은 trim 후 Bearer {token}을 반환한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingAuthHeaderTest"
      • Expected: helper 구현 전 RED 실패.
      • Result: RED 확인. homeFollowingAuthHeader 미구현으로 compileDebugUnitTestKotlin unresolved reference 실패가 발생했다.
  • Task 2.2: optional auth header helper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingAuthHeader.kt
    • 작업:
      • fun homeFollowingAuthHeader(token: String): String?를 추가한다.
      • token.trim().takeIf { it.isNotEmpty() }?.let { "Bearer $it" } 규칙을 적용한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingAuthHeaderTest"
      • Expected: PASS.
      • Result: PASS. blank/whitespace token은 null, non-blank token은 trim 후 Bearer {token}으로 검증됐다.
  • Task 2.3: API/DTO/Repository 계약 추가

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingRepository.kt
    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 작업:
      • Retrofit endpoint는 @GET("/api/v2/home/following")로 정의한다.
      • @Header("Authorization") authHeader: String?를 사용한다.
      • query parameter는 정의하지 않는다.
      • DTO는 PRD의 Android Response Contract 필드를 모두 포함하고 @Keep, @SerializedName을 사용한다.
      • recentChats는 기존 ChatRoomListItemResponse를 사용한다.
      • FollowingScheduleResponse.type은 기존 CreatorActivityType을 사용한다.
      • Koin networkModule, repositoryModule에 신규 API/Repository를 등록한다.
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: 신규 data layer와 DI 등록이 컴파일된다.
      • Result: PASS. HomeFollowingApi, DTO, Repository, API/Repository DI 등록이 compileDebugKotlin에서 컴파일됐다.
  • Task 2.4: string resource 추가

    • 수정:
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/src/main/res/values-ja/strings.xml
    • 작업:
      • 섹션 title 문자열을 추가한다.
        • screen_home_following_creators_title
        • screen_home_following_on_air_title
        • screen_home_following_recent_chats_title
        • screen_home_following_monthly_schedules_title
        • screen_home_following_recent_news_title
      • news/category 문자열을 추가한다.
        • screen_home_following_on_air
        • screen_home_following_photo_content
      • empty/error 문자열을 추가한다.
        • screen_home_following_empty
        • screen_home_following_error
    • 검증:
      • Run: ./gradlew :app:mergeDebugResources
      • Expected: 3개 locale string resource가 중복 없이 merge된다.
      • Result: PASS. values, values-en, values-ja string resource merge가 통과했다.
  • Task 2.5: mapper RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingMapperTest.kt
    • 테스트 케이스:
      • isLoginRequired = true 응답은 HomeFollowingUiState.LoginRequired로 매핑된다.
      • isLoginRequired = false이고 모든 섹션이 비면 HomeFollowingUiState.Empty로 매핑된다.
      • followingCreators, onAirLives, recentChats, monthlySchedules, recentNews가 section UI model로 매핑된다.
      • recentChatsChatRoomListItemResponse.toUiItem() 결과가 null인 항목을 제외한다.
      • monthlySchedules는 서버 응답 순서를 유지한다.
      • PHOTO_CONTENTscreen_home_following_photo_content label로 매핑된다.
      • CREATOR_RANKING, CONTENT_RANKINGrank == null 항목은 제외된다.
      • news 상대 시간은 visibleFromAtUtc 값을 formatter에 전달한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"
      • Expected: UI model/mapper 구현 전 RED 실패.
      • Result: RED 확인. DTO/UI model/mapper/string resource 미구현으로 compileDebugUnitTestKotlin unresolved reference 실패가 발생했다.
  • Task 2.6: UI state/model과 mapper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiState.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingUiModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt
    • 작업:
      • HomeFollowingUiStateLoading, LoginRequired, Content, Empty, Error를 정의한다.
      • Content에는 followingCreators, onAirLives, recentChats, monthlySchedules, recentNews section을 둔다.
      • Content.isEmpty helper를 추가해 모든 section item이 비었는지 판정한다.
      • mapper는 UtcRelativeTimeTextFormatter를 받아 visibleFromAtUtc 상대 시간을 생성한다.
      • ranking news의 rank == null은 map 단계에서 제외한다.
      • PHOTO_CONTENT는 photo label string resource id를 매핑한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingMapperTest"
      • Expected: PASS.
      • Result: PASS. login-required, empty, content mapping, invalid chat 제외, schedule 순서 유지, PHOTO_CONTENT label, null rank filtering, visibleFromAtUtc formatter 전달이 검증됐다.

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

  • Task 3.1: ViewModel RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModelTest.kt
    • 테스트 케이스:
      • loadFollowing()은 loading 후 content 상태를 발행한다.
      • blank token이면 repository에 null auth header를 전달한다.
      • token이 있으면 repository에 Bearer {token} auth header를 전달한다.
      • isLoginRequired = true 응답은 login-required 상태를 발행한다.
      • API success이지만 data가 null이면 error 상태와 toast를 발행한다.
      • API failure throwable이면 error 상태와 toast를 발행한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"
      • Expected: ViewModel 구현 전 RED 실패.
      • Result: RED 확인. HomeFollowingViewModel 미구현으로 compileDebugUnitTestKotlin unresolved reference 실패가 발생했다.
  • Task 3.2: HomeFollowingViewModel 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt
    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 작업:
      • HomeFollowingViewModel(repository, relativeTimeTextFormatter)를 추가한다.
      • SharedPreferenceManager.tokenhomeFollowingAuthHeader()로 변환해 repository에 전달한다.
      • RxJava3 subscribeOn(Schedulers.io()), observeOn(AndroidSchedulers.mainThread()) 패턴을 따른다.
      • success data는 mapper로 UI state 변환한다.
      • error는 HomeFollowingUiState.Error와 기존 unknown error toast 패턴을 따른다.
      • Koin viewModelModuleHomeFollowingViewModel(get(), get())를 등록한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingViewModelTest"
      • Expected: PASS.
      • Result: PASS. loading/content, optional auth header, login-required, null data error/toast, throwable error/toast 상태 전환이 검증됐다.
  • Task 3.3: data/model/ViewModel 통합 컴파일 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingApi.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeFollowingModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeFollowingMappers.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: 팔로잉 data/model/ViewModel/DI 코드가 컴파일된다.
      • Result: PASS. 팔로잉 data/model/ViewModel/DI 코드가 compileDebugKotlin에서 컴파일됐다.
    • 검증 기록:
      • 2026-06-25 코드 리뷰: Phase 1~3 범위의 API/DTO/Repository/mapper/ViewModel/DI/string/test 변경을 검토했으며, 현재 코드 기준으로 blocking finding은 발견하지 못했다.
      • 2026-06-25 검증: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew :app:mergeDebugResources, ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check 모두 PASS.

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

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

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt
    • 테스트 케이스:
      • fragment_v2_main_home.xmlnsv_home_following_content가 있다.
      • layout에 rv_home_following_creators, rv_home_following_on_air_lives, rv_home_following_recent_chats, rv_home_following_monthly_schedules, rv_home_following_recent_news가 있다.
      • HomeMainFragmentHomeFollowingViewModel을 주입한다.
      • HOME_TAB_FOLLOWING 분기에서 팔로잉 surface를 visible 처리한다.
      • section title chevron click listener가 연결되어 있고 실제 route 호출은 없다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest"
      • Expected: layout/fragment 수정 전 RED 실패.
  • 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된다.
  • 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가 컴파일된다.
  • 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.
  • 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 콜백 함수가 확인된다.

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

  • Task 5.1: 팔로잉 관련 단위 테스트 실행

    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*"
      • Expected: 팔로잉 관련 local unit/source test가 모두 PASS.
    • 검증 기록:
      • 실행 후 결과를 이 task 아래에 한국어로 누적 기록한다.
  • 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 없음.
    • 검증 기록:
      • 실행 후 결과를 이 task 아래에 한국어로 누적 기록한다.
  • 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 터치 시 앱이 크래시하지 않고 화면 이동은 발생하지 않는다.
    • 검증 기록:
      • 수동 확인 결과를 이 task 아래에 한국어로 누적 기록한다.

Verification Log

  • 2026-06-25 Phase 1-3 구현 검증: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*Following*", ./gradlew :app:mergeDebugResources, ./gradlew :app:compileDebugKotlin, ./gradlew :app:ktlintCheck, git diff --check 모두 PASS.
  • 2026-06-25 Phase 1-3 코드 리뷰 및 재검증: blocking finding 없음. ./gradlew :app:mergeDebugResources는 최초 sandbox lock 권한 오류 후 승인 실행으로 PASS했고, 나머지 검증 명령도 PASS.