15 KiB
15 KiB
PRD: 콘텐츠 랭킹 위젯 컴포넌트
1. Overview
Figma 20:3715, 20:3718, 20:3721, 20:3724 디자인을 기준으로 콘텐츠 랭킹을 순위 구간별 카드 형태로 표현하는 Android XML Views 기반 위젯 컴포넌트를 개발한다.
2. Problem
- 콘텐츠 랭킹은 순위 구간에 따라 카드 UI와 한 줄 배치 개수가 달라져야 한다.
- 기기 폭이 하나로 고정되지 않으므로 Figma metadata size를 실제 이미지 크기로 고정하면 다양한 화면 폭에서 재사용하기 어렵다.
- 2위
7위와 8위10위는 카드 UI와 텍스트 크기가 달라질 수 있어 순위 구간별 표시 contract를 명확히 해야 한다. - 순위 변동, 신규 진입, 차단 관계, 터치 가능 여부가 함께 표시되어야 하므로 데이터와 UI 상태 계약을 분리해야 한다.
3. Goals
- Figma 4개 노드 기준의 콘텐츠 랭킹 카드 variant와 row 배치 정책을 제공한다.
- 이미지 크기는 컴포넌트 내부에서 고정하지 않고 실제 사용하는 row container 폭과 row count에 맞춰 계산한다.
- 순위 구간별 한 줄 배치 규칙을 제공한다.
- 1위: 한 줄에 1개, Figma
20:3715, 큰 콘텐츠 카드. - 2위~7위: 한 줄에 2개, Figma
20:3718, 2열 정사각형 카드. - 8위~10위: 한 줄에 3개, Figma
20:3721, 3열 정사각형 카드. - 11위 이후: 가로형으로 한 줄에 1개, Figma
20:3724, 가로형 카드.
- 1위: 한 줄에 1개, Figma
- 콘텐츠명은 순위 구간별 글자 수 기준을 초과하면 한 줄 말줄임 처리한다.
rank-num영역은 이전 순위와 비교한 변동 상태를 표시한다.- 차단 관계인 크리에이터의 콘텐츠는 이미지 블러, 정보 비노출 또는 대체문구, 터치 불가 상태로 표시한다.
- 기존 화면 일괄 적용은 구현 계획에서 별도 task로 제한하고, 컴포넌트 계약을 우선 고정한다.
4. Non-Goals
- 이번 범위에서는 서버 API 설계나 응답 필드명을 확정하지 않는다. 필요한 클라이언트 데이터 계약만 문서화한다.
- 크리에이터 랭킹 위젯, 오디오 콘텐츠 카드, 콘텐츠 상세 화면, 검색/필터 UI는 변경하지 않는다.
- Compose 컴포넌트 또는 Compose Theme를 추가하지 않는다.
- Figma에 없는 skeleton loading, shimmer, pressed animation, 별도 badge, 광고 영역, 페이지네이션 UI를 추가하지 않는다.
- 차단/차단 해제 기능 자체를 새로 만들지 않는다.
- 내가 차단했는지, 나를 차단했는지를 UI에서 구분해 표시하지 않는다.
- 이미지 로딩 라이브러리 교체를 수행하지 않는다.
5. Target Users
- 콘텐츠 랭킹 화면을 보는 앱 사용자.
- XML 레이아웃과 RecyclerView 기반 랭킹 UI를 구현/유지보수하는 Android 개발자.
6. User Stories
- 사용자는 인기 콘텐츠의 상위 순위를 구간별 강조도 차이로 빠르게 확인하고 싶다.
- 사용자는 콘텐츠의 순위가 올랐는지, 내려갔는지, 유지됐는지, 신규 진입했는지 알고 싶다.
- 사용자는 차단 관계에 있는 크리에이터의 콘텐츠 정보가 노출되지 않기를 기대한다.
- 개발자는 순위 구간별 UI를 하나의 명확한 계약으로 바인딩하고 싶다.
7. Core Features
Content Ranking Widget
콘텐츠 랭킹 목록을 순위 구간별 카드 variant와 행 배치 규칙으로 표시한다.
Figma References
- Rank 1 large content card: 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=20-3715&m=dev
- Rank 2~7 two-column content card: 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=20-3718&m=dev
- Rank 8~10 three-column content card: 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=20-3721&m=dev
- Rank 11+ horizontal content card: 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=20-3724&m=dev
Variant and Row Requirements
| Rank range | Figma node | Row count | UI variant | Size policy |
|---|---|---|---|---|
| 1 | 20:3715 |
한 줄에 1개 | Large |
실제 사용 영역 폭을 1등분하고 Figma 큰 카드 비율로 표시 |
| 2~7 | 20:3718 |
한 줄에 2개 | MediumGrid |
실제 사용 영역 폭을 2등분해 정사각형으로 표시 |
| 8~10 | 20:3721 |
한 줄에 3개 | SmallGrid |
실제 사용 영역 폭을 3등분해 정사각형으로 표시 |
| 11+ | 20:3724 |
한 줄에 1개 | Horizontal |
실제 사용 영역 폭을 1등분하고 Figma 가로형 비율로 표시 |
Variant Details
Large: 1위 전용 카드다. 배경 영역, 중앙 콘텐츠 이미지, 하단 콘텐츠명/크리에이터명, 순위 숫자, 순위 변동 표시를 포함한다.MediumGrid: 2위~7위 전용 정사각형 카드다. 2열 배치를 기준으로 콘텐츠명은22spbold 스타일을 사용한다.SmallGrid: 8위~10위 전용 정사각형 카드다. 3열 배치를 기준으로 콘텐츠명은14spbold 스타일을 사용한다.Horizontal: 11위 이후 전용 가로형 카드다. 좌측 순위/변동, 중앙 이미지, 우측 콘텐츠명/크리에이터명 영역을 가진다.- Figma metadata size는 참고용 비율 확인에만 사용하고, 구현에서 고정 dp 크기로 사용하지 않는다.
Text Requirements
- 모든 텍스트는
maxLines=1,ellipsize=end로 한 줄 말줄임 처리한다. - 1위 콘텐츠명은 16자를 초과하면 말줄임 처리한다.
- 2위~10위 콘텐츠명은 8자를 초과하면 말줄임 처리한다.
- 11위 이후 콘텐츠명은 12자를 초과하면 말줄임 처리한다.
- 크리에이터명도 한 줄 제한을 유지하고, 실제 잘림은 레이아웃 폭과
ellipsize=end에 따른다.
Figma Token Requirements
- 공통 카드 이미지 radius는
radius_14또는14dp를 사용한다. - 공통 dim gradient는 위쪽
rgba(0,0,0,0), 아래쪽 black, opacity50%, 전환 시작점64.423%기준으로 구현한다. - 1위 카드에는 Figma 기준 배경 blur/dim 영역과 중앙 콘텐츠 이미지 영역을 함께 둔다.
- 공통
rank-num배경은gray_900(#202020), radius4dp, horizontal padding4dp, gap2dp를 사용한다. - 공통
rank-num숫자는 Pretendard Variable Medium,16sp, line-height1.45, color white를 사용한다. - 공통 caret icon 크기는
14dp x 14dp를 기준으로 한다. - 순위 숫자는 Pattaya Regular를 사용하고 white~
#EEEEEEvertical gradient와0px 0px 4px rgba(0,0,0,0.48)shadow를 적용한다. Large콘텐츠명은 Pretendard Variable Bold,22sp, line-height1.45, color white를 기준으로 한다.Large크리에이터명은 Pretendard Variable Regular,12sp, line-height normal, color white를 기준으로 한다.MediumGrid콘텐츠명은 Pretendard Variable Bold,22sp, line-height1.45, color white를 기준으로 한다.MediumGrid크리에이터명은 Pretendard Variable Regular,12sp, line-height normal, color white를 기준으로 한다.SmallGrid콘텐츠명은 Pretendard Variable Bold,14sp, line-height normal, color white를 기준으로 한다.SmallGrid크리에이터명은 Pretendard Variable Regular,12sp, line-height normal, color white를 기준으로 한다.Horizontal콘텐츠명은 Pretendard Variable Bold,18sp, line-height1.45, color white를 기준으로 한다.Horizontal크리에이터명은 Pretendard Variable Regular,14sp, line-height1.45, color white를 기준으로 한다.
Rank Change Requirements
- 모든 variant는 현재 순위 숫자를 표시한다.
rank-num영역은 순위 변동 상태를 표시한다.- 순위 상승:
ic_rank_caret_increase와 변동 숫자를 표시한다. - 순위 하락:
ic_rank_caret_decrease와 변동 숫자를 표시한다. - 순위 동일: 숫자 없이
ic_rank_caret_stay아이콘만 표시한다. - 신규 진입:
rank-num대신ic_rank_new이미지를 표시한다.
- 순위 상승:
- 신규 진입이 아니고 순위 동일이 아닌 경우
rank-num에는 이전 순위 대비 변동 숫자를 표시한다.
Blocked Creator Requirements
- 내가 차단했거나 나를 차단한 크리에이터는 하나의 차단 관계 상태로만 전달받는다.
- 차단 관계 상태에서는 콘텐츠 이미지를 블러 처리한다.
- 차단 관계 상태의 1위~10위 카드는 콘텐츠명과 크리에이터 이름을 표시하지 않는다.
- 차단 관계 상태의 1위~10위 카드는 이름 영역을 숨겨도 하단 dim gradient 영역은 유지한다.
- 차단 관계 상태의 11위 이후 가로형 카드는 콘텐츠명과 크리에이터 이름 대신
접근할 수 없는 정보입니다.한 줄만 표시한다. - 차단 관계 상태의 카드는 터치할 수 없다.
- 접근 가능 상태의 카드는 터치할 수 있고, 터치 시 호출부가 콘텐츠 상세 이동 등 후속 동작을 처리한다.
Data Contract Requirements
- 최소 데이터 계약은 다음 정보를 포함해야 한다.
contentId: 콘텐츠 식별자.creatorId: 크리에이터 식별자.rank: 현재 순위. 1부터 시작한다.previousRank: 이전 순위. 신규 진입이면 null 허용.rankChangeType:increase,decrease,stay,new중 하나.rankChangeAmount: 신규 진입이 아닌 경우 표시할 변동 숫자.contentName: 콘텐츠명.creatorName: 크리에이터 이름.imageUrl: 콘텐츠 이미지 URL.isBlocked: 내가 차단했거나 나를 차단한 차단 관계 여부.
- UI는
isBlocked만 사용하고 차단 방향은 구분하지 않는다. - 순위 변동 타입은 콘텐츠 랭킹 전용 타입을 새로 만들지 않고, 크리에이터 랭킹에서 사용하는 변동 타입을 공용 이름으로 변경해 함께 사용한다.
Edge Cases
- 랭킹 데이터가 0개이면 위젯 영역은 표시하지 않거나 호출부의 empty 정책을 따른다.
- 랭킹 데이터가 1~10개이면 존재하는 순위까지만 구간 규칙을 적용한다.
rank가 누락되거나 1보다 작으면 호출부 데이터 오류로 간주하고 해당 항목을 표시하지 않는다.rankChangeType이new이면previousRank와rankChangeAmount가 없어도 된다.rankChangeType이stay이면rankChangeAmount가 있어도 숫자를 표시하지 않는다.rankChangeType이increase또는decrease인데rankChangeAmount가 없으면 구현 단계에서 데이터 검증 정책을 정한다.- 이미지 로딩 실패 시 placeholder 정책은 기존 이미지 로딩 계층 또는 호출 화면 정책을 따른다.
8. UX / UI Expectations
- 전체 위젯은 어두운 배경 위에서 사용하는 것을 전제로 한다.
- 카드 이미지는 rounded corner를 가진다.
- 카드 이미지는 공통 dim gradient를 가진다. 차단 관계 상태에서 이름을 숨기더라도 1위~10위의 gradient overlay는 유지한다.
- 1위 카드는 배경 영역과 중앙 콘텐츠 이미지가 분리된 형태를 유지한다.
- 2위
7위와 8위10위는 각각 2열/3열 배치에 맞춰 같은 데이터 계약을 다른 variant로 표시한다. - 11위 이후 카드는 좌측 순위, 중앙 이미지, 우측 텍스트 영역을 가진다.
- 이미지 크기는 고정 dp로 박지 않고 row container 폭에서 계산한다.
- 정사각형 variant는 계산된 카드 폭과 동일한 높이로 표시한다.
- 가로형 variant는 부모 폭을 채우고 Figma 가로형 비율에 맞는 높이를 유지한다.
- 차단 관계 상태는 사용자가 상세로 들어갈 수 없음을 시각적으로 알 수 있어야 한다.
9. Technical Constraints
- 현재 프로젝트는 Android XML Views + ViewBinding + RecyclerView 기반이므로 XML 레이아웃과 Kotlin custom view/adapter 패턴을 우선한다.
- 신규 Kotlin 코드는
kr.co.vividnext.sodalive.v2패키지 하위에 작성한다. - 재사용 가능한 위젯은
kr.co.vividnext.sodalive.v2.widget.contentranking또는 기능 범위에 맞는 하위 패키지에 둔다. - 기존 크리에이터 랭킹 위젯 문서는 참고 대상으로만 사용하고, 콘텐츠 랭킹 contract는 별도 파일로 분리한다.
- 크리에이터 랭킹에서 사용 중인 순위 변동 타입은
RankingChangeType처럼 랭킹 공용 이름으로 변경하고, 크리에이터 랭킹과 콘텐츠 랭킹이 동일 타입을 참조하도록 한다. - 기존 프로젝트의 이미지 로딩 방식이 화면별로 Glide/Coil을 함께 사용하므로, 컴포넌트 내부에 특정 이미지 로더를 강제하지 않는 API를 우선한다.
- 차단 관계 이미지 블러는 기존
kr.co.vividnext.sodalive.common.image.BlurTransformation등 기존 blur 구현의 재사용 가능성을 먼저 검토한다. ic_rank_caret_increase,ic_rank_caret_decrease,ic_rank_caret_stay,ic_rank_new리소스가 없으면 구현 단계에서 디자인 에셋 추가가 필요하다.
10. Metrics
- 순위 구간별 row count가 요구사항과 일치한다.
- 1위는
Large, 2위7위는10위는MediumGrid, 8위SmallGrid, 11위 이후는Horizontalvariant로 바인딩된다. - 콘텐츠명은 순위 구간별 글자 수 기준과 한 줄 말줄임 계약을 만족한다.
rankChangeType별 아이콘/숫자 표시가 문서와 일치한다.stay상태에서는 숫자 없이ic_rank_caret_stay만 표시된다.new상태에서는rank-num대신ic_rank_new가 표시된다.- 차단 관계 상태에서 이미지 블러, 이름 비노출/대체문구, 터치 불가가 모두 적용된다.
- 차단 관계 상태에서 1위~10위 카드의 gradient overlay가 유지된다.
- 이미지 크기가 고정 dp가 아닌 부모 폭과 row count 기반으로 계산된다.
- 관련 unit test와 Android resource merge/build가 성공한다.
11. Open Questions
- 서버 응답에 이전 순위, 신규 진입 여부, 차단 관계 여부가 이미 포함되는지 확인이 필요하다. 없으면 API/DTO 확장이 별도 백엔드 협의 항목이다.
- Figma
get_design_context확인 결과 typography/color/radius 토큰은 본 문서의Figma Token Requirements에 반영했다. - 콘텐츠명 글자 수 제한은 사용자 요구사항에 따라 1위 16자, 2위~10위 8자, 11위 이후 12자로 확정한다.
- 차단 관계 상태에서 1위~10위 카드의 이름 영역을 숨길 때 gradient 영역은 유지하는 것으로 확정한다.
- 순위 변동 타입은 크리에이터 랭킹과 콘텐츠 랭킹이 같은 데이터이므로, 구현 시 기존 크리에이터 랭킹 타입을 공용
RankingChangeType으로 rename해 재사용하는 것으로 확정한다.