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

21 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된다.
      • Result: Figma item node 185:4509Figma_get_design_context, Figma_get_screenshot으로 확인한 뒤 75dp profile, LIVE pill/time 분리, title, creator, price/free 영역을 반영했다. --no-daemon 단독 실행 기준 BUILD SUCCESSFUL.
  • 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가 컴파일된다.
      • Result: HomeOnAirLiveAdapterloadUrl, paid/free visibility, item click callback을 바인딩하도록 구현했고 --no-daemon 실행 기준 BUILD SUCCESSFUL.
  • 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 등록이 컴파일된다.
      • Result: HomeOnAirLiveActivity.newIntent(context), 첫 페이지 로드, pagination, toast/loading/empty 표시, 기존 getRoomDetail(roomId) 후 입장/비밀번호/결제 흐름을 연결했고 --no-daemon 실행 기준 BUILD SUCCESSFUL.
  • 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 진입 코드가 컴파일된다.
      • Result: HomeLiveAdapter.setOnMoreClick()HomeMainFragment.openHomeOnAirLive()를 연결했고 기존 onLiveClick()은 미구현 상태로 유지했다. --no-daemon 실행 기준 BUILD SUCCESSFUL.
  • Task 3.5: Phase 3 코드 리뷰 및 검증

    • 리뷰 결과:
      • HomeOnAirLiveActivityLiveViewModel.getRoomDetail()/enterRoom()을 호출하지만 liveViewModel.isLoading, liveViewModel.toastLiveData를 observe하지 않아 상세 조회/입장 실패 시 기존 loading/toast 패턴이 사용자에게 전달되지 않는다.
      • GetRoomDetailResponse.isAdult 기준의 기존 ensureLoginAndAdultAuth 정책이 신규 화면에 연결되어 있지 않아 성인 라이브에서 본인인증/콘텐츠 설정 가드가 누락된다.
      • 기존 LiveNowAllActivity는 상세 응답의 channelName != null일 때만 입장 분기를 수행하지만, 신규 화면에는 해당 가드가 없어 종료/예약 상태 응답에서 입장을 시도할 수 있다.
    • 반영:
      • 로그인 확인 후 getRoomDetail(roomId)를 호출하고, 응답의 isAdult 기준으로 기존 성인 인증/성인 콘텐츠 설정 가드를 수행한다.
      • LiveViewModel.isLoadingLiveViewModel.toastLiveData를 observe하고, 목록 로딩과 입장 로딩 상태를 OR 조건으로 합쳐 LoadingDialog를 제어한다.
      • channelName이 blank인 상세 응답은 입장 분기를 중단하도록 정책 함수를 추가한다.
    • 검증:
      • Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon
      • Result: BUILD SUCCESSFUL.
      • Run: ./gradlew :app:mergeDebugResources --no-daemon
      • Result: BUILD SUCCESSFUL.
      • Run: ./gradlew :app:compileDebugKotlin --no-daemon
      • Result: BUILD SUCCESSFUL.
      • Run: ./gradlew :app:ktlintCheck --no-daemon
      • Result: BUILD SUCCESSFUL.
      • Run: git diff --check
      • Result: whitespace error 없음.
      • 종합: 리뷰 지적사항을 반영했고 빌드/린트/단위 테스트가 모두 통과했다.
      • 추가 Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveEntryPolicyTest"
      • 추가 RED Result: canEnterHomeOnAirLiveRoom 미구현으로 컴파일 실패를 확인했다.
      • 추가 GREEN Result: channelName이 blank인 상세 응답에서 입장 분기를 중단하는 정책 함수를 구현한 뒤 BUILD SUCCESSFUL.
      • 추가 Run: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon
      • 추가 Result: BUILD SUCCESSFUL.
      • 추가 Run: ./gradlew :app:mergeDebugResources --no-daemon
      • 추가 Result: 최초 sandbox 실행은 ~/.gradle wrapper lock 접근 제한으로 실패했고, 승인 실행 기준 BUILD SUCCESSFUL.
      • 추가 Run: ./gradlew :app:compileDebugKotlin --no-daemon
      • 추가 Result: BUILD SUCCESSFUL.
      • 추가 Run: ./gradlew :app:ktlintCheck --no-daemon
      • 추가 Result: 기존 .editorconfigdisabled_rules deprecation 경고가 출력되었지만 BUILD SUCCESSFUL.
      • 추가 Run: git diff --check
      • 추가 Result: whitespace error 없음.

Phase 4: 최종 검증

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

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

    • 검증:
      • Run: ./gradlew :app:ktlintCheck
      • Expected: ktlint PASS.
      • Result: --no-daemon 단독 실행 기준 BUILD SUCCESSFUL.
      • Run: git diff --check
      • Expected: whitespace error 없음.
      • Result: 출력 없음. 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 모두 통과했다.
  • 2026-06-27 Phase 3 검증: ./gradlew :app:mergeDebugResources --no-daemon, ./gradlew :app:compileDebugKotlin --no-daemon, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon, ./gradlew :app:ktlintCheck --no-daemon, git diff --check 모두 통과했다. 최초 병렬 실행 중 mergeDebugResources가 stripped 중간 파일 경합으로 1회 실패했으나, 순차 단독 재실행에서 BUILD SUCCESSFUL을 확인했다.
  • 2026-06-27 Phase 3 Figma 재검증: 리뷰 지적에 따라 item_home_on_air_live.xml의 title을 Typography.Heading4, LIVE badge를 Typography.Caption3 기반 pill, time/creator/price를 Typography.Body6로 조정했다. 이후 ./gradlew :app:mergeDebugResources --no-daemon, ./gradlew :app:compileDebugKotlin --no-daemon, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon, ./gradlew :app:ktlintCheck --no-daemon, git diff --check 모두 통과했다.
  • 2026-06-27 Phase 3 리뷰 반영: HomeOnAirLiveActivity에 기존 라이브 입장 정책과 동일하게 로그인 확인, getRoomDetail(roomId) 응답의 isAdult 기준 성인 인증/성인 콘텐츠 설정 가드, LiveViewModel.isLoading/toastLiveData 관찰을 추가했다. 이후 ./gradlew :app:mergeDebugResources --no-daemon, ./gradlew :app:compileDebugKotlin --no-daemon, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --no-daemon, ./gradlew :app:ktlintCheck --no-daemon, git diff --check 모두 통과했다.
  • 2026-06-27 Phase 3 코드 리뷰 및 재검증: HomeOnAirLiveActivityLiveViewModel loading/toast 관찰과 GetRoomDetailResponse.isAdult 기준 성인 인증/성인 콘텐츠 설정 가드를 추가한 뒤 ./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를 순차 실행해 모두 통과했다.
  • 2026-06-27 Phase 4 최종 검증: ./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 모두 BUILD SUCCESSFUL을 확인했다. git diff --check는 출력이 없어 whitespace error가 없음을 확인했다.