Files
sodalive-android/docs/20260626_현재_진행_중인_라이브_리스트_페이지/plan-task.md

15 KiB

현재 진행 중인 라이브 리스트 페이지 구현 계획/TASK

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

Goal: GET /api/v2/home/on-air-lives 응답을 기반으로 현재 진행 중인 라이브 리스트 화면을 만들고, 아이템 터치 시 기존 라이브 상세 조회 후 입장/비밀번호/결제 흐름을 재사용한다.

Architecture: 신규 화면/API/Repository/ViewModel/adapter/model은 kr.co.vividnext.sodalive.v2.live.onair 하위에 둔다. 기존 홈 추천 라이브 섹션의 더보기 진입점은 새 Activity를 여는 역할만 담당하고, 실제 라이브 입장 정책은 기존 LiveViewModel.getRoomDetail()enterRoom()을 호출해 중복 구현을 최소화한다.

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


전제와 성공 기준

  • PRD: docs/20260626_현재_진행_중인_라이브_리스트_페이지/prd.md
  • Figma 전체: 185:4506
  • Figma 아이템: 185:4509
  • 신규 기능 본체의 Kotlin package는 kr.co.vividnext.sodalive.v2.live.onair로 고정한다.
  • 메인 홈 추천 탭은 HomeOnAirLiveActivity 진입 연결만 추가하고, on-air live API/data/model 구현을 홈 패키지에 두지 않는다.
  • API endpoint는 GET /api/v2/home/on-air-lives이다.
  • 앱은 size query parameter를 보내지 않는다.
  • HomeOnAirLiveResponse에는 beginDateTimeUtc: String이 포함된다.
  • 목록 아이템은 LIVE HH:mm을 표시하며, HH:mmbeginDateTimeUtc를 디바이스 Timezone으로 변환한 라이브 시작 시각이다.
  • 유료 라이브는 ic_bar_cash, 가격, 입장 캔을 표시한다.
  • 무료 라이브는 cash 아이콘 없이 무료를 표시한다.
  • 아이템 터치 시 getRoomDetail(roomId) 호출 후 기존 입장 정책을 따른다.
  • 구현 완료 후 최소 다음 명령을 실행한다.
    • ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*"
    • ./gradlew :app:mergeDebugResources
    • ./gradlew :app:compileDebugKotlin
    • ./gradlew :app:ktlintCheck
    • git diff --check

파일 구조

  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveApi.kt
    • /api/v2/home/on-air-lives Retrofit endpoint를 정의한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveModels.kt
    • HomeOnAirLivePageResponse, HomeOnAirLiveResponse DTO를 정의한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveRepository.kt
    • API 호출을 repository method로 감싼다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveUiModels.kt
    • 리스트 item UI model, page state, 시간/가격 표시 모델을 정의한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveMappers.kt
    • DTO를 UI model로 변환하고 beginDateTimeUtcHH:mm으로 변환한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveAuthHeader.kt
    • blank token이면 null, 값이 있으면 Bearer {token}을 반환한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveViewModel.kt
    • 첫 페이지/추가 페이지 로딩, loading/error/page 상태를 관리한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
    • 화면 구성, pagination, 상세 조회 후 입장 흐름을 연결한다.
  • Create: app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/ui/HomeOnAirLiveAdapter.kt
    • 리스트 아이템을 바인딩한다.
  • Create: app/src/main/res/layout/activity_home_on_air_live.xml
    • title bar와 RecyclerView 컨테이너를 정의한다.
  • Create: app/src/main/res/layout/item_home_on_air_live.xml
    • Figma 기준 라이브 리스트 아이템을 정의한다.
  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt
    • 더보기 항목 클릭 콜백을 받을 수 있게 한다.
  • Modify: app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
    • 홈 추천 라이브 더보기에서 HomeOnAirLiveActivity를 연다.
  • Modify: app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 신규 API/Repository/ViewModel을 Koin에 등록한다.
  • Modify: app/src/main/AndroidManifest.xml
    • HomeOnAirLiveActivity를 등록한다.
  • Modify: app/src/main/res/values/strings.xml
    • On Air, 입장 캔, 무료 등 필요한 문자열을 추가하거나 기존 문자열을 재사용한다.
  • Modify: app/src/main/res/values-en/strings.xml
    • 신규 문자열 번역을 추가한다.
  • Modify: app/src/main/res/values-ja/strings.xml
    • 신규 문자열 번역을 추가한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveAuthHeaderTest.kt
    • optional auth header 생성 규칙을 검증한다.
  • Create: app/src/test/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveMapperTest.kt
    • DTO to UI mapping, LIVE HH:mm, 가격/무료 표시를 검증한다.

Phase 1: 문서와 기존 구조 확인

  • Task 1.1: 기존 홈 라이브 클릭 동작 확인

    • 확인:
      • 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/HomeLiveAdapter.kt
    • 결과:
      • 홈 추천 최상단 라이브 아이템 클릭은 onLiveClick()으로 연결되어 있으나 현재 구현은 Unit이다.
      • 더보기 항목은 setOnClickListener(null)로 클릭 동작이 없다.
    • 검증:
      • Run: rg -n "onLiveClick|setOnLiveClick|MoreViewHolder|setOnClickListener\\(null\\)" app/src/main/java/kr/co/vividnext/sodalive/v2/main/home
      • Expected: 클릭 콜백과 미구현 지점이 확인된다.
  • Task 1.2: 기존 라이브 입장 정책 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/live/now/all/LiveNowAllActivity.kt
      • app/src/main/java/kr/co/vividnext/sodalive/live/LiveViewModel.kt
      • app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt
    • 결과:
      • 기존 입장은 getRoomDetail(roomId)manager.id, price, isPaid, isPrivateRoom, beginDateTimeUtc를 기준으로 분기한다.
      • LiveViewModel.enterRoom(roomId, onSuccess, password)를 재사용할 수 있다.
    • 검증:
      • Run: rg -n "fun enterLiveRoom|fun getRoomDetail|fun enterRoom|data class GetRoomDetailResponse" app/src/main/java/kr/co/vividnext/sodalive/live
      • Expected: 상세 조회와 입장 API 호출 지점이 확인된다.

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

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

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveAuthHeaderTest.kt
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveAuthHeaderTest"
      • Expected: helper 구현 전 RED 실패.
      • Result: helper 구현 전 Unresolved reference 'model', Unresolved reference 'homeOnAirLiveAuthHeader'로 RED 실패 확인.
  • Task 2.2: optional auth header helper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveAuthHeader.kt
    • 작업:
      • fun homeOnAirLiveAuthHeader(token: String): String?를 추가한다.
      • token.trim().takeIf { it.isNotEmpty() }?.let { "Bearer $it" } 규칙을 적용한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveAuthHeaderTest"
      • Expected: PASS.
      • Result: --no-daemon 재실행 기준 BUILD SUCCESSFUL.
  • Task 2.3: mapper 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveMapperTest.kt
    • 테스트 케이스:
      • beginDateTimeUtc를 디바이스 Timezone 기준 HH:mm으로 변환한다.
      • price > 0이면 유료 가격 표시 모델로 매핑한다.
      • price == 0이면 무료 표시 모델로 매핑한다.
      • page 응답의 page, hasNext, items를 UI state로 유지한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveMapperTest"
      • Expected: mapper 구현 전 RED 실패.
      • Result: mapper 구현 전 Unresolved reference 'data', Unresolved reference 'model', Unresolved reference 'HomeOnAirLivePageResponse'로 RED 실패 확인.
  • Task 2.4: API/DTO/Repository/model/mapper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveApi.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/data/HomeOnAirLiveRepository.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveUiModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/model/HomeOnAirLiveMappers.kt
    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 작업:
      • Retrofit endpoint는 @GET("/api/v2/home/on-air-lives")로 정의한다.
      • @Header("Authorization") authHeader: String?, @Query("page") page: Int만 사용한다.
      • DTO는 @Keep, @SerializedName을 사용한다.
      • mapper는 java.time 사용 가능성을 확인하고, 프로젝트 minSdk 제약상 문제가 있으면 기존 SimpleDateFormat 패턴을 사용한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveMapperTest"
      • Expected: PASS.
      • Result: --no-daemon 재실행 기준 BUILD SUCCESSFUL.
  • Task 2.5: ViewModel 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveViewModel.kt
    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
    • 작업:
      • loadFirstPage()는 page 0부터 호출하고 기존 items를 교체한다.
      • loadNextPage()hasNext = true이고 loading 중이 아닐 때만 호출한다.
      • success이면 mapper 결과를 state로 발행한다.
      • failure이면 기존 unknown error toast 패턴을 따른다.
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: 신규 data/model/ViewModel/DI 코드가 컴파일된다.
      • Result: auth header 테스트 GREEN 실행 중 :app:compileDebugKotlin 성공. 추가로 HomeOnAirLiveViewModelTest를 작성해 최초 page 0 로드, 다음 page append, hasNext=false guard를 검증했고 BUILD SUCCESSFUL.

Phase 3: 화면 UI와 홈 진입점 연결

  • Task 3.1: string/layout 추가

    • 생성:
      • app/src/main/res/layout/activity_home_on_air_live.xml
      • app/src/main/res/layout/item_home_on_air_live.xml
    • 수정:
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/src/main/res/values-ja/strings.xml
    • 작업:
      • On Air title, 입장 캔, 무료 문자열은 기존 리소스가 있으면 재사용하고 부족한 값만 추가한다.
      • item_home_on_air_live.xml은 75dp profile, LIVE HH:mm, title, creator, price/free 영역을 포함한다.
    • 검증:
      • Run: ./gradlew :app:mergeDebugResources
      • Expected: 신규 layout/string resource가 merge된다.
  • Task 3.2: adapter 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/ui/HomeOnAirLiveAdapter.kt
    • 작업:
      • submitItems(items)onClick callback을 제공한다.
      • 유료/무료 표시 모델에 따라 cash icon visibility와 텍스트를 바인딩한다.
      • 이미지 로드는 기존 loadUrl 확장을 사용한다.
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: adapter가 컴파일된다.
  • Task 3.3: Activity 구현과 Manifest 등록

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
    • 수정:
      • app/src/main/AndroidManifest.xml
    • 작업:
      • newIntent(context)를 제공한다.
      • RecyclerView와 pagination scroll listener를 연결한다.
      • item click 시 getRoomDetail(roomId) 후 기존 입장 정책을 적용한다.
      • 오디오 재생 서비스 중지 후 LiveRoomActivity를 실행한다.
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: Activity와 Manifest 등록이 컴파일된다.
  • Task 3.4: 홈 추천 라이브 더보기 진입점 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeLiveAdapter.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt
    • 작업:
      • HomeLiveAdaptersetOnMoreClick()을 추가한다.
      • 더보기 항목 클릭 시 HomeOnAirLiveActivity.newIntent(requireContext())를 실행한다.
      • 기존 라이브 아이템 클릭은 이번 범위에서 직접 입장 연결하지 않고 기존 onLiveClick() 미구현 상태를 유지한다.
    • 검증:
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: 홈 추천 더보기에서 신규 Activity 진입 코드가 컴파일된다.

Phase 4: 최종 검증

  • Task 4.1: 단위 테스트와 컴파일 검증

    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*"
      • Expected: on-air live 관련 단위 테스트 PASS.
      • Run: ./gradlew :app:mergeDebugResources
      • Expected: resource merge PASS.
      • Run: ./gradlew :app:compileDebugKotlin
      • Expected: Kotlin compile PASS.
  • Task 4.2: 린트/차이 검증

    • 검증:
      • Run: ./gradlew :app:ktlintCheck
      • Expected: ktlint PASS.
      • Run: git diff --check
      • Expected: whitespace error 없음.

Verification Log

  • 2026-06-26 Phase 2 RED: production 구현 전 HomeOnAirLiveAuthHeaderTest, HomeOnAirLiveMapperTest 실행 시 신규 data/model symbol 미존재로 컴파일 실패를 확인했다.
  • 2026-06-26 Phase 2 GREEN: HomeOnAirLiveAuthHeaderTest, HomeOnAirLiveMapperTest, HomeOnAirLiveViewModelTest 각각 --no-daemon 실행 기준 BUILD SUCCESSFUL을 확인했다.
  • 2026-06-26 Phase 2 최종 검증: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon, ./gradlew :app:mergeDebugResources --no-daemon, ./gradlew :app:compileDebugKotlin --no-daemon, ./gradlew :app:ktlintCheck --no-daemon, git diff --check 모두 통과했다.