# PRD: 메인 홈 추천 UI와 API 연동 ## 1. Overview Figma `home_001` 화면(`24:5514`)을 기준으로 메인 홈 추천 영역을 구성하고, 홈 추천 API 응답을 기존 v2 widget 중심으로 바인딩한다. --- ## 2. Problem - 메인 홈 추천 화면은 라이브, 배너, 최근 활동, 신규 데뷔, 첫 오디오 콘텐츠, AI 캐릭터, 장르별 크리에이터, 응원 크리에이터, 인기 커뮤니티 등 여러 데이터 섹션을 한 화면에 표시해야 한다. - Figma에는 이미 `live`, `banner`, `section-title`, `contents`, `chat-thumbnail`, `profile`, `feed` 등 재사용 가능한 형태가 다수 포함되어 있으므로, 기존에 생성된 widget을 최대한 활용해야 한다. - `HomeActiveCreatorItem.activityType`은 백엔드 enum code 그대로 표시하면 사용자에게 부자연스러우므로 `LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY`를 다국어 처리 가능한 표시 문자열로 변환해야 한다. - 사업자 정보 섹션은 별도 외부 라이브러리 없이 최대 3줄 말줄임과 더보기/접기 토글을 제공해야 한다. - Figma에 포함된 일부 섹션은 이번 범위에서 제외되어야 하므로 구현 범위를 명확히 분리해야 한다. --- ## 3. Goals - 홈 추천 API 응답 `HomeRecommendationResponse`를 받아 Figma `24:5514`의 홈 추천 섹션 순서에 맞게 표시한다. - 기존 `HomeApi`에 메서드를 추가하지 않고, 메인 홈 추천 전용 신규 API 인터페이스/Repository 흐름을 만든다. - 추천 탭이 선택된 `HomeMainFragment`에 메인 홈 추천 UI를 구현한다. - Figma에서 기존 widget으로 대응 가능한 항목은 기존 v2 widget을 우선 재사용한다. - 재사용이 어려운 영역만 최소 범위 신규 UI 또는 adapter item으로 정의한다. - `추천 필모그래피`, `또 다른 모습` 섹션은 만들지 않는다. - 최하단 사업자 정보 섹션은 표시하되, 기본 최대 3줄 말줄임과 `더보기`/`접기` 토글을 제공한다. - `HomeActiveCreatorItem.activityType`은 백엔드 code를 앱 내부 enum 또는 mapper로 변환하고, 표시 문구는 string resource 기반 다국어 처리를 적용한다. - API DTO, 화면 상태, empty/error/loading 정책, click routing은 구현 계획에서 검증 가능하도록 정리한다. --- ## 4. Non-Goals - 이번 PRD 작성 단계에서는 코드, 리소스, 레이아웃 파일을 구현하지 않는다. - `추천 필모그래피` 섹션은 구현하지 않는다. - `또 다른 모습` 섹션은 구현하지 않는다. - 추천 알고리즘, API 응답 정렬 기준, 서버 필드명은 변경하지 않는다. - 외부 라이브러리를 추가하지 않는다. - Compose 화면으로 전환하지 않는다. - `ViewPager` 또는 swipe 기반 tab 전환은 추가하지 않는다. - 이번 범위에서 `랭킹`, `팔로잉`용 별도 Fragment 또는 숨김 View를 미리 만들지 않는다. - 기존 레거시 홈 화면 전체 리팩터링은 포함하지 않는다. - Figma에 없는 skeleton loading, shimmer, 임의 애니메이션, 추가 badge는 만들지 않는다. --- ## 5. Target Users - 메인 홈에서 추천 라이브, 크리에이터, 오디오 콘텐츠, AI 캐릭터, 커뮤니티를 탐색하는 앱 사용자. - 기존 XML Views와 v2 widget을 재사용해 홈 추천 화면을 구현/유지보수하는 Android 개발자. --- ## 6. User Stories - 사용자는 홈에서 현재 라이브 중인 크리에이터와 추천 콘텐츠를 빠르게 탐색하고 싶다. - 사용자는 배너를 스와이프하거나 터치해 관련 이벤트, 크리에이터, 시리즈, 외부 링크 또는 앱 내부 딥링크로 이동하고 싶다. - 사용자는 최근 활동한 크리에이터의 활동 유형을 `라이브`, `오디오`, `커뮤니티`처럼 이해 가능한 문구로 보고 싶다. - 사용자는 인기 커뮤니티 글을 읽다가 사업자 정보가 길어도 홈 화면 탐색이 과도하게 방해받지 않기를 기대한다. - 개발자는 기존 v2 widget을 최대한 재사용해 화면별 UI 중복과 스타일 차이를 줄이고 싶다. --- ## 7. Core Features ### 홈 추천 API 연동 메인 홈 추천 화면은 다음 응답을 단일 API 결과로 받아 섹션별 UI 모델로 변환한다. #### Response Contract ```kotlin data class HomeRecommendationResponse( val lives: List, val banners: List, val recentlyActiveCreators: List, val recentDebutCreators: List, val firstAudioContents: List, val aiCharacters: List, val genreCreators: List, val cheerCreators: List, val popularCommunityPosts: List ) ``` #### Requirements - API endpoint는 `GET /api/v2/home/recommendations`를 사용한다. - 기존 `HomeApi`에 추가하지 않고 홈 추천 전용 신규 API 인터페이스를 만든다. - Repository/ViewModel 위치는 구현 계획에서 `HomeMainFragment`와 기존 v2 홈 구조를 확인한 뒤 결정한다. - 응답 DTO는 서버 필드명을 임의 변경하지 않고, 필요한 경우 `@SerializedName`만 추가한다. - 화면 UI는 DTO를 직접 노출하지 않고 섹션별 UI model 또는 adapter item으로 변환한다. - 각 리스트가 비어 있으면 해당 섹션은 숨기는 것을 기본 정책으로 한다. - 전체 API 실패 시 기존 홈 화면의 error/toast/loading 패턴을 따른다. - 이미지 URL이 null이면 호출부의 기존 placeholder 정책을 따른다. #### Changed Item Contracts ```kotlin data class HomeFirstAudioContentItem( val contentId: Long, val creatorId: Long, val creatorNickname: String, val creatorProfileImage: String, val title: String, val price: Int, val coverImage: String?, val isPointAvailable: Boolean ) data class HomeAiCharacterItem( val characterId: Long, val name: String, val description: String, val profileImage: String?, val totalChatCount: Long, val originalWorkTitle: String? ) data class HomePopularCommunityPostItem( val postId: Long, val creatorId: Long, val creatorNickname: String, val creatorProfileImage: String?, val imageUrl: String?, val audioUrl: String?, val content: String, val price: Int, val createdAt: String, val likeCount: Long, val commentCount: Long, val existOrdered: Boolean ) ``` #### Changed Item Requirements - `HomeFirstAudioContentItem.price`는 첫 오디오 콘텐츠의 무료 여부 표시 정책에 사용한다. - `HomeFirstAudioContentItem.isPointAvailable`은 첫 오디오 콘텐츠 카드 바인딩 시 포인트 사용 가능 표시 정책에 반영한다. - `HomeAiCharacterItem.profileImage`는 `CharacterChatThumbnailView`의 실제 이미지 URL로 사용한다. - `HomeAiCharacterItem.originalWorkTitle`이 null이면 기존 `CharacterChatThumbnailView`의 원작 없음 표시 정책에 맞춰 원작 영역을 유지하거나 숨기는 방식을 구현 계획에서 확정한다. - `HomePopularCommunityPostItem.postId`는 커뮤니티 게시글 이동/식별 값으로 사용한다. - `HomePopularCommunityPostItem.price`, `existOrdered`는 추천 페이지용 `FeedCommunityView`의 유료 미구매 UI 판정에 사용한다. - `HomePopularCommunityPostItem.content`, `price`, `createdAt`, `likeCount`, `commentCount`는 인기 커뮤니티 feed 표시 데이터로 사용한다. ### Activity Type 다국어 표시 `HomeActiveCreatorItem.activityType`은 백엔드 enum code를 앱 표시용 enum으로 변환한다. #### Backend Codes ```kotlin enum class RecommendedActivityType(val code: String) { LIVE("LIVE"), AUDIO("AUDIO"), COMMUNITY("COMMUNITY"), LIVE_REPLAY("LIVE_REPLAY") } ``` #### Requirements - `LIVE`, `LIVE_REPLAY`는 동일하게 `라이브`로 표시한다. - `AUDIO`는 `오디오`로 표시한다. - `COMMUNITY`는 `커뮤니티`로 표시한다. - 표시 문구는 `strings.xml`, `values-en/strings.xml` 등 string resource를 통해 다국어 처리한다. - 알 수 없는 code는 크래시시키지 않고 기본값 또는 미표시 정책을 구현 계획에서 확정한다. - mapper 또는 enum 변환 로직은 local unit test로 검증한다. ### 사업자 정보 접기/더보기 Figma 최하단 사업자 정보 섹션(`218:2058`)은 외부 라이브러리 없이 TextView와 클릭 가능한 컨트롤로 구현한다. #### Requirements - 기본 상태는 최대 3줄, 끝 말줄임표로 표시한다. - 기본 상태에서 `더보기`를 터치하면 전체 텍스트를 표시한다. - 펼침 상태에서 `접기`를 터치하면 다시 최대 3줄 말줄임 상태로 돌아간다. - `더보기`/`접기` 표시 문구는 string resource로 관리한다. - 외부 라이브러리를 사용하지 않는다. - 사업자 정보 텍스트는 하드코딩 여부를 구현 계획에서 확인하되, 다국어/운영 변경 가능성이 있으면 string resource 또는 서버/설정값 사용을 우선 검토한다. - 텍스트가 3줄 이하인 경우 `더보기`를 표시하지 않는다. ### 상단 Bar와 추천 Tab `HomeMainFragment`에서 추천 탭 선택 상태의 메인 홈 추천 페이지를 표시한다. #### Requirements - title-bar는 기존 `view_title_bar_home`을 사용한다. - title-bar 우측 아이콘은 왼쪽부터 `ic_bar_cash`, `ic_bar_search`, `ic_bar_bell` 순서로 배치한다. - tab-bar는 기존 `TextTabBarView`를 사용한다. - tab 항목은 `추천`, `랭킹`, `팔로잉` 순서로 구성한다. - 이 PRD의 화면은 `추천` tab이 선택되었을 때 표시되는 page다. - tab 표시 문구는 string resource로 관리한다. - tab 전환은 글자 터치로만 처리하고, 좌우 swipe 또는 `ViewPager` 기반 전환은 제공하지 않는다. - `TextTabBarView` 아래에는 이번 범위의 추천 page view만 구성한다. - `랭킹`, `팔로잉` page는 이번 PRD에서 Fragment나 `VISIBLE`/`GONE` 대상 View로 미리 만들지 않는다. - 세로 스크롤 시 title-bar와 `TextTabBarView`는 화면에 유지하고, `TextTabBarView` 아래 추천 content 영역만 스크롤되도록 구성한다. ### 모두 팔로우 하기 `최근 응원이 많은 크리에이터`, `장르별 크리에이터` 섹션에는 모두 팔로우 버튼을 제공한다. #### API Requirements - API는 `POST /api/v2/home/recommendations/creators/follow`를 사용한다. - request body는 `FollowRecommendedCreatorsRequest`를 사용한다. - 기존 API에 추가하지 않고, 홈 추천 전용 신규 API 인터페이스에 포함한다. - API 호출이 정상 완료되고 response가 success이면 버튼 상태를 `모두 팔로우 완료`로 변경한다. - 실패 시 기존 화면의 toast/error 표시 패턴을 따른다. #### Request Contract ```kotlin data class FollowRecommendedCreatorsRequest( val creatorIds: List ) ``` #### Request Requirements - `creatorIds`에는 사용자가 터치한 `모두 팔로우 하기` 버튼이 속한 섹션의 크리에이터 id 목록을 전달한다. - `최근 응원이 많은 크리에이터` 섹션에서는 `cheerCreators.creatorId` 목록을 전달한다. - `장르별 크리에이터` 섹션에서는 해당 장르 그룹의 `creators.creatorId` 목록을 전달한다. - `creatorIds`가 빈 리스트인 경우 API를 호출하지 않고 버튼 상태를 변경하지 않는다. #### Button Requirements - 기본 상태 버튼 문구는 `모두 팔로우 하기`다. - 기본 상태 아이콘은 `ic_new_follow`를 사용한다. - 완료 상태 버튼 문구는 `모두 팔로우 완료`다. - 완료 상태 아이콘은 `ic_new_following`을 사용한다. - 완료 상태 Figma reference: 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-9092&m=dev - 완료 상태 버튼은 Figma `24:9092` 기준 white background, capsule radius `100dp`, horizontal/vertical padding `12dp`, icon `20dp`, icon/text gap `6dp`, Pretendard Medium `16sp`, black text를 기준으로 한다. - 문구는 string resource로 관리한다. - 버튼 상태는 API success 후 중복 호출되지 않도록 disabled 또는 click 무시 상태로 전환한다. ### 추천 페이지 위젯 수정 추천 페이지에 표시되는 기존 widget은 화면 요구사항에 맞춰 최소 범위로 확장한다. 별도 PRD로 분리하지 않고, 메인 홈 추천 UI의 표시 계약으로 이 문서에 함께 기록한다. #### FeedCommunityView Requirements - 추천 페이지의 인기 커뮤니티는 `HomePopularCommunityPostItem`을 `FeedCommunityView` 또는 `FeedAdapter`의 Community variant로 매핑한다. - 기존 keyword 영역은 추천 페이지에서는 제거한다. - `HomePopularCommunityPostItem.content`는 본문 영역에 표시한다. - `HomePopularCommunityPostItem.imageUrl`이 있으면 본문 아래 이미지 영역을 표시한다. - 이미지 영역은 Figma `309:19774`, `309:19775` 기준 `346dp x 236dp`, radius `14dp`, centerCrop을 기준으로 한다. - `imageUrl`이 null이면 이미지 영역은 `GONE` 처리한다. - `audioUrl`은 이번 추천 카드에서 별도 플레이어 UI를 만들지 않고, 게시글 상세 이동 또는 후속 오디오 정책에서 사용한다. - `price > 0 && existOrdered == false`이면 유료 미구매 커뮤니티 포스트로 판단한다. - 유료 미구매 커뮤니티 포스트는 Figma `309:19774` 기준으로 이미지 위에 blur/lock overlay와 가격 capsule을 표시한다. - 가격 capsule에는 `HomePopularCommunityPostItem.price`를 표시한다. - `price > 0 && existOrdered == true`이면 유료 구매 완료 포스트로 판단한다. - `price == 0`이면 무료 포스트로 판단한다. - 유료 구매 완료 포스트 또는 무료 포스트는 Figma `309:19775`의 이미지 표시 상태를 기준으로 하되, Figma에 있는 `구매완료` 버튼/태그는 표시하지 않는다. - 댓글 수와 좋아요 수는 기존 reaction row를 유지한다. - creator profile, creator name, createdAt, body, reaction row typography와 색상은 기존 `FeedCommunityView` 기준을 유지한다. #### FeedCommunityView Figma References - 유료이고 구매하지 않은 UI: 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=309-19774&m=dev - 유료인데 구매함 또는 무료 UI: 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=309-19775&m=dev #### AudioContentCardView Requirements - 첫 오디오 콘텐츠는 `v2.widget.AudioContentCardView`를 사용한다. - `HomeFirstAudioContentItem.isPointAvailable == true`이면 `ic_content_tag_point`를 쓰는 `ImageView`를 표시한다. - `HomeFirstAudioContentItem`에서는 오리지널 여부를 판단하지 않는다. - 오리지널 판단 변수가 없으므로 `ic_content_tag_original`을 쓰는 `ImageView`는 항상 `GONE` 처리한다. - `HomeFirstAudioContentItem.price == 0`이면 무료 콘텐츠로 판단하고 `무료` TextView를 `VISIBLE`로 표시한다. - `HomeFirstAudioContentItem.price > 0`이면 `무료` TextView를 `GONE` 처리한다. - `HomeFirstAudioContentItem`으로 표시하는 아이템은 모두 크리에이터의 첫 콘텐츠이므로 `FIRST` 글자를 포함하는 `LinearLayout`을 항상 표시한다. - 위 조건에 해당하지 않는 태그는 모두 `GONE` 처리한다. - 기존 `AudioContentCardView`의 `AudioContentTag.Original`, `AudioContentTag.Point`, `AudioContentTag.First`, `AudioContentTag.Free` 계약을 우선 재사용한다. - `HomeFirstAudioContentItem` 응답에는 오리지널 여부 필드가 없으므로 오리지널 태그는 표시하지 않는다. ### 섹션 구성과 UI 재사용 도식 Figma `24:5514` 기준 홈 추천 화면은 아래 순서로 배치한다. 제외 섹션은 구현하지 않는다. ```text HomeRecommendation 화면 ├─ 상단 title-bar / tab-bar / nav │ ├─ title-bar: view_title_bar_home │ │ └─ right icons: ic_bar_cash, ic_bar_search, ic_bar_bell │ └─ tab-bar: TextTabBarView(추천 selected, 랭킹, 팔로잉) └─ 추천 content scroll area ├─ 라이브 가로 목록: lives │ ├─ Figma instance: live, more │ ├─ 재사용: v2.widget.livethumbnail.LiveThumbnailSimpleView │ └─ 신규: 더보기 item 또는 기존 more UI 대응 adapter item 필요 ├─ 배너 캐러셀: banners │ ├─ Figma instance: banner │ └─ 재사용: v2.widget.banner.BannerView ├─ 최근 활동한 크리에이터: recentlyActiveCreators │ ├─ Figma instance: section-title, list-act │ ├─ 재사용: view_section_title │ └─ 신규: list-act 형태의 최근 활동 카드 UI 필요 ├─ 최근 데뷔한 크리에이터: recentDebutCreators │ ├─ Figma instance: section-title, creater │ ├─ 재사용: view_section_title │ └─ 신규: 최근 데뷔 크리에이터 카드 UI ├─ 첫 오디오 콘텐츠: firstAudioContents │ ├─ Figma instance: contents, profile │ ├─ 재사용: v2.widget.AudioContentCardView │ └─ 신규: 카드 하단 profile 결합 adapter item 필요 여부 확인 ├─ AI 캐릭터: aiCharacters │ ├─ Figma instance: section-title, chat-thumbnail │ ├─ 재사용: view_section_title │ └─ 재사용: v2.widget.characterchatthumbnail.CharacterChatThumbnailView ├─ 장르별 크리에이터: genreCreators │ ├─ Figma instance: section-title, profile, button-capsule │ ├─ 재사용: view_section_title │ └─ 신규: 장르 그룹 카드, 2행 profile grid, 모두 팔로우 버튼 ├─ 추천 필모그래피 │ └─ 제외: 만들지 않음 ├─ 최근 응원이 많은 크리에이터: cheerCreators │ ├─ Figma instance: section-title, profile, button-capsule │ ├─ 재사용: view_section_title │ └─ 신규: profile grid, 모두 팔로우 버튼 ├─ 또 다른 모습 │ └─ 제외: 만들지 않음 ├─ 인기 커뮤니티: popularCommunityPosts │ ├─ Figma instance: section-title, feed │ └─ 재사용/수정: v2.widget.feed.FeedCommunityView 또는 FeedAdapter Community variant └─ 사업자 정보 └─ 신규: 3줄 말줄임 + 더보기/접기 TextView 영역 ``` #### Section Mapping | API field | Figma node range | 표시 섹션 | 재사용 후보 | 신규 필요 여부 | | --- | --- | --- | --- | --- | | `lives` | `24:5516` | 라이브 가로 목록 | `LiveThumbnailSimpleView` | `more` item 확인 필요 | | `banners` | `24:5525` | 배너 캐러셀 | `BannerView` | 없음 | | `recentlyActiveCreators` | `24:5529` | 방금 활동한 크리에이터 | `view_section_title` | `list-act` 신규 필요 | | `recentDebutCreators` | `24:5534` | 최근 데뷔한 크리에이터 | `view_section_title` | 신규 카드 필요 | | `firstAudioContents` | `24:5539` | 첫 오디오 콘텐츠 | `AudioContentCardView` | 태그 조건 매핑 확인 필요 | | `aiCharacters` | `24:5551` | AI 캐릭터 | `view_section_title`, `CharacterChatThumbnailView` | DTO mapping 필요 | | `genreCreators` | `24:5611`, `24:5636` 후보 | 장르별 크리에이터 | `view_section_title` | 그룹 카드, 모두 팔로우 버튼 신규 필요 | | `cheerCreators` | `24:5636` 후보 | 최근 응원이 많은 크리에이터 | `view_section_title` | profile grid, 모두 팔로우 버튼 신규 필요 | | `popularCommunityPosts` | `24:5645`, `309:19774`, `309:19775` | 인기 커뮤니티 | `view_section_title`, `FeedCommunityView` | 이미지/유료 상태 UI 수정 필요 | | 사업자 정보 | `218:2058` | 사업자 정보 | 없음 | 접기/더보기 신규 필요 | ### 제외 섹션 - Figma `24:5557` 근처의 `추천 필모그래피`로 판단되는 카드형 시리즈/필모그래피 영역은 구현하지 않는다. - Figma `24:5602` 근처의 `또 다른 모습`으로 판단되는 콘텐츠 grid 영역은 구현하지 않는다. - 제외 섹션과 연결되는 서버 필드가 현재 응답에 없으므로 adapter item도 만들지 않는다. #### Edge Cases - 모든 추천 리스트가 비어 있으면 홈 추천 영역은 비어 있는 섹션을 노출하지 않고, 기존 홈 화면 empty 정책을 따른다. - 특정 섹션만 비어 있으면 해당 section-title과 목록 전체를 숨긴다. - 긴 제목, 닉네임, 커뮤니티 본문은 각 widget의 말줄임 정책을 따른다. - `HomeAiCharacterItem.profileImage`가 null이면 호출부 이미지 로딩 정책에 따라 placeholder 또는 빈 상태를 표시한다. - `HomePopularCommunityPostItem.imageUrl` 또는 `audioUrl`이 null이어도 커뮤니티 feed는 본문/반응 수 중심으로 표시 가능해야 한다. - 유료 미구매 포스트의 가격 capsule은 `price` 값을 사용하고, 유료 구매 완료 또는 무료 포스트에는 가격 capsule을 표시하지 않는다. - `HomeBannerItem`은 `type` 없이 `eventItem`, `creatorId`, `seriesId`, `link` 값 기준으로 이동한다. - 배너 이동 우선순위는 `eventItem`, `creatorId`, `seriesId`, `link` 순서로 처리한다. - `link`가 웹 URL이면 `Intent.ACTION_VIEW`를 사용해 외부 웹 URL로 이동한다. - `link`가 내부 딥링크이면 기존 딥링크 실행 정책에 따라 앱 내부 딥링크를 실행한다. - 웹 URL도 내부 딥링크도 아닌 `link`는 이동하지 않는다. --- ## 8. UX / UI Expectations - Figma `24:5514`의 세로 순서, 좌우 여백, 가로 스크롤 형태를 유지한다. - `HomeMainFragment`의 추천 tab page로 표시하며, `TextTabBarView`에서 추천 선택 상태가 명확해야 한다. - `ViewPager`나 swipe로 tab을 넘기는 동작은 없어야 하며, tab 글자 터치로만 전환한다. - 세로 스크롤 중에도 `TextTabBarView`가 화면에 남아 있어야 하므로, 스크롤 컨테이너는 `TextTabBarView` 아래 content 영역에만 적용한다. - 상단 라이브 목록, 배너, 추천 카드들은 어두운 홈 배경 위에서 기존 v2 widget 색상/typography/radius를 유지한다. - 각 섹션 제목은 기존 `view_section_title`을 사용한다. - `최근 응원이 많은 크리에이터`, `장르별 크리에이터`의 모두 팔로우 버튼은 success 전/후 상태가 명확히 구분되어야 한다. - 인기 커뮤니티는 keyword 없이 본문, 선택적 이미지, 유료 잠금 상태, reaction row를 보여야 한다. - 유료 미구매 커뮤니티 이미지는 내용을 바로 읽을 수 없도록 blur/lock overlay가 적용되어야 한다. - 유료 구매 완료 또는 무료 커뮤니티 이미지는 overlay 없이 표시하고, `구매완료` 태그는 표시하지 않는다. - 가로 목록은 화면 밖 다음 item이 일부 보이도록 Figma의 peek 느낌을 유지한다. - 커뮤니티 본문과 사업자 정보는 긴 텍스트가 화면 폭을 밀어내지 않아야 한다. - 사업자 정보는 기본 3줄만 보여 홈 탐색을 방해하지 않고, 사용자가 원할 때 전체 내용을 확인할 수 있어야 한다. - 터치 동작은 UI 컴포넌트 내부에서 목적지를 결정하지 않고 화면/adapter callback으로 위임한다. --- ## 9. Technical Constraints - 현재 프로젝트는 Android XML Views + Kotlin + ViewBinding 기반이므로 XML layout, RecyclerView/adapter, custom view 패턴을 우선한다. - UI는 기존 `kr.co.vividnext.sodalive.v2.main.HomeMainFragment`에 구현한다. - `HomeMainFragment`는 이번 범위에서 추천 content만 직접 구성하며, `ViewPager2`, `FragmentStateAdapter`, tab별 신규 Fragment 3개 구조는 도입하지 않는다. - `TextTabBarView` 아래 추천 content 영역만 `NestedScrollView` 또는 동등한 단일 세로 스크롤 컨테이너로 구성한다. - 신규 API/Repository/ViewModel 및 그와 연결된 하위 코드는 `kr.co.vividnext.sodalive.v2` 패키지 하위에 작성한다. - 기존 `kr.co.vividnext.sodalive.v2.widget.*` 컴포넌트를 우선 재사용한다. - 기존 API 인터페이스에 메서드를 추가하지 않고, 홈 추천 전용 신규 API 인터페이스를 만든다. - 이미지 로딩 라이브러리를 새로 추가하지 않고 기존 호출부의 이미지 로딩 방식을 따른다. - 외부 라이브러리를 추가하지 않는다. - API 모델은 기존 네트워크 스택과 JSON 파서 규칙을 따른다. - string resource 기반 다국어 처리를 적용하며, UI 표시 문자열을 Kotlin 코드에 직접 하드코딩하지 않는다. - Figma에 있는 기존 widget과 다른 신규 UI는 구현 계획에서 파일 경로와 테스트 범위를 명시한 뒤 최소 범위로 작성한다. - 신규 순수 변환 로직(activity type mapper, count/time formatter 등)은 가능하면 local unit test로 검증한다. --- ## 10. Metrics - PRD와 구현 계획에 Figma 섹션 배치, 재사용 widget, 신규 필요 컴포넌트가 한눈에 확인 가능하게 정리된다. - `HomeRecommendationResponse`의 각 필드가 노출/제외/확인필요 중 하나로 분류된다. - `LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY` 표시 문자열이 string resource 기반으로 매핑된다. - `HomeMainFragment`에서 `view_title_bar_home`, `TextTabBarView`, `view_section_title` 재사용이 확인된다. - `FeedCommunityView`에서 keyword 영역이 추천 페이지에 노출되지 않고, 이미지와 유료 미구매 overlay가 표시된다. - `AudioContentCardView`에서 `ic_content_tag_point`, `무료`, `FIRST` 태그가 조건에 맞게 표시되고, `ic_content_tag_original`은 표시되지 않는다. - 제외 대상인 `추천 필모그래피`, `또 다른 모습` 섹션이 화면에 생성되지 않는다. - 모두 팔로우 API success 후 버튼이 `모두 팔로우 완료`와 `ic_new_following` 상태로 변경된다. - 사업자 정보는 기본 3줄 말줄임, 더보기 후 전체 표시, 접기 후 3줄 복귀가 동작한다. - 기존 v2 widget 재사용 범위가 구현 diff에서 확인된다. - 관련 unit test, resource merge, `./gradlew :app:testDebugUnitTest` 또는 구현 범위에 맞는 단일 테스트가 성공한다. --- ## 11. Open Questions - 홈 추천 API endpoint는 `/api/v2/home/recommendations`로 확정됐다. HTTP method는 `GET`으로 가정하되, 구현 전 백엔드 계약 또는 기존 `HomeApi` 패턴에서 최종 확인한다. - 모두 팔로우 API는 `POST /api/v2/home/recommendations/creators/follow`, request body는 `FollowRecommendedCreatorsRequest(creatorIds: List)`로 확정됐다. - 인증 token 처리 방식은 아직 제공되지 않았다. 구현 전 기존 `HomeApi` 확장 지점과 공통 인증 interceptor 사용 여부를 확인해야 한다. - `genreCreators`와 `cheerCreators`의 profile grid는 기존 profile widget이 저장소에 있는지 추가 확인 후 재사용/신규를 결정한다. - 배너 이동 정책은 `type` 없이 `eventItem`, `creatorId`, `seriesId`, `link` 값 기준으로 처리하는 것으로 확정됐다. `link`가 내부 딥링크이면 기존 딥링크 실행 정책을 사용한다. - `activityAt`, `releaseDate`, `createdAt`, `beginDateTime`의 표시 포맷은 기존 시간 formatter 재사용 여부를 구현 계획에서 확인한다. --- ## 12. 검증 기록 - 2026-06-05: 사용자 추가 요청에 따라 배너 `link`가 내부 딥링크이면 기존 딥링크 실행 정책에 따라 앱 내부 딥링크를 실행하도록 PRD의 사용자 스토리, Edge Cases, Open Questions를 갱신했다. 문서 변경만 수행했으므로 Gradle 검증은 실행하지 않고 문서 재읽기로 확인한다. - 2026-06-01: `docs/prd/sample-prd.md`와 `docs/agent-guides/work-plan-docs.md`를 확인해 신규 문서 위치와 PRD 작성 형식을 맞췄다. - 2026-06-01: Figma `24:5514`의 최상위 구조를 확인해 `live`, `banner`, `section-title`, `contents`, `chat-thumbnail`, `profile`, `feed`, 사업자 정보 섹션의 배치와 제외 후보 섹션을 PRD에 반영했다. - 2026-06-01: 저장소의 기존 v2 widget 패키지(`banner`, `livethumbnail`, `characterchatthumbnail`, `feed`, `AudioContentCardView`)와 관련 PRD를 확인해 재사용 후보를 문서화했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 홈 추천 API URL을 `GET /api/v2/home/recommendations`로 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 신규 API 인터페이스 방침, `HomeMainFragment` 구현 위치, `view_title_bar_home`, `TextTabBarView`, `view_section_title` 재사용, 모두 팔로우 API/버튼 상태, 변경된 `HomeFirstAudioContentItem`, `HomeAiCharacterItem` 응답 계약을 PRD에 반영했다. - 2026-06-01: 저장소에서 `HomeMainFragment`, `view_title_bar_home`, `view_section_title`, `TextTabBarView`, `ic_bar_cash`, `ic_bar_search`, `ic_bar_bell`, `ic_new_follow`, `ic_new_following` 존재를 확인했다. - 2026-06-01: Figma `24:9092`를 확인해 모두 팔로우 완료 버튼의 white capsule, `20dp` icon, `6dp` gap, `16sp` medium text 요구사항을 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 모두 팔로우 request body `FollowRecommendedCreatorsRequest(creatorIds: List?)`와 섹션별 `creatorIds` 전달 정책을 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 모두 팔로우 request body의 `creatorIds`를 nullable이 아닌 `List`으로 변경하고 null 의미 확인 항목을 제거했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 모두 팔로우 API HTTP method를 `POST`로 확정해 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `HomeRecommendationResponse.popularCommunities`를 `popularCommunityPosts`로 변경하고, `HomePopularCommunityPostItem` 응답 계약을 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 추천 페이지의 `FeedCommunityView` 수정 요구사항(keyword 제거, 이미지 추가, 유료 미구매 overlay, 구매완료 태그 제외)과 `AudioContentCardView` 태그 표시 정책을 PRD에 반영했다. - 2026-06-01: Figma `309:19774`, `309:19775`의 design context와 screenshot을 확인해 유료 미구매/구매 또는 무료 커뮤니티 feed의 이미지, blur/lock overlay, reaction row 요구사항을 문서화했다. - 2026-06-01: 저장소에서 `AudioContentCardView`가 `AudioContentTag.Original`, `Point`, `First`, `Free`와 `ic_content_tag_original`, `ic_content_tag_point`를 이미 지원하는 것을 확인했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `HomeFirstAudioContentItem`으로 표시하는 모든 아이템은 첫 콘텐츠로 간주해 `FIRST` 태그를 항상 표시하도록 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `HomePopularCommunityPostItem.content` 아래에 `price: Int`를 추가하고, 유료 미구매 가격 capsule 표시값으로 사용하도록 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `HomeFirstAudioContentItem.title` 아래에 `price: Int`를 추가하고, `price == 0`이면 무료 태그를 표시하도록 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `HomeFirstAudioContentItem`에서는 오리지널 여부를 판단하지 않고, 오리지널 태그를 항상 숨기는 정책으로 PRD에 반영했다. - 2026-06-01: 사용자 추가 제공 정보에 따라 `popularCommunityPosts.price`와 `existOrdered` 조합으로 유료 미구매/유료 구매 완료/무료 포스트 UI를 판정하도록 PRD에 반영했다. - 2026-06-02: 사용자 추가 제공 정보에 따라 `ViewPager`/swipe tab 전환을 제외하고, tab 글자 터치 전환만 허용하며, `TextTabBarView` 아래 추천 content 영역만 세로 스크롤되도록 요구사항을 반영했다. - 2026-06-05: Phase 7 구현에서 Figma `24:5645` 기준 인기 커뮤니티 섹션을 `FeedCommunityView` 확장과 `HomePopularCommunityAdapter`로 연결했다. 추천용 커뮤니티는 keyword를 숨기고, 이미지 null 시 이미지 영역을 숨기며, 유료 미구매 상태에서는 overlay와 가격 capsule을 표시한다. 사용자 추가 요청에 따라 인기 커뮤니티 게시글은 최대 10개만 노출하도록 반영했고, 관련 targeted test/full unit test/resource merge/compile/ktlint가 성공했음을 확인했다. - 2026-06-05: Phase 7 리뷰에서 확인된 `audioUrl` 상세 이동 데이터 보존 누락과 유료 미구매 원본 이미지 로드 위험을 수정했다. `FeedItem.Community.audioUrl`과 mapper 전달을 추가하고, 유료 미구매 item은 원본 `imageUrl`을 로드하지 않도록 adapter를 보완했으며 관련 회귀 테스트와 Gradle 검증이 성공했다. - 2026-06-05: 유료 미구매 item 재바인딩 시 이전 Coil 요청이 뒤늦게 완료될 가능성을 방지하기 위해 locked image 경로에서 기존 Coil 요청을 `dispose()`로 취소한 뒤 drawable을 비우도록 추가 보완했다. - 2026-06-05: 인기 커뮤니티 Phase 7 UI를 실제 기기에서 확인할 수 있도록 `HomeMainFragment`의 임시 샘플 content에 무료/유료 미구매/구매 완료 커뮤니티 3건을 추가했다. 관련 컴파일, 홈 레이아웃 테스트, ktlint, resource merge는 성공했으나 연결 기기가 없어 `installDebug` 기반 실기기 확인은 수행하지 못했다. - 2026-06-05: 커뮤니티 게시물 이미지가 고정 크기 때문에 잘려 보이는 문제를 수정했다. `view_feed_community.xml`에서 root와 이미지 container의 고정 폭/높이를 제거하고, `FeedCommunityView`에서 실제 card content width 기준 `346:236` 비율로 이미지 영역 높이를 계산하도록 변경했다. 관련 Feed/Home 테스트, compile, resource merge, ktlint가 성공했다.