Files
sodalive-android/docs/prd/20260520_라이브썸네일컴포넌트_prd.md

149 lines
10 KiB
Markdown

# PRD: 라이브 썸네일 컴포넌트
## 1. Overview
Figma `24:4999`, `24:5017` 디자인을 기준으로 현재 라이브 중인 상태를 표시하는 Android XML Views 기반 라이브 썸네일 컴포넌트를 문서화한다.
---
## 2. Problem
- 현재 Figma의 프로필 이미지 영역은 빈 이미지로 표시되어 있으나, 실제 앱에서는 라이브 크리에이터의 프로필 또는 커버 이미지를 표시해야 한다.
- 라이브 썸네일은 세로형 프로필 variant와 가로형 상세 variant가 함께 필요하다.
- 라이브 제목과 크리에이터 이름이 지정된 영역을 넘으면 UI가 깨질 수 있으므로 모든 텍스트는 1줄 제한과 말줄임 처리가 필요하다.
- 기존 라이브 관련 layout은 화면별로 흩어져 있어, 새 Figma variant를 재사용 가능한 v2 widget 계약으로 분리할 필요가 있다.
---
## 3. Goals
- Figma `24:4999` 기준 세로형 `Simple` 라이브 썸네일을 제공한다.
- Figma `24:5017` 기준 가로형 `Detail` 라이브 썸네일을 제공한다.
- Figma에서 빈 이미지로 보이는 영역에는 실제 이미지 URL을 바인딩할 수 있는 `ImageView`를 제공한다.
- 모든 텍스트는 `maxLines=1`, `ellipsize=end`로 표시한다.
- LIVE 배지, 라이브 테두리, 배경, 간격, typography는 Figma 기준을 Android resource로 옮긴다.
- 이미지 로딩 라이브러리는 컴포넌트 내부에 고정하지 않고, 기존 호출부가 Coil/Glide 등 현재 화면의 방식을 사용해 이미지를 로드할 수 있도록 한다.
---
## 4. Non-Goals
- 이번 문서 범위에서는 서버 API 또는 DTO 필드명을 변경하지 않는다.
- 라이브 목록 화면 전체 개편, 정렬, pagination, filtering은 포함하지 않는다.
- Compose 컴포넌트 또는 Compose Theme를 추가하지 않는다.
- Figma에 없는 skeleton loading, shimmer, pressed animation, 시청자 수, 가격, 잠금 배지는 추가하지 않는다.
- 이미지 로딩 라이브러리 교체를 수행하지 않는다.
- 라이브 상태 계산 로직 또는 라이브 입장 정책을 새로 만들지 않는다.
---
## 5. Target Users
- 라이브 중인 크리에이터를 탐색하는 앱 사용자.
- 메인/라이브/크리에이터 화면에서 라이브 썸네일 UI를 재사용해야 하는 Android 개발자.
---
## 6. User Stories
- 사용자는 현재 라이브 중인 크리에이터를 LIVE 배지와 테두리로 빠르게 구분하고 싶다.
- 사용자는 라이브 제목과 크리에이터 이름이 길어도 레이아웃이 깨지지 않는 썸네일을 보고 싶다.
- 개발자는 같은 라이브 상태 UI를 세로형/가로형 variant로 일관되게 바인딩하고 싶다.
- 개발자는 기존 이미지 로딩 방식을 유지하면서 실제 이미지 URL만 연결하고 싶다.
---
## 7. Core Features
### Live Thumbnail Widget
현재 라이브 중인 콘텐츠를 `Simple`, `Detail` 두 variant로 표시한다.
#### Figma References
- Simple live thumbnail: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=24-4999&m=dev
- Detail live thumbnail: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=24-5017&m=dev
#### Variant Requirements
| Variant | Figma node | Size policy | Primary use |
| --- | --- | --- | --- |
| `Simple` | `24:4999` | 기본 폭 `70dp`, profile area `70dp x 76dp`, 이름 영역 `70dp` | 가로 스크롤 프로필 목록, 작은 추천 영역 |
| `Detail` | `24:5017` | 기본 `266dp x 99dp`, pill radius `90dp` | 넓은 카드형 라이브 추천 영역 |
#### Figma Token Requirements
- `Simple` root는 세로 배치, gap `6dp`, width `70dp`를 기준으로 한다.
- `Simple` 실제 이미지 영역은 `58dp x 58dp`, 원형 crop, top/start `6dp` 위치를 기준으로 한다.
- `Simple` 라이브 ring은 `70dp x 70dp`이며 ocean-blue gradient 또는 동일한 기존 drawable을 사용한다.
- `Simple` LIVE badge는 `50dp x 18dp`, black background, `#62CFFF` border `2dp`, radius `759dp`, icon `8dp`, text `12sp` regular white를 기준으로 한다.
- `Simple` 크리에이터 이름은 `@style/Typography.Body5`, white, center 정렬이다.
- `Detail` root는 `266dp x 99dp`, `gray_900` (`#202020`) background, `#62CFFF` border `2dp`, radius `90dp`, overflow clipped 형태를 기준으로 한다.
- `Detail` 실제 이미지 영역은 `75dp x 75dp`, left `10dp`, vertical center, 원형 crop을 기준으로 한다.
- `Detail` 텍스트 column은 left `93dp`, width `149dp`, vertical center, 내부 gap `4dp`를 기준으로 한다.
- `Detail` LIVE badge는 `Tag(size=s,type=live)` 형태로 black background, radius `100dp`, height `18dp`, horizontal padding `4dp`, icon/text gap `4dp`를 기준으로 한다.
- `Detail` LIVE badge는 `Simple`의 cyan stroke badge와 달리 stroke 없는 capsule background를 사용한다.
- `Detail` 라이브 시작 시간은 `@style/Typography.Body6`, `gray_500` (`#959595`)를 기준으로 한다.
- `Detail` 라이브 제목은 `@style/Typography.Heading4`, white를 기준으로 한다.
- `Detail` 크리에이터 이름은 `@style/Typography.Body6`, `gray_500` (`#959595`)를 기준으로 한다.
#### Display Requirements
- 실제 이미지는 Figma placeholder가 아니라 `imageUrl` 데이터로 표시한다.
- 이미지 로딩 실패 시 placeholder 정책은 호출 화면의 기존 이미지 로딩 정책을 따른다.
- `Simple`은 크리에이터 이름만 표시한다.
- `Detail`은 LIVE badge, 라이브 시작 시간, 라이브 제목, 크리에이터 이름을 표시한다.
- `liveStartTimeText`가 비어 있으면 `00:00`을 강제하지 않고 빈 문자열 또는 호출부 정책을 따른다.
- 모든 텍스트는 한 줄만 표시한다.
- 모든 텍스트는 영역을 초과하면 끝 말줄임 처리한다.
- LIVE 문구는 고정 문자열 `LIVE`로 표시한다.
- 터치 동작은 컴포넌트 내부에서 목적지를 결정하지 않고 호출부 callback으로 위임한다.
#### Data Contract Requirements
- 최소 데이터 계약은 다음 정보를 포함해야 한다.
- `liveId`: 라이브 식별자.
- `creatorId`: 크리에이터 식별자.
- `imageUrl`: 실제로 표시할 프로필 또는 라이브 이미지 URL.
- `title`: 라이브 제목. `Detail` variant에서 사용한다.
- `creatorName`: 크리에이터 이름.
- `liveStartTimeText`: 라이브 시작 시간 표시 문자열. `Detail` variant에서 사용한다.
- UI는 `imageUrl`의 의미가 프로필 이미지인지 커버 이미지인지 판단하지 않는다. 호출부가 variant 목적에 맞는 URL을 전달한다.
#### Edge Cases
- `imageUrl`이 비어 있으면 호출부 이미지 로딩 정책에 따라 placeholder 또는 빈 상태를 표시한다.
- `creatorName`이 비어 있으면 해당 TextView는 빈 문자열로 유지하고 layout은 유지한다.
- `title`이 비어 있으면 `Detail` title TextView는 빈 문자열로 유지하고 layout은 유지한다.
- 긴 한글/영문/숫자 혼합 텍스트는 1줄 말줄임으로 처리한다.
- `liveStartTimeText`가 긴 문자열이어도 1줄 말줄임으로 처리한다.
---
## 8. UX / UI Expectations
- 전체 컴포넌트는 어두운 배경에서 사용하는 것을 전제로 한다.
- 라이브 상태는 `#62CFFF` 계열 ring/border와 LIVE badge로 식별 가능해야 한다.
- 실제 이미지가 원형 영역을 벗어나지 않도록 centerCrop + circle clipping을 적용한다.
- 세로형과 가로형 모두 Figma의 둥근 형태와 clipping을 유지한다.
- 텍스트가 길어도 컴포넌트의 지정 width를 밀어내지 않아야 한다.
- Android 접근성 관점에서 장식용 LIVE dot/ring은 `contentDescription=@null`로 둔다.
---
## 9. Technical Constraints
- 현재 프로젝트는 Android XML Views + Kotlin custom View + ViewBinding/resource 기반이므로 XML layout과 Kotlin custom view 패턴을 우선한다.
- 신규 Kotlin 코드는 `app/src/main/java/kr/co/vividnext/sodalive/v2` 하위 패키지에 작성한다.
- 재사용 가능한 위젯은 `kr.co.vividnext.sodalive.v2.widget.livethumbnail` 하위에 둔다.
- 기존 `AudioContentCardView`, `CreatorRanking*CardView`처럼 custom view는 텍스트/상태를 바인딩하고, 실제 이미지 로딩은 `imageView()`를 통해 호출부가 처리할 수 있게 한다.
- `gray_900`, `gray_500`, `white` 등 기존 color resource를 우선 재사용한다.
- Pretendard font는 기존 `@font/regular`, `@font/medium`, `@font/bold`를 사용한다.
- 기존 Typography/spacing/radius token과 정확히 대응되는 값은 신규 token을 만들지 않고 재사용한다.
- `#62CFFF` 또는 ocean-blue gradient에 해당하는 기존 리소스가 없으면 구현 단계에서 drawable/color resource를 추가하고, drawable에서는 literal 색상보다 color resource를 참조한다.
- 기존 레거시 layout(`item_home_live.xml`, `item_live_now.xml`, `item_live_now_all.xml`)은 참고 대상으로만 사용하고, 이번 widget 계약에서 직접 변경하지 않는다.
---
## 10. Metrics
- `Simple` variant가 Figma `24:4999`의 주요 크기, ring, LIVE badge, 1줄 이름 표시와 일치한다.
- `Detail` variant가 Figma `24:5017`의 주요 크기, 배경, border, 이미지, LIVE/time/title/name 배치와 일치한다.
- 실제 이미지 URL을 호출부에서 `ImageView`에 로드할 수 있다.
- 컴포넌트 내부에는 Coil/Glide 등 이미지 로딩 라이브러리 import나 `.load()` 호출이 없다.
- 모든 텍스트 TextView에 `maxLines=1``ellipsize=end`가 적용된다.
- 원형 이미지 clipping과 Figma 주요 spacing/size/badge 형태는 XML/코드 속성 확인 또는 screenshot/manual QA로 확인한다.
- 관련 local unit test, Android resource merge, build가 성공한다.
---
## 11. Open Questions
- `Simple` variant의 이미지 URL은 기본적으로 크리에이터 프로필 이미지를 전달하는 것으로 가정한다.
- `Detail` variant의 이미지 URL은 현재 Figma가 프로필형 원형 이미지를 사용하므로 프로필 이미지를 전달하는 것으로 가정한다. 호출 화면에서 라이브 커버를 써야 한다면 같은 `imageUrl` 계약에 다른 URL을 전달한다.
- `liveStartTimeText` 포맷은 이 문서에서 계산하지 않고 호출부가 완성된 문자열로 전달한다.
- Figma `get_design_context`와 screenshot 확인 결과, 빈 이미지 영역은 실제 이미지 바인딩 영역으로 문서화했다.