docs(live): 현재 진행 라이브 리스트 문서를 추가한다
This commit is contained in:
248
docs/20260626_현재_진행_중인_라이브_리스트_페이지/plan-task.md
Normal file
248
docs/20260626_현재_진행_중인_라이브_리스트_페이지/plan-task.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 현재 진행 중인 라이브 리스트 페이지 구현 계획/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:mm`은 `beginDateTimeUtc`를 디바이스 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로 변환하고 `beginDateTimeUtc`를 `HH: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: 문서와 기존 구조 확인
|
||||
|
||||
- [x] **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: 클릭 콜백과 미구현 지점이 확인된다.
|
||||
|
||||
- [x] **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 실패.
|
||||
|
||||
- [ ] **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.
|
||||
|
||||
- [ ] **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 실패.
|
||||
|
||||
- [ ] **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.
|
||||
|
||||
- [ ] **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 코드가 컴파일된다.
|
||||
|
||||
---
|
||||
|
||||
### 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`
|
||||
- 작업:
|
||||
- `HomeLiveAdapter`에 `setOnMoreClick()`을 추가한다.
|
||||
- 더보기 항목 클릭 시 `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
|
||||
- 아직 구현 전이다.
|
||||
149
docs/20260626_현재_진행_중인_라이브_리스트_페이지/prd.md
Normal file
149
docs/20260626_현재_진행_중인_라이브_리스트_페이지/prd.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# PRD: 현재 진행 중인 라이브 리스트 페이지
|
||||
|
||||
## 1. Overview
|
||||
메인 홈 추천 탭 최상단의 라이브 섹션에서 전체 라이브 리스트 화면으로 이동하고, `GET /api/v2/home/on-air-lives` 응답을 Figma `live_detail_001` 디자인 기준으로 표시한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem
|
||||
- 메인 홈 추천 탭 최상단 라이브 아이템은 현재 클릭 콜백이 비어 있어 터치해도 아무 동작을 하지 않는다.
|
||||
- 기존 레거시 `LiveNowAllActivity`는 별도 `/live/room` API와 그리드 UI를 사용하므로, v2 홈 추천의 Figma 리스트 디자인과 API 계약에 맞지 않는다.
|
||||
- 라이브 입장은 비공개방, 유료방, 이미 결제한 방, 방장 입장, 성인 인증/콘텐츠 설정 등 기존 정책을 따라야 한다.
|
||||
- 신규 목록 API는 리스트 표시용 정보만 제공하므로, 아이템 터치 시 기존 라이브 상세 조회 후 입장 정책을 결정해야 한다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Goals
|
||||
- 홈 추천 최상단 라이브 섹션의 전체보기/더보기 항목 또는 진입 지점에서 현재 진행 중인 라이브 리스트 페이지를 연다.
|
||||
- `GET /api/v2/home/on-air-lives?page={page}`를 호출해 0부터 시작하는 페이지 단위로 현재 진행 중인 라이브 목록을 표시한다.
|
||||
- 서버 `size`는 20으로 고정되므로 앱은 `size` query parameter를 보내지 않는다.
|
||||
- 리스트 아이템은 Figma 기준으로 프로필 이미지, `LIVE ${라이브 시작시간}`, 제목, 크리에이터 닉네임, 가격/무료 정보를 표시한다.
|
||||
- `beginDateTimeUtc`는 디바이스 Timezone으로 변환한 뒤 `HH:mm` 형태로 표시한다.
|
||||
- 유료 라이브는 `ic_bar_cash`와 가격, `입장 캔` 문구를 표시한다.
|
||||
- 무료 라이브는 cash 아이콘을 숨기고 `무료`라고 표시한다.
|
||||
- 아이템 터치 시 기존 `getRoomDetail(roomId)` 조회 후 상세 응답 기준으로 입장/비밀번호/결제/성인 인증 흐름을 결정한다.
|
||||
- 기존 레거시 라이브 입장 정책은 직접 수정하지 않고, v2 신규 화면에서 호출/재사용한다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Non-Goals
|
||||
- 이번 작업에서 서버 API 구현은 포함하지 않는다.
|
||||
- 기존 레거시 `LiveNowAllActivity` 그리드 UI를 리팩터링하지 않는다.
|
||||
- 기존 `LiveRoomActivity`, `LiveViewModel`, `LiveRepository`, `LiveApi`의 입장 정책 자체를 변경하지 않는다.
|
||||
- 검색, 정렬, 필터, pull-to-refresh, skeleton loading은 이번 범위에 포함하지 않는다.
|
||||
- Figma에 없는 별도 마케팅/빈 상태 화면을 새로 설계하지 않는다.
|
||||
- 라이브 종료/삭제/네트워크 오류에 대한 신규 정책을 만들지 않고 기존 unknown error toast/loading 패턴을 따른다.
|
||||
|
||||
---
|
||||
|
||||
## 5. Target Users
|
||||
- 홈 추천 탭에서 현재 진행 중인 라이브를 더 많이 보고 바로 입장하려는 사용자.
|
||||
- 무료/유료 라이브 여부와 시작 시각을 리스트에서 빠르게 확인하려는 사용자.
|
||||
- 기존 라이브 입장 정책을 유지하면서 v2 홈 UI를 확장해야 하는 Android 개발자.
|
||||
|
||||
---
|
||||
|
||||
## 6. User Stories
|
||||
- 사용자는 홈 추천 탭의 라이브 영역에서 전체 현재 라이브 리스트를 열고 싶다.
|
||||
- 사용자는 리스트에서 라이브 제목, 크리에이터, 시작 시각, 가격을 확인하고 싶다.
|
||||
- 사용자는 유료 라이브와 무료 라이브를 명확히 구분하고 싶다.
|
||||
- 사용자는 라이브 아이템을 터치했을 때 기존과 동일하게 비밀번호 입력, 결제 확인, 성인 인증 안내를 거쳐 입장하고 싶다.
|
||||
- 개발자는 새 목록 API와 화면은 v2 하위에 두되, 기존 입장 플로우는 중복 구현하지 않고 재사용하고 싶다.
|
||||
|
||||
---
|
||||
|
||||
## 7. Core Features
|
||||
|
||||
### 현재 진행 중인 라이브 리스트 API
|
||||
#### Endpoint Contract
|
||||
- Method: `GET`
|
||||
- Path: `/api/v2/home/on-air-lives`
|
||||
- Header: `Authorization: Bearer {accessToken}` optional
|
||||
- Query:
|
||||
- `page`: optional, default `0`, 0부터 시작하는 page index
|
||||
- `size`: 앱에서 보내지 않음. 서버에서 20으로 고정
|
||||
|
||||
#### Android Response Contract
|
||||
```kotlin
|
||||
data class HomeOnAirLivePageResponse(
|
||||
val items: List<HomeOnAirLiveResponse>,
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
val hasNext: Boolean
|
||||
)
|
||||
|
||||
data class HomeOnAirLiveResponse(
|
||||
val roomId: Long,
|
||||
val creatorNickname: String,
|
||||
val creatorProfileImage: String,
|
||||
val title: String,
|
||||
val price: Int,
|
||||
val beginDateTimeUtc: String
|
||||
)
|
||||
```
|
||||
|
||||
#### Requirements
|
||||
- DTO는 `@Keep`, `@SerializedName`을 사용해 기존 v2 data layer 관례를 따른다.
|
||||
- Retrofit API는 `@GET("/api/v2/home/on-air-lives")`와 nullable `Authorization` header를 사용한다.
|
||||
- 토큰이 비어 있으면 Authorization header를 보내지 않는다.
|
||||
- Repository/ViewModel은 기존 `Api -> Repository -> ViewModel -> Activity` 흐름을 따른다.
|
||||
- 첫 진입 시 `page = 0`을 요청한다.
|
||||
- `hasNext = true`이고 리스트 하단에 도달하면 다음 page를 요청한다.
|
||||
- 중복 로딩 방지를 위해 loading 중 추가 page 요청은 무시한다.
|
||||
|
||||
### 리스트 UI
|
||||
#### Requirements
|
||||
- 화면 배경은 검은색을 유지한다.
|
||||
- 상단 title bar는 Figma처럼 뒤로가기 + `On Air` 제목을 표시한다.
|
||||
- 리스트 좌우 여백은 Figma 기준 14dp를 따른다.
|
||||
- 아이템은 세로 리스트로 표시한다.
|
||||
- 프로필 이미지는 75dp 원형으로 표시한다.
|
||||
- 상단 메타 텍스트는 `LIVE HH:mm` 형태로 표시한다.
|
||||
- `HH:mm`은 `beginDateTimeUtc`를 UTC로 파싱한 뒤 디바이스 Timezone으로 변환한 시작 시각이다.
|
||||
- 제목은 18sp bold, 한 줄 말줄임으로 표시한다.
|
||||
- 크리에이터 닉네임은 14sp regular, 회색으로 표시한다.
|
||||
- 유료 라이브는 `ic_bar_cash`, 가격, `입장 캔`을 표시한다.
|
||||
- 무료 라이브는 cash 아이콘을 숨기고 `무료` 텍스트만 표시한다.
|
||||
- 이미지 로드는 기존 `loadUrl` 또는 기존 Glide 확장 관례를 따른다.
|
||||
|
||||
### 라이브 입장
|
||||
#### Requirements
|
||||
- 아이템 터치 시 바로 `LiveRoomActivity`를 열지 않는다.
|
||||
- 터치 시 `LiveViewModel.getRoomDetail(roomId)`를 호출한다.
|
||||
- 상세 응답을 받은 뒤 기존 `LiveNowAllActivity.enterLiveRoom`과 동일한 정책을 적용한다.
|
||||
- 방장(`manager.id == SharedPreferenceManager.userId`)이면 `enterRoom` 후 `LiveRoomActivity`를 연다.
|
||||
- 이미 결제했거나 무료인 방은 비공개 여부에 따라 비밀번호 다이얼로그 또는 직접 입장을 수행한다.
|
||||
- 유료 미결제 방은 비공개 여부에 따라 비밀번호 다이얼로그 또는 `LivePaymentDialog`를 표시한다.
|
||||
- 성인방은 기존 `ensureLoginAndAdultAuth` 정책을 v2 화면에 맞춰 재사용한다.
|
||||
- 입장 성공 시 오디오 재생 서비스를 중지한 뒤 `Constants.EXTRA_ROOM_ID`와 함께 `LiveRoomActivity`를 실행한다.
|
||||
|
||||
---
|
||||
|
||||
## 8. UX / UI Expectations
|
||||
- 첫 진입 시 기존 `LoadingDialog` 패턴으로 로딩 상태를 표시한다.
|
||||
- 추가 페이지 로딩은 기존 화면 스타일을 해치지 않는 최소 처리로 구현한다.
|
||||
- API 실패 또는 상세 조회 실패는 기존 unknown error toast 패턴을 따른다.
|
||||
- 빈 목록이면 리스트를 비우고 과도한 신규 빈 화면 디자인은 추가하지 않는다.
|
||||
- 뒤로가기는 현재 리스트 화면을 종료한다.
|
||||
|
||||
---
|
||||
|
||||
## 9. Technical Constraints
|
||||
- 신규 `Activity`, `ViewModel`, DTO, Repository, adapter/helper는 `kr.co.vividnext.sodalive.v2` 하위에 작성한다.
|
||||
- 현재 진행 중인 라이브 리스트 화면의 신규 화면/API/Repository/ViewModel/adapter/model은 `kr.co.vividnext.sodalive.v2.live.onair` 패키지 하위에 작성한다.
|
||||
- 메인 홈 추천 탭 코드는 신규 화면으로 이동하는 진입점 연결만 담당한다.
|
||||
- 레거시 파일은 직접 수정하지 않는다. 필요한 기능은 기존 public API를 호출해 사용한다.
|
||||
- `LiveViewModel`은 기존 Koin 등록을 재사용한다.
|
||||
- 기존 `BuildConfig` 값, token, URL을 로그/Toast에 노출하지 않는다.
|
||||
- Compose로 전환하지 않고 기존 XML View/ViewBinding/RecyclerView 패턴을 따른다.
|
||||
|
||||
---
|
||||
|
||||
## 10. Metrics
|
||||
- 별도 분석 이벤트는 이번 범위에 추가하지 않는다.
|
||||
- 기능 검증 기준은 API 호출, 페이지네이션, 가격 표시, 시작 시각 표시, 상세 조회 후 입장 분기 동작이다.
|
||||
|
||||
---
|
||||
|
||||
## 11. Open Questions
|
||||
- 없음. `LIVE ${라이브 시작시간}`은 `beginDateTimeUtc`를 디바이스 Timezone으로 변환한 `HH:mm` 표시로 확정한다.
|
||||
Reference in New Issue
Block a user