docs(content): 추천 탭 계획 문서를 추가한다

This commit is contained in:
Yu Sung
2026-07-06 01:13:25 +09:00
parent 9a141a09de
commit f3cbf42328
2 changed files with 627 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
# PRD: 메인 콘텐츠 탭 내부 추천 탭
## 1. Overview
메인 하단 `콘텐츠` 탭 안의 내부 `추천` 탭에서 `GET /api/v2/audio/recommendations` 응답을 사용해 오디오 추천 화면을 제공한다.
화면 상단 title bar와 내부 tab bar는 고정 영역으로 유지하고, Figma 기준 `배너`부터 하위 콘텐츠만 세로 스크롤한다. 신규 기능이므로 구현 대상 API, 모델, ViewModel, 화면, 섹션 컴포넌트는 `SodaLive/Sources/V2/Main/Content/Recommendation/**` 하위에 둔다.
Figma 참조:
- 전체 화면: `24:6737`, `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-6737&m=dev`
- 배너: `24:6741`
- 새로 올라온 오디오: `24:6751`
- New&Hot: `24:6758`
- 오직 보이스온에서만: `24:6745`
- 추천 시리즈: `24:6770` (이번 범위 제외)
- 무료 오디오: `24:6807`
- 포인트 오디오: `24:6813`
- 최근 댓글이 많은 오디오: `24:6820`
- 키워드의 오디오: `24:6829` (이번 범위 제외)
- 추천 오디오: `24:6842`
## 2. Problem
- 현재 V2 `MainView``.content` 탭은 placeholder 상태라 메인 콘텐츠 탭에서 실제 추천 오디오 콘텐츠를 제공하지 못한다.
- 기존 홈 추천 탭은 크리에이터/커뮤니티/라이브 중심이고, 콘텐츠 탭 내부 추천 화면은 오디오 추천 API와 섹션 구성이 다르다.
- Figma 화면은 카드형, 리스트형, 댓글 카드형, grid형 등 섹션마다 레이아웃이 달라 단일 컴포넌트로 단순 렌더링하면 디자인과 맞지 않는다.
- 오디오 카드에는 `ONLY`, original audio, first, free, point, adult 등 여러 태그가 겹쳐 표시되므로 기존 V2 컴포넌트 재사용 범위를 먼저 정해야 한다.
## 3. Goals
- 메인 하단 `콘텐츠` 탭에서 내부 `추천` 탭 화면을 표시한다.
- `GET /api/v2/audio/recommendations`를 호출하고 응답 데이터로 섹션을 구성한다.
- title bar와 내부 tab bar는 고정하고, 배너부터 하위 콘텐츠만 스크롤한다.
- Figma에 보이는 구현 대상 섹션을 섹션별로 분리한다.
- `banners`, `originalSeries`, `latestAudios`, `newAndHotAudios`, `freeAudios`, `pointAudios`, `mostCommentedAudios`, `recommendedAudios`를 응답 모델에 반영한다.
- `오직 보이스온에서만!` 섹션에는 `originalSeries`를 사용하고, `ic_series_original` + `img_new_only` 조합으로 `ONLY` 표시를 노출한다.
- `새로 올라온 오디오`, `무료 오디오`, `포인트 오디오`, `추천 오디오`는 기존 `AudioContentCard` 재사용 또는 최소 보완을 우선 검토한다.
- `New&Hot`, `최근 댓글이 많은 오디오`는 기존 `CreatorChannelAudioContentListItem`의 row/태그 표시 규칙을 참고한다.
- 오디오 item 탭 시 기존 content detail 진입 guard 흐름을 재사용한다.
## 4. Non-Goals
- `추천 시리즈` 섹션은 이번 범위에서 구현하지 않는다.
- `#키워드의 오디오` 섹션은 이번 범위에서 구현하지 않는다.
- API 스펙에 없는 추천 시리즈 상세 카드, 키워드 추천 API, 키워드별 더보기 화면을 임의로 추가하지 않는다.
- 기존 홈 `추천`/`랭킹`/`팔로잉` 탭 구조를 변경하지 않는다.
- 기존 legacy `SodaLive/Sources/Content/**` 화면을 리팩터링하지 않는다.
- Figma localhost asset URL을 앱 코드에 직접 사용하지 않는다.
- 외부 라이브러리를 추가하지 않는다.
- `Pods/**`, `generated/**`, `build/**`는 수정하지 않는다.
## 5. Target Users
- 메인 콘텐츠 탭에서 새 오디오와 추천 오디오를 빠르게 탐색하려는 사용자
- 무료/포인트/신규/오리지널 여부를 보고 오디오 콘텐츠를 선택하려는 사용자
- 댓글 반응이 많은 오디오를 확인하고 상세로 이동하려는 사용자
- 보이스온 오리지널 시리즈를 별도 섹션에서 발견하려는 사용자
## 6. User Stories
- 사용자는 콘텐츠 탭의 추천 화면에서 최신 오디오를 가로 카드 목록으로 보고 싶다.
- 사용자는 `New&Hot` 오디오를 리스트 형태로 빠르게 훑어보고 싶다.
- 사용자는 보이스온에서만 제공되는 오리지널 시리즈를 `ONLY` 표시로 구분하고 싶다.
- 사용자는 무료 오디오와 포인트 사용 가능 오디오를 별도 섹션으로 탐색하고 싶다.
- 사용자는 최근 댓글이 많은 오디오와 최신 댓글 작성자 프로필을 함께 보고 싶다.
- 사용자는 추천 오디오를 2열 grid로 탐색하고 마음에 드는 콘텐츠 상세로 이동하고 싶다.
## 7. Core Requirements
### 7.1 API
- Method: `GET`
- Path: `/api/v2/audio/recommendations`
- 인증: 기존 V2 API 인증 헤더 패턴을 따른다.
- 응답 래퍼: 기존 관례대로 `ApiResponse<AudioRecommendationsResponse>` 디코딩을 우선한다.
### 7.2 Response model
```kotlin
data class AudioRecommendationsResponse(
val banners: List<AudioBannerResponse>,
val originalSeries: List<OriginalSeriesResponse>,
val latestAudios: List<AudioCardResponse>,
val newAndHotAudios: List<AudioCardResponse>,
val freeAudios: List<AudioCardResponse>,
val pointAudios: List<AudioCardResponse>,
val mostCommentedAudios: List<CommentedAudioResponse>,
val recommendedAudios: List<AudioCardResponse>
)
```
Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다. `audioContentId`, `seriesId`, `creatorId` 같은 Kotlin `Long` 대응 값은 프로젝트 기존 모델 관례에 맞춰 Swift `Int`를 우선 사용한다.
### 7.3 Fixed shell and scrolling
- 콘텐츠 탭 title bar는 화면 상단 고정 영역이다.
- 내부 tab bar도 title bar 아래 고정 영역이다.
- Figma `24:6741` 배너부터 하위 콘텐츠만 세로 `ScrollView` 안에 둔다.
- 하단 main tab bar와 mini player 영역은 기존 `MainView` 구조를 유지한다.
### 7.4 Section visibility
- 섹션별 배열이 비어 있으면 해당 섹션은 렌더링하지 않는다.
- 모든 구현 대상 섹션이 비어 있으면 empty state를 표시한다.
- API 실패 또는 디코딩 실패 시 사용자 노출 문구를 `I18n`에 둔다.
### 7.5 구현 대상 섹션
- 배너: `banners`
- 새로 올라온 오디오: `latestAudios`
- New&Hot: `newAndHotAudios`
- 오직 보이스온에서만: `originalSeries`
- 무료 오디오: `freeAudios`
- 포인트 오디오: `pointAudios`
- 최근 댓글이 많은 오디오: `mostCommentedAudios`
- 추천 오디오: `recommendedAudios`
### 7.6 제외 대상 섹션
- 추천 시리즈: Figma `24:6770`
- 키워드의 오디오: Figma `24:6829`
`originalSeries`는 이번 범위에서 `오직 보이스온에서만!` 섹션에만 사용한다. 추천 시리즈 카드형 상세 UI는 만들지 않는다.
### 7.7 Tag display
- `isOriginalSeries == true`이면 original audio/series 태그를 표시한다.
- `isFirstContent == true`이면 first 태그를 표시한다.
- `isPointAvailable == true`이면 point 태그를 표시한다.
- `price == 0`이면 free 태그를 표시한다.
- `isAdult == true`이면 adult shield 태그를 표시한다.
- `오직 보이스온에서만!``ONLY` 표시는 `ic_series_original` + `img_new_only` asset 조합을 사용한다.
### 7.8 Navigation
- 오디오 item 탭은 `audioContentId`로 기존 content detail 진입 흐름을 사용한다.
- 배너 탭은 `eventItem`, `creatorId`, `seriesId`, `link`를 명시적으로 분기한다.
- `eventItem`은 기존 event detail 흐름이 있으면 재사용한다.
- `creatorId`는 기존 creator detail guard callback을 재사용한다.
- `seriesId``AppStep.seriesDetail(seriesId:)` route로 연결한다.
- `AudioBannerResponse.link`는 먼저 `AppDeepLinkHandler.handle(urlString:)`로 deep link 여부를 확인한다.
- `AudioBannerResponse.link`가 deep link로 처리되지 않으면 `UIApplication.shared.open`으로 외부 브라우저를 연다.
## 8. UX / UI Expectations
- 전체 배경은 기존 V2 dark UI 기준을 따른다.
- 섹션 title은 Figma 텍스트를 우선 사용하되 신규 사용자 노출 문자열은 `I18n`에 둔다.
- 배너는 Figma `24:6741` 기준으로 가로 carousel과 `01 / 20` page indicator를 표시한다.
- 일반 오디오 카드 섹션은 가로 스크롤 카드 목록으로 표시한다.
- `New&Hot`은 3개 row를 한 묶음으로 구성하고 묶음 단위로 가로 스크롤한다.
- `최근 댓글이 많은 오디오`는 오디오 row와 댓글 preview bubble을 하나의 카드로 묶어 가로 스크롤한다.
- `최근 댓글이 많은 오디오`의 댓글 preview bubble에는 nickname을 표시하지 않는다. Figma `24:6820` 기준으로 `latestCommentWriterProfileImageUrl`과 댓글 텍스트 영역만 사용한다.
- `추천 오디오`는 2열 grid로 표시한다.
- 텍스트가 길면 한 줄 또는 Figma 기준 line limit로 말줄임 처리한다.
- 카드/row의 루트 레이아웃은 가능한 부모 width, grid column, aspect ratio 기반으로 구성한다.
## 9. Technical Constraints
- 신규 기능 파일은 `SodaLive/Sources/V2/Main/Content/Recommendation/**` 하위에 둔다.
- 콘텐츠 탭 root는 `SodaLive/Sources/V2/Main/Content/**` 하위에 둔다.
- 여러 화면에서 재사용할 명확한 근거가 생긴 컴포넌트만 `SodaLive/Sources/V2/Component/**`로 승격한다.
- 기존 `MainHomeRecommendation` API/ViewModel을 직접 확장하지 않는다.
- 기존 `AudioContentCard`, `BannerCarousel`, `SectionTitle`, `TextTabBar`, `HomeTitleBar` 재사용을 먼저 검토한다.
- 신규 사용자 노출 문구는 `SodaLive/Sources/I18n/I18n.swift`에 ko/en/ja로 추가한다.
- Figma localhost asset URL은 앱 코드에 사용하지 않는다.
- 외부 라이브러리는 추가하지 않는다.
## 10. Success Criteria
- `.content` 탭에서 placeholder 대신 콘텐츠 탭 root가 표시된다.
- 콘텐츠 탭 내부 `추천` 탭 진입 시 `GET /api/v2/audio/recommendations` 호출이 발생한다.
- title bar와 내부 tab bar는 고정되고, 배너부터 하위 섹션만 세로 스크롤된다.
- API 응답의 구현 대상 배열이 각 Figma 섹션에 매핑된다.
- `추천 시리즈`, `#키워드의 오디오` 섹션은 구현되지 않는다.
- `오직 보이스온에서만!` 섹션은 `ic_series_original` + `img_new_only` 조합으로 `ONLY` 표시를 노출한다.
- 오디오 item 탭 시 기존 content detail guard 흐름을 통해 상세로 이동한다.
- `AudioBannerResponse.seriesId``OriginalSeriesResponse.seriesId` 탭 시 `seriesDetail`로 이동한다.
- `AudioBannerResponse.link`는 deep link 우선 처리 후, deep link가 아니면 외부 브라우저로 열린다.
- 빈 섹션은 숨겨지고, 전체 empty/API 실패 상태는 사용자에게 안내된다.
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`가 성공한다.
## 11. Open Questions
- 현재 확정된 요구사항 기준으로 미결정 사항은 없다.
## 12. Verification Notes
- 2026-07-05: Figma `get_design_context``get_screenshot`으로 `24:6737`, `24:6741`, `24:6751`, `24:6758`, `24:6745`, `24:6770`, `24:6807`, `24:6813`, `24:6820`, `24:6829`, `24:6842`를 확인했다.
- 2026-07-05: `MainView`, `MainHomeView`, `AudioContentCard`, `BannerCarousel`, `CreatorChannelAudioContentListItem`, `CreatorChannelSeriesListItem`을 확인해 재사용 후보를 정리했다.
- 2026-07-05: 최초 문서 작성 시 `plan-task.md`만 작성되어 신규 기능 문서 정책과 맞지 않았다. 본 PRD를 추가해 정책 누락을 보완한다.
- 2026-07-05: 사용자 확인으로 댓글 preview bubble에는 nickname을 표시하지 않고, 배너 `link`는 deep link 우선 후 외부 브라우저로 열며, `seriesId``seriesDetail` route로 이동하도록 확정했다.