332 lines
26 KiB
Markdown
332 lines
26 KiB
Markdown
# PRD: 크리에이터 채널 커뮤니티 탭
|
|
|
|
## 1. Overview
|
|
크리에이터 채널의 `커뮤니티` 탭에서 커뮤니티 게시글 수, 리스트/썸네일 보기 전환, 게시글 목록, 본인 채널 글쓰기 CTA와 스크롤 pagination을 제공한다.
|
|
|
|
---
|
|
|
|
## 2. Problem
|
|
- 크리에이터 채널 컨테이너와 `홈`, `라이브`, `오디오`, `시리즈` 탭은 별도 문서에서 정의되었지만, `커뮤니티` 탭의 API 계약과 Figma 기반 UI 요구사항은 별도 정의가 필요하다.
|
|
- 사용자는 크리에이터 채널에서 커뮤니티 게시글을 일반 피드 형태 또는 썸네일 그리드 형태로 전환해 탐색할 수 있어야 한다.
|
|
- 커뮤니티 탭의 Sort-bar 우측 액션은 오디오/시리즈 탭의 정렬 선택이 아니라 리스트/썸네일 보기 토글이어야 한다.
|
|
- 커뮤니티 게시글은 공지 고정 여부, 유료 글 가격, 이미지/오디오 포함 여부, 댓글 가능 여부에 따라 표시 상태가 달라질 수 있다.
|
|
- 크리에이터 본인의 채널에서는 하단 고정 `커뮤니티 글 올리기` CTA를 제공해야 한다.
|
|
- 목록이 길어질 수 있으므로 `CreatorChannelCommunityTabResponse.hasNext == true`일 때 다음 페이지를 자동 로딩해야 한다.
|
|
|
|
---
|
|
|
|
## 3. Goals
|
|
- Figma 전체 리스트형 `290:9061` 기준으로 크리에이터 채널 `커뮤니티` 탭의 리스트형 UI 요구사항을 정의한다.
|
|
- Figma 전체 썸네일형 `290:9073` 기준으로 썸네일형 UI 요구사항을 정의한다.
|
|
- Figma 전체 리스트형 + 본인 채널 `665:19021` 기준으로 본인 채널 UI 요구사항을 정의한다.
|
|
- Figma 유료이고 구매하지 않은 게시글 `290:9066` 기준으로 유료 미구매 게시글 표시 요구사항을 정의한다.
|
|
- 본인 채널 + 썸네일형은 게시글 표시 UI만 썸네일형으로 변경하고 하단 고정 `커뮤니티 글 올리기` CTA는 동일하게 유지한다.
|
|
- 본인 채널에 본인이 쓴 커뮤니티 게시글의 리스트형 item은 우측 상단에 더보기 버튼을 표시하고, 유료 게시글이면 가격을 함께 표시한다.
|
|
- API 응답의 `creatorProfileUrl`, `existOrdered`를 사용해 작성자 프로필 이미지와 유료 게시글 구매 여부를 표시한다.
|
|
- 이번 탭에서 게시글 item 터치 시 별도 동작을 수행하지 않는다.
|
|
- 오디오 게시글 재생은 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
|
- API endpoint `GET /api/v2/creator-channels/{creatorId}/community`를 기준으로 최초 조회와 pagination 요구사항을 정의한다.
|
|
- 최초 조회 query parameter 기본값은 `page=0`, `size=20`으로 둔다.
|
|
- Sort-bar 좌측에는 전체 커뮤니티 게시글 수를 표시한다.
|
|
- Sort-bar 우측에는 현재 보기 방식 label과 icon을 표시하고, 터치할 때마다 `리스트형`과 `썸네일형`을 토글한다.
|
|
- 보기 방식 기본값은 `리스트형`이며 icon은 `ic_new_list`를 사용한다.
|
|
- 썸네일형으로 토글되면 label은 `썸네일형`, icon은 `ic_new_grid`를 사용한다.
|
|
- `리스트형`, `썸네일형`, `커뮤니티 글 올리기` 등 사용자 표시 문구는 다국어 문자열 리소스로 관리한다.
|
|
- 응답의 `hasNext`가 `true`이면 현재 `page + 1` 페이지를 추가 로딩한다.
|
|
|
|
---
|
|
|
|
## 4. Non-Goals
|
|
- 크리에이터 채널 상단 header, title bar, 공통 main tab-bar 구조 자체를 재설계하지 않는다.
|
|
- `홈`, `라이브`, `오디오`, `시리즈`, `화보`, `팬Talk`, `후원` 탭의 상세 구현은 이번 범위에서 제외한다.
|
|
- 커뮤니티 게시글 상세 화면은 다음 범위에서 추가하며, 이번 범위에서 신규 상세 화면을 구현하지 않는다.
|
|
- 댓글 화면, 좋아요 API, 댓글 작성 API, 결제/구매 플로우 내부 동작 변경은 이번 범위에서 제외한다.
|
|
- 커뮤니티 글 작성 화면 내부 구현은 이번 범위에서 제외하고 CTA 진입점만 정의한다.
|
|
- API schema를 임의 변경하거나 서버 응답 필드명을 클라이언트에서 새로 정의하지 않는다.
|
|
- 서버 정렬, 필터, 검색, pull-to-refresh, skeleton/shimmer는 이번 범위에서 제외한다.
|
|
- Figma asset을 localhost URL 그대로 앱 코드에 직접 의존하지 않는다.
|
|
|
|
---
|
|
|
|
## 5. Target Users
|
|
- 크리에이터 채널에서 커뮤니티 게시글을 탐색하는 앱 사용자.
|
|
- 게시글의 이미지, 오디오, 유료 여부, 좋아요/댓글 수를 목록에서 확인하려는 앱 사용자.
|
|
- 피드형 목록과 썸네일 그리드를 상황에 맞게 전환해 보고 싶은 앱 사용자.
|
|
- 본인 채널에서 커뮤니티 글 작성 화면으로 진입하려는 크리에이터.
|
|
- `kr.co.vividnext.sodalive.v2` 하위 크리에이터 채널 탭을 구현/유지보수하는 Android 개발자.
|
|
|
|
---
|
|
|
|
## 6. User Stories
|
|
- 사용자는 크리에이터 채널의 `커뮤니티` 탭에서 전체 커뮤니티 게시글 수를 확인하고 싶다.
|
|
- 사용자는 커뮤니티 게시글을 기본 리스트형으로 보고 싶다.
|
|
- 사용자는 Sort-bar 우측 토글을 눌러 커뮤니티 게시글을 썸네일형으로 전환하고 싶다.
|
|
- 사용자는 다시 토글을 눌러 썸네일형에서 리스트형으로 돌아오고 싶다.
|
|
- 사용자는 공지로 고정된 게시글을 목록에서 구분하고 싶다.
|
|
- 사용자는 유료 커뮤니티 글의 가격을 목록 또는 썸네일에서 확인하고 싶다.
|
|
- 사용자는 게시글 본문, 이미지, 좋아요 수, 댓글 수, 댓글 가능 여부를 목록에서 확인하고 싶다.
|
|
- 사용자는 목록 하단까지 스크롤하면 다음 페이지가 자연스럽게 이어서 로딩되길 기대한다.
|
|
- 사용자는 크리에이터가 아직 커뮤니티 글을 작성하지 않은 경우 불필요한 목록 UI 없이 empty 문구만 보고 싶다.
|
|
- 크리에이터 본인은 본인 채널의 `커뮤니티` 탭에서 하단 고정 버튼으로 커뮤니티 글 작성 화면에 진입하고 싶다.
|
|
|
|
---
|
|
|
|
## 7. Core Features
|
|
|
|
### Creator Channel Community Tab API
|
|
`커뮤니티` 탭 진입과 추가 로딩 시 크리에이터별 커뮤니티 탭 데이터를 조회한다.
|
|
|
|
#### Requirements
|
|
- API endpoint는 `GET /api/v2/creator-channels/{creatorId}/community`이다.
|
|
- `creatorId`는 path variable로 전달한다.
|
|
- Query parameters는 `page`, `size`를 사용한다.
|
|
- 최초 조회 기본값은 `page=0`, `size=20`이다.
|
|
- 정렬 또는 보기 방식 query parameter는 사용하지 않는다.
|
|
- 보기 방식이 변경되어도 API를 재호출하지 않고 현재 로드된 데이터를 다른 UI로 표시한다.
|
|
- `hasNext == true`일 때 다음 페이지 요청은 현재 응답의 `page + 1` 값을 사용한다.
|
|
- 중복 pagination 요청이 발생하지 않도록 loading 중 추가 요청을 막아야 한다.
|
|
|
|
#### Response Contract
|
|
```kotlin
|
|
data class CreatorChannelCommunityTabResponse(
|
|
val communityPostCount: Int,
|
|
val communityPosts: List<CreatorChannelCommunityPostResponse>,
|
|
val page: Int,
|
|
val size: Int,
|
|
@JsonProperty("hasNext")
|
|
val hasNext: Boolean
|
|
)
|
|
|
|
data class CreatorChannelCommunityPostResponse(
|
|
val postId: Long,
|
|
val creatorId: Long,
|
|
val creatorNickname: String,
|
|
val creatorProfileUrl: String,
|
|
val createdAtUtc: String,
|
|
val content: String,
|
|
val imageUrl: String?,
|
|
val audioUrl: String?,
|
|
val price: Int,
|
|
val existOrdered: Boolean,
|
|
@JsonProperty("isCommentAvailable")
|
|
val isCommentAvailable: Boolean,
|
|
val likeCount: Int,
|
|
val commentCount: Int,
|
|
@JsonProperty("isPinned")
|
|
val isPinned: Boolean
|
|
)
|
|
```
|
|
|
|
#### Edge Cases
|
|
- 최초 조회 실패 시 기존 크리에이터 채널 탭의 error/retry 패턴을 따른다.
|
|
- 다음 페이지 로딩 실패 시 기존 목록은 유지하고 기존 pagination 실패 표시 정책을 따른다.
|
|
- 다음 페이지 응답의 `communityPosts`가 비어 있어도 `hasNext` 값 기준으로 이후 로딩 가능 여부를 갱신한다.
|
|
- 서버 응답의 `page`, `size`가 요청 상태와 다를 경우 구현 계획 단계에서 기존 ViewModel 상태 동기화 패턴을 확인해 따른다.
|
|
- `communityPostCount > 0`이지만 첫 페이지 `communityPosts`가 비어 있는 경우 기존 목록 탭의 empty/error 정책을 구현 계획 단계에서 확인해 따른다.
|
|
- `creatorProfileUrl`이 비어 있거나 이미지 로딩에 실패하면 기존 프로필 placeholder 정책을 따른다.
|
|
|
|
### Sort Bar and View Mode Toggle
|
|
Sort-bar는 전체 커뮤니티 게시글 수와 현재 보기 방식을 표시하고, 우측 액션 터치 시 리스트형/썸네일형을 전환한다.
|
|
|
|
#### Requirements
|
|
- Figma 전체 리스트형 기준 Sort-bar는 `290:9061` 내 `sort-bar`이다.
|
|
- Figma 전체 썸네일형 기준 Sort-bar는 `290:9073` 내 `sort-bar`이다.
|
|
- Figma 본인 채널 리스트형 기준 Sort-bar는 `665:19021` 내 `sort-bar`이다.
|
|
- 좌측에는 `전체`와 `communityPostCount`를 표시한다.
|
|
- 우측에는 현재 보기 방식 label과 icon을 표시한다.
|
|
- 기본 보기 방식은 리스트형이다.
|
|
- 리스트형 label은 `리스트형`이며 icon은 `ic_new_list`를 사용한다.
|
|
- 썸네일형 label은 `썸네일형`이며 icon은 `ic_new_grid`를 사용한다.
|
|
- 우측 보기 방식 영역을 터치하면 `리스트형 -> 썸네일형 -> 리스트형` 순서로 토글한다.
|
|
- 보기 방식 label은 한국어/영어/일본어 다국어 문자열 리소스로 관리한다.
|
|
- 보기 방식 변경 시 스크롤 위치 유지 여부는 구현 계획 단계에서 기존 탭 목록 상태 정책을 확인해 결정한다.
|
|
|
|
#### Edge Cases
|
|
- 다국어 label 길이가 길어져도 Sort-bar 좌측 카운트와 우측 토글이 겹치지 않아야 한다.
|
|
- 빠르게 반복 터치해도 현재 보기 방식 상태와 Adapter item type이 불일치하지 않아야 한다.
|
|
- 화면 회전 또는 Fragment/View 재생성 후 보기 방식 상태는 ViewModel 또는 saved state 정책에 따라 유지되어야 한다.
|
|
|
|
### Community Post List Mode
|
|
리스트형은 커뮤니티 게시글을 피드 카드 형태로 세로 표시한다.
|
|
|
|
#### Requirements
|
|
- Figma 전체 리스트형 기준 콘텐츠 영역은 `290:9061`이다.
|
|
- Figma 본인 채널 리스트형 기준 콘텐츠 영역은 `665:19021`이다.
|
|
- `communityPosts`를 세로 목록으로 표시한다.
|
|
- 각 item은 Figma `feed` 구조처럼 `gray/900 #202020` 배경, 14dp radius, 14dp padding을 기준으로 한다.
|
|
- 게시글 작성자 영역에는 `creatorProfileUrl` 기반 프로필 이미지, `creatorNickname`, `createdAtUtc`의 상대 시간 표시를 제공한다.
|
|
- `isPinned == true`인 게시글은 item 상단에 공지 표시를 노출한다.
|
|
- 공지 label은 Figma 기준 `Notice`로 보이지만 사용자 표시 문구는 다국어 문자열 리소스로 관리한다.
|
|
- 게시글 본문에는 `content`를 표시한다.
|
|
- 무료 게시글 또는 `existOrdered == true`인 구매 완료 게시글은 `imageUrl != null`이면 본문 아래에 이미지를 표시한다.
|
|
- 본인 채널에 본인이 쓴 게시글은 구매 여부와 관계없이 작성자 권한으로 본문 아래에 이미지를 표시한다.
|
|
- 본인 또는 구매한 사용자에게 표시되는 게시글에서 `audioUrl != null`이고 `imageUrl != null`이면 이미지 가운데에 기존 커뮤니티 페이지와 동일한 재생/일시정지 버튼을 표시한다.
|
|
- 재생/일시정지 버튼 터치 시 기존 `CreatorCommunityMediaPlayerManager`로 오디오 재생 상태를 제어한다.
|
|
- `audioUrl == null`이면 재생 버튼을 표시하지 않는다.
|
|
- `price > 0 && existOrdered == false`이고 본인 채널 작성자 권한도 아닌 유료 미구매 게시글은 Figma `290:9066`처럼 본문 일부와 잠금 이미지 영역을 표시한다.
|
|
- 유료 미구매 게시글은 이번 API에서 `imageUrl == null`로 내려오는 계약이므로, 이미지 영역에는 실제 이미지 대신 회색 RoundedRectangle을 표시한다.
|
|
- 유료 미구매 게시글의 회색 RoundedRectangle 중앙에는 lock icon과 가격 캡슐을 표시한다.
|
|
- 유료 미구매 게시글에서는 오디오 재생 버튼을 표시하지 않는다.
|
|
- `price > 0`이면 유료 게시글로 취급한다.
|
|
- `price == 0`이면 무료 게시글로 취급한다.
|
|
- 좋아요 영역에는 `likeCount`를 표시한다.
|
|
- `isCommentAvailable == true`이면 댓글 icon과 `commentCount`를 표시한다.
|
|
- `isCommentAvailable == false`이면 댓글 icon과 댓글 수를 모두 숨기고 댓글 진입도 제공하지 않는다.
|
|
- 본인 채널에 본인이 쓴 게시글의 리스트형 item에서만 우측 상단에 더보기 버튼을 표시한다.
|
|
- 본인 채널에 본인이 쓴 게시글의 리스트형 item에서만 `price > 0`인 유료 게시글 가격을 우측 상단 영역에 표시한다.
|
|
- 본인 채널에 본인이 쓴 유료 게시글에서 가격 표시와 더보기 버튼이 같은 우측 상단 영역에 함께 표시되는 경우 서로 겹치지 않게 배치한다.
|
|
- 본인 채널이 아니거나 본인이 쓴 게시글이 아닌 경우 리스트형 item 우측 상단 더보기 버튼과 상단 가격 표시는 표시하지 않는다.
|
|
- 우측 더보기 액션은 기존 `CreatorCommunityPostMenuBottomSheetDialog`와 동일한 수정/삭제/고정/고정 해제 액션 정책을 따른다.
|
|
- 리스트형 item 터치 시 아무 동작도 수행하지 않는다. 상세 화면은 다음 범위에서 추가한다.
|
|
|
|
#### Edge Cases
|
|
- `content`가 긴 경우 Figma처럼 본문 영역 안에서 줄바꿈하고 목록 item 간 겹침이 없어야 한다.
|
|
- `content`가 blank string이면 본문 텍스트 영역을 숨길지 빈 영역을 유지할지 구현 계획 단계에서 기존 커뮤니티 item 정책을 확인해 결정한다.
|
|
- `imageUrl` 이미지 로딩 실패 시 기존 이미지 placeholder 정책을 따른다.
|
|
- 유료 미구매 게시글에서 서버가 예외적으로 `imageUrl`을 내려줘도 이번 요구사항은 회색 RoundedRectangle + 잠금/가격 표시를 우선한다.
|
|
- `likeCount`, `commentCount`가 큰 숫자여도 reaction 영역의 icon과 text가 겹치지 않아야 한다.
|
|
- `isCommentAvailable == false`이고 `likeCount`만 표시되는 경우 reaction 영역의 간격이 어색하게 남지 않아야 한다.
|
|
|
|
### Community Post Thumbnail Mode
|
|
썸네일형은 커뮤니티 게시글을 정사각형 그리드 item으로 표시한다.
|
|
|
|
#### Requirements
|
|
- Figma 전체 썸네일형 기준 콘텐츠 영역은 `290:9073`이다.
|
|
- 본인 채널 + 썸네일형은 Figma 전체 썸네일형의 게시글 그리드 표시 방식을 따르되, 하단 고정 CTA는 본인 채널 정책을 유지한다.
|
|
- `communityPosts`를 3열 그리드로 표시한다.
|
|
- 각 grid item은 정사각형 비율을 유지한다.
|
|
- `imageUrl != null`이면 이미지를 grid item 전체에 표시한다.
|
|
- 썸네일형의 GIF 이미지는 재생을 요구하지 않으며 정적 썸네일로 표시해도 된다.
|
|
- `imageUrl == null`이고 유료 미구매 게시글이 아니면 기존 커뮤니티 그리드 페이지처럼 `gray/900 #202020` 배경 위에 `content` 일부를 중앙 정렬로 표시한다.
|
|
- text-only grid item의 본문 preview 길이와 줄 수는 기존 `CreatorCommunityAllGridAdapter` 정책을 우선 참고한다.
|
|
- `isPinned == true`이면 Figma처럼 pin/notice icon을 grid item 상단에 표시한다.
|
|
- `price > 0 && existOrdered == false`이고 본인 채널 작성자 권한도 아닌 유료 미구매 게시글은 grid item에 잠금/가격 표시를 노출한다.
|
|
- 본인 또는 구매한 사용자는 grid item에 실제 이미지 또는 text preview를 표시하고 잠금 overlay를 표시하지 않는다.
|
|
- grid item 터치 시 아무 동작도 수행하지 않는다. 상세 화면은 다음 범위에서 추가한다.
|
|
|
|
#### Edge Cases
|
|
- `content`가 긴 text-only 게시글은 grid item 내부에서 정해진 줄 수까지만 표시하고 이후 말줄임 처리한다.
|
|
- `imageUrl` 이미지 로딩 실패 시 text-only fallback이 아니라 기존 이미지 placeholder 정책을 따른다.
|
|
- 유료 글 잠금 overlay와 text가 겹치지 않아야 한다.
|
|
- 3열 그리드에서 기기 폭이 달라져도 `CreatorCommunityAllGridAdapter`처럼 item width와 height를 같은 값으로 지정해 이미지가 세로로 길어지지 않아야 한다.
|
|
- 썸네일형 item 사이 간격은 Figma 전체 썸네일형 `290:9078` 기준처럼 별도 gap 없이 붙어 보여야 한다.
|
|
- 썸네일형 전환 후 현재 스크롤 위치가 이미 하단 조건을 만족하면 다음 페이지 로딩 여부를 다시 평가해야 한다.
|
|
- 본인 채널에서 CTA가 표시되는 경우 마지막 grid row가 CTA에 가려지지 않도록 하단 padding 또는 inset을 추가한다.
|
|
|
|
### V2 Widget Reuse
|
|
커뮤니티 탭 구현 시 v2 패키지 하위 기존 위젯을 우선 검토하고, 부족한 상태만 최소 확장한다.
|
|
|
|
#### Requirements
|
|
- 리스트형 기본 피드 카드는 `kr.co.vividnext.sodalive.v2.widget.feed.FeedCommunityView`와 `view_feed_community.xml` 재사용을 우선 검토한다.
|
|
- `FeedItem.Community`에는 이미 `creatorImageUrl`, `imageUrl`, `audioUrl`, `price`, `existOrdered`가 있으므로 새 응답 모델을 매핑할 수 있다.
|
|
- `FeedCommunityView`는 현재 `price > 0 && !existOrdered` 조건으로 잠금 overlay와 가격을 표시하므로 유료 미구매 기본 표현에 재사용 가능하다.
|
|
- 기존 `FeedCommunityView`만으로 부족한 댓글 불가 숨김, 본인 채널 우측 상단 더보기/가격, 이미지 중앙 재생 버튼은 구현 계획 단계에서 위젯 확장 또는 커뮤니티 탭 전용 item wrapper 중 더 작은 변경을 선택한다.
|
|
- 이미지 중앙 재생 버튼의 실제 재생 제어는 기존 `CreatorCommunityMediaPlayerManager`를 재사용한다.
|
|
- 썸네일형은 기존 `CreatorCommunityAllGridAdapter`의 image/text/locked 분기 정책을 참고하되, v2 하위에 별도 grid item view가 없으면 `kr.co.vividnext.sodalive.v2.creator.channel` 하위 전용 view holder로 최소 구현한다.
|
|
- 이미지 blur 처리는 기존 v2 홈 커뮤니티 섹션의 `BlurTransformation` 사용 패턴을 참고하되, 이번 리스트형 유료 미구매 상태는 서버가 `imageUrl == null`로 내려주는 계약이므로 회색 RoundedRectangle 표시를 우선한다.
|
|
|
|
#### Edge Cases
|
|
- 공통 `FeedCommunityView`를 수정할 경우 v2 홈 추천 커뮤니티와 크리에이터 채널 홈 커뮤니티 섹션의 표시가 의도치 않게 바뀌지 않아야 한다.
|
|
- 공통 위젯 변경이 기존 홈 화면에 영향을 준다면 커뮤니티 탭 전용 wrapper 또는 optional bind flag로 범위를 제한한다.
|
|
- 레거시 `explorer.profile.creator_community` 코드를 v2 패키지로 무리하게 이동하지 않고, 필요한 정책만 참고한다.
|
|
|
|
### Pagination
|
|
커뮤니티 게시글 목록은 스크롤 하단 접근 시 다음 페이지를 로딩한다.
|
|
|
|
#### Requirements
|
|
- `CreatorChannelCommunityTabResponse.hasNext == true`일 때만 다음 페이지를 요청한다.
|
|
- 다음 페이지는 마지막 성공 응답의 `page + 1`로 요청한다.
|
|
- 다음 페이지 요청에는 `size=20`을 유지한다.
|
|
- 다음 페이지 로딩 중에는 추가 page 요청을 중복으로 보내지 않는다.
|
|
- 다음 페이지 성공 시 기존 `communityPosts` 뒤에 append한다.
|
|
- 보기 방식 변경 시 pagination 상태와 로드된 데이터는 초기화하지 않는다.
|
|
|
|
#### Edge Cases
|
|
- 빠른 스크롤로 load-more trigger가 반복 발생해도 page가 중복 append되지 않아야 한다.
|
|
- 리스트형과 썸네일형을 오가도 이미 로드된 page 데이터가 중복 추가되지 않아야 한다.
|
|
- 마지막 페이지 응답 이후 `hasNext == false`이면 이후 load-more trigger를 무시한다.
|
|
|
|
### Empty State
|
|
커뮤니티 게시글이 없으면 게시글이 있을 때 표시하는 UI를 숨기고 empty 문구만 표시한다.
|
|
|
|
#### Requirements
|
|
- `communityPostCount == 0` 또는 표시 가능한 `communityPosts`가 없는 전체 empty 상태이면 empty 상태를 표시한다.
|
|
- empty 상태에서는 Sort-bar의 보기 방식 토글과 게시글 목록/그리드를 표시하지 않는다.
|
|
- empty 문구는 `크리에이터가 커뮤니티 글을 준비 중입니다.\n기대해 주세요!`이다.
|
|
- empty 문구는 한국어/영어/일본어 다국어 문자열 리소스로 관리한다.
|
|
- empty 상태 표시 방식은 라이브/오디오/시리즈 탭 empty 상태와 동일하게 적용한다.
|
|
- 내 채널 empty 상태에서도 empty 문구는 동일하게 표시한다.
|
|
- 내 채널 empty 상태에서 하단 CTA는 내 채널 CTA 정책을 따른다.
|
|
|
|
#### Edge Cases
|
|
- API 최초 조회 실패 상태는 empty 상태로 취급하지 않고 기존 error/retry 패턴을 따른다.
|
|
- 첫 페이지에서 표시 가능한 게시글이 모두 필터링되거나 숨김 처리되는 경우 empty 상태 정책을 적용한다.
|
|
|
|
### Owner Community Write CTA
|
|
로그인 사용자가 해당 크리에이터 본인인 경우 커뮤니티 탭 하단에 고정된 글쓰기 버튼을 표시한다.
|
|
|
|
#### Requirements
|
|
- Figma 본인 채널 리스트형 기준 CTA는 `665:19021` 내 `CTA`이다.
|
|
- 본인 채널 + 썸네일형에서도 동일한 하단 고정 CTA를 표시한다.
|
|
- 로그인 사용자가 현재 크리에이터 채널의 본인이면 하단 고정 CTA 영역을 표시한다.
|
|
- 로그인 사용자가 현재 크리에이터 채널의 본인이 아니면 하단 고정 CTA 영역을 표시하지 않는다.
|
|
- CTA 영역은 화면 하단에 고정하고, 목록 스크롤과 함께 움직이지 않는다.
|
|
- CTA 버튼 label은 `커뮤니티 글 올리기`이며 다국어 문자열 리소스로 관리한다.
|
|
- CTA 버튼 icon은 Figma 기준 글쓰기 icon이며, 구현 시 기존 drawable 또는 신규 drawable 명칭을 확인해 사용한다.
|
|
- CTA가 표시되는 경우 목록 마지막 item 또는 empty 문구가 CTA에 가려지지 않도록 하단 padding 또는 inset을 추가한다.
|
|
- Android gesture navigation, soft navigation bar, display cutout 환경에서 CTA가 system navigation 영역과 겹치지 않도록 bottom inset을 반영한다.
|
|
- 버튼 터치 시 커뮤니티 글 작성 화면으로 진입한다.
|
|
|
|
#### Edge Cases
|
|
- CTA가 표시된 상태에서 리스트형/썸네일형을 토글해도 CTA는 계속 고정 표시되어야 한다.
|
|
- 키보드가 열리는 작성 화면 진입 이후 복귀해도 CTA와 목록 inset이 깨지지 않아야 한다.
|
|
|
|
---
|
|
|
|
## 8. UX / UI Expectations
|
|
- 전체 화면은 기존 크리에이터 채널 컨테이너의 black background, sticky tab-bar, title-bar 동작을 유지한다.
|
|
- `커뮤니티` main tab은 선택 상태로 표시하고, 선택 underline과 텍스트 색상은 기존 tab-bar 정책을 따른다.
|
|
- Sort-bar 높이와 배치는 Figma `290:9061`, `290:9073`, `665:19021`을 기준으로 하되 기존 탭 구현과 가능한 한 동일한 공통 UI를 사용한다.
|
|
- 리스트형 item 간 간격은 Figma 기준 8dp 수준을 따른다.
|
|
- 썸네일형은 3열 정사각형 그리드로 표시하고 item 간 별도 gap 없이 배치한다.
|
|
- 썸네일형 공지 게시글은 Figma `290:9079`처럼 item 우측 상단에 pin icon만 표시해 일반 게시글과 구분한다.
|
|
- 유료 게시글은 가격 또는 잠금 표현으로 무료 게시글과 구분한다.
|
|
- 리스트형 게시물 이미지는 14dp corner radius가 실제 bitmap에도 적용되어야 하며, GIF 이미지는 재생되어야 한다.
|
|
- 썸네일형 Grid item과 게시물 이미지는 rounded corner 없이 정사각 모서리로 표시하며, GIF 이미지는 정적 썸네일로 표시되어도 된다.
|
|
- 리스트형 공지 표시는 Figma처럼 item 최상단에 pin icon과 `Notice` label을 한 줄로 표시한다.
|
|
- 리스트형 본인 채널 유료 가격 표시는 Figma처럼 우측 상단 capsule(`cash` icon + 12sp 가격)로 표시하고, 유료 미구매 중앙 가격 표시는 lock 아래 흰색 capsule로 표시한다.
|
|
- 모든 사용자 표시 문구는 문자열 리소스로 관리한다.
|
|
- Figma localhost asset URL은 앱 코드에 직접 사용하지 않는다.
|
|
|
|
---
|
|
|
|
## 9. Technical Constraints
|
|
- Android Gradle 단일 모듈 `:app` 안에서 구현한다.
|
|
- 신규 `Fragment`, `ViewModel` 및 그와 연결된 하위 코드는 `kr.co.vividnext.sodalive.v2` 패키지 하위에 작성한다.
|
|
- 기존 `CreatorChannelActivity`와 `ViewPager2` 기반 탭 구조를 유지한다.
|
|
- 기존 크리에이터 채널 API/Repository 패턴을 따른다.
|
|
- 서버 DTO 필드명과 타입은 PRD의 Response Contract를 따른다.
|
|
- `CreatorChannelCommunityPostResponse`에는 `creatorProfileUrl: String`, `existOrdered: Boolean`을 포함한다.
|
|
- API 기본값은 `page=0`, `size=20`이다.
|
|
- 보기 방식은 클라이언트 UI 상태이며 API query parameter로 보내지 않는다.
|
|
- 리스트형은 v2 `FeedCommunityView` 재사용 가능성을 우선 검토하되, 기존 홈/추천 피드에 영향이 생기는 변경은 커뮤니티 탭 전용 확장으로 제한한다.
|
|
- 썸네일형은 레거시 `CreatorCommunityAllGridAdapter`의 표시 정책을 참고하되 신규 코드는 v2 패키지 하위에 작성한다.
|
|
- 오디오 게시글 재생은 기존 `kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityMediaPlayerManager`를 재사용한다.
|
|
- 네트워크, 이미지 로딩, error/retry, pagination 중복 방지 방식은 기존 라이브/오디오/시리즈 탭 패턴을 우선 따른다.
|
|
- 비밀값, `BuildConfig` 값, 로컬 Figma asset URL을 로그/Toast/크래시 메시지에 노출하지 않는다.
|
|
|
|
---
|
|
|
|
## 10. Metrics
|
|
- 커뮤니티 탭 최초 조회 성공률.
|
|
- 커뮤니티 탭 API 실패율과 retry 성공률.
|
|
- 리스트형/썸네일형 토글 사용 비율.
|
|
- 커뮤니티 게시글 item 클릭률.
|
|
- 본인 채널 `커뮤니티 글 올리기` CTA 클릭률.
|
|
- pagination 추가 로딩 성공률과 중복 요청 발생 여부.
|
|
|
|
---
|
|
|
|
## 11. Open Questions
|
|
- 커뮤니티 게시글 작성 CTA는 기존 `CreatorCommunityWriteActivity` 진입을 재사용할 수 있어 보이나, 구현 계획 단계에서 v2 채널 컨테이너와 activity result 처리 범위를 확인한다.
|