Files
sodalive-ios/docs/20260602_메인_홈_추천_UI_API_연동/prd.md

18 KiB

PRD: 메인 홈 추천 UI와 API 연동

1. Overview

메인 홈의 추천 탭을 Figma 디자인 기준으로 구성하고, 신규 홈 추천 API(/api/v2/home/recommendations)와 모두 팔로우 API(/api/v2/home/recommendations/creators/follow)를 연동한다.

2. Problem

  • 기존 홈 API에 추천 탭 전용 응답을 추가하지 않고 신규 V2 API로 분리해야 한다.
  • Figma 기준 추천 탭에는 라이브, 배너, 최근 활동 크리에이터, 데뷔/첫 오디오/AI 캐릭터/장르/응원/커뮤니티/사업자 정보 등 여러 반복 UI가 포함되어 있다.
  • 반복 UI가 많아 단일 화면에 직접 구현하면 유지보수 비용이 커진다.
  • 사업자 정보 텍스트는 외부 라이브러리 없이 3줄 말줄임표와 더보기/접기 토글을 제공해야 한다.

3. Goals

  • 추천 탭 진입 시 /api/v2/home/recommendations를 호출하고 응답 데이터로 화면을 구성한다.
  • 기존 HomeApi에 케이스를 추가하지 않고 신규 API/Repository/ViewModel 계층을 만든다.
  • Figma에서 확인한 기존 widget과 저장소 내 V2 공용 컴포넌트를 가능한 범위에서 재사용한다.
  • 반복되는 UI는 Custom Widget으로 분리해 재사용 가능하게 한다.
  • 모두 팔로우 API 호출이 정상 완료되고 success == true이면 버튼 문구를 모두 팔로우 완료로 변경한다.
  • RecommendedActivityType 서버 enum 값은 앱 enum으로 변환하고 다국어 문구로 표시한다.
  • 빈 섹션 데이터는 제목과 빈 컨테이너를 노출하지 않아 화면 밀도를 유지한다.

4. Non-Goals

  • 추천 필모그래피 섹션은 만들지 않는다.
  • 또 다른 모습 섹션은 만들지 않는다.
  • 기존 홈 API(/api/home) 또는 기존 HomeApi에 추천 API를 추가하지 않는다.
  • 외부 라이브러리를 추가하지 않는다.
  • 서버 응답 스펙에 없는 팔로우 개별 상태, 페이지네이션, 정렬/필터 기능은 이번 범위에 포함하지 않는다.
  • Figma 로컬 asset URL을 앱 코드에 직접 사용하지 않는다.

5. Target Users

  • 앱 홈에서 추천 크리에이터, 라이브, 콘텐츠, 커뮤니티를 빠르게 탐색하는 일반 사용자
  • 추천된 크리에이터 그룹을 한 번에 팔로우하려는 사용자

6. User Stories

  • 사용자는 홈 추천 탭에서 현재 라이브 중인 크리에이터와 추천 콘텐츠를 한 화면에서 보고 싶다.
  • 사용자는 관심 있는 장르/응원 크리에이터 그룹을 한 번에 팔로우하고 싶다.
  • 사용자는 사업자 정보를 기본적으로 짧게 보고, 필요할 때 전체 내용을 펼쳐 보고 싶다.
  • 사용자는 최근 활동 타입을 한국어/영어/일본어 등 현재 앱 언어에 맞게 보고 싶다.

7. Core Features

7.1 추천 홈 데이터 조회

API

  • Method: GET
  • Path: /api/v2/home/recommendations
  • 인증: 기존 인증 헤더 패턴과 동일하게 Authorization: Bearer {token} 사용
  • 응답 래퍼: 기존 관례대로 ApiResponse<HomeRecommendationResponse> 디코딩

Response

data class HomeRecommendationResponse(
    val lives: List<HomeLiveItem>,
    val banners: List<HomeBannerItem>,
    val recentlyActiveCreators: List<HomeActiveCreatorItem>,
    val recentDebutCreators: List<HomeCreatorItem>,
    val firstAudioContents: List<HomeFirstAudioContentItem>,
    val aiCharacters: List<HomeAiCharacterItem>,
    val genreCreators: List<HomeGenreCreatorGroupItem>,
    val cheerCreators: List<HomeCreatorItem>,
    val popularCommunityPosts: List<HomePopularCommunityPostItem>
)

Swift 모델은 위 필드명을 그대로 Decodable로 구성한다. 서버 nullable 필드는 Swift optional로 선언한다.

7.2 모두 팔로우 하기

API

  • Method: POST
  • Path: /api/v2/home/recommendations/creators/follow
  • Request body:
data class FollowRecommendedCreatorsRequest(
    val creatorIds: List<Long>?
)
  • 응답 래퍼: 기존 관례대로 ApiResponseWithoutData 디코딩

동작

  • 장르 크리에이터 그룹과 최근 응원이 많은 크리에이터 섹션의 모두 팔로우하기 버튼에서 호출한다.
  • 요청 creatorIds는 해당 섹션에 표시된 크리에이터의 creatorId 목록을 사용한다.
  • API 호출 중 중복 터치를 막는다.
  • 호출 완료 후 success == true이면 해당 섹션 버튼 상태를 완료로 전환한다.
  • 완료 상태 버튼 문구는 모두 팔로우 완료로 표시한다.
  • 완료 상태 버튼 아이콘은 ic_new_following을 사용한다.
  • 실패 시 기존 앱 에러 표시 관례에 맞춰 공통 에러 또는 서버 message를 노출하고 버튼 상태는 변경하지 않는다.

7.3 활동 타입 다국어 표시

서버 enum:

enum class RecommendedActivityType(val code: String) {
    LIVE("LIVE"),
    AUDIO("AUDIO"),
    COMMUNITY("COMMUNITY"),
    LIVE_REPLAY("LIVE_REPLAY")
}

앱 변환:

  • LIVE, LIVE_REPLAY -> I18n.HomeRecommendation.activityLive
  • AUDIO -> I18n.HomeRecommendation.activityAudio
  • COMMUNITY -> I18n.HomeRecommendation.activityCommunity
  • 알 수 없는 값은 빈 문자열 또는 서버 코드 직접 표시 대신 해당 아이템의 보조 문구를 숨긴다.

다국어 기본 문구:

  • ko: 라이브, 오디오, 커뮤니티
  • en: Live, Audio, Community
  • ja: ライブ, オーディオ, コミュニティ

7.4 사업자 정보 더보기/접기

요구사항:

  • 외부 라이브러리를 사용하지 않는다.
  • 기본 상태는 최대 3줄 표시, 말줄임표 적용, 더보기 액션 제공
  • 더보기 터치 시 전체 표시로 전환하고 접기 액션 제공
  • 접기 터치 시 다시 3줄 말줄임표 상태로 돌아간다.

구현 방향:

  • SwiftUI TextlineLimit(isExpanded ? nil : 3)를 사용한다.
  • 더보기/접기 버튼은 텍스트 하단 우측 또는 Figma 사업자 정보 섹션 내 자연스러운 위치에 배치한다.
  • 실제 텍스트가 3줄 이하이면 더보기 버튼은 숨긴다. 줄 수 판정은 Geometry 기반 측정 또는 제한/무제한 높이 비교 방식으로 구현한다.

7.5 커뮤니티 포스트 카드

참조 Figma:

  • Text Only: node-id=446-9688
  • Text + Img, 유료 + 구매하지 않음: node-id=446-9690
  • Text + Img, 유료/무료 + 구매함: node-id=446-9691

요구사항:

  • CommunityPostCard는 다른 페이지에서도 재사용 가능하도록 SodaLive/Sources/V2/Component/Card 아래에 둔다.
  • 텍스트 전용, 이미지 포함, 유료 이미지 잠금 상태를 지원한다.
  • imageUrl == nil이면 Text Only variant로 표시한다.
  • imageUrl != nil && price > 0 && existOrdered == false이면 이미지 영역에 blur/lock/pay capsule을 표시한다.
  • imageUrl != nil && (price <= 0 || existOrdered == true)이면 이미지를 일반 노출한다.
  • 유료/무료 + 구매함 Figma 카드의 우측 상단 구매완료 캡슐은 구현하지 않는다.
  • 본문, 작성자, 생성 시간, 좋아요 수, 댓글 수는 표시한다.

8. UX / UI Expectations

8.1 Figma 기준 화면 구성

참조 Figma:

  • 추천 화면: node-id=24-5514
  • 모두 팔로우 완료 버튼: node-id=24-9092

Figma 확인 결과 추천 화면은 검정 배경, 상단 홈 타이틀/탭, 하단 메인 탭바 사이에 세로 스크롤 콘텐츠로 구성된다. 주요 섹션은 카드/가로 스크롤/그리드 조합이며, 반복 프로필과 버튼은 재사용 위젯화가 필요하다.

8.2 UI 배치 도식

아래 도식은 구현 대상만 포함한다. 추천 필모그래피, 또 다른 모습은 제외한다.

MainHomeView
└─ HomeTitleBar 재사용
└─ 홈 상단 탭(추천/랭킹/팔로잉)
└─ ScrollView
   ├─ 현재 라이브 섹션
   │  └─ MainHomeLiveSection 신규
   │     └─ MainHomeLiveItem 신규
   ├─ 배너 섹션
   │  └─ BannerCarousel 신규
   ├─ 최근 활동 크리에이터 섹션
   │  └─ MainHomeActiveCreatorSection 신규
   │     └─ MainHomeActiveCreatorItem 신규
   ├─ 최근 데뷔한 크리에이터 섹션
   │  ├─ SectionTitle 재사용
   │  └─ CreatorProfileGrid 재사용
   │     └─ CreatorProfileItem 재사용
   ├─ 처음 만나는 오디오 섹션
   │  ├─ SectionTitle 재사용
   │  └─ AudioContentCard 재사용/확장
   ├─ AI 캐릭터 섹션
   │  ├─ SectionTitle 재사용
   │  └─ AiCharacterCard 신규
   ├─ 장르의 크리에이터 섹션
   │  └─ MainHomeCreatorGroupSection 신규
   │     ├─ SectionTitle 재사용(size 보정 필요 시 신규 variant)
   │     ├─ MainHomeCreatorGrid 신규
   │     ├─ CreatorProfileItem 재사용
   │     └─ FollowAllButton 재사용
   ├─ 최근 응원이 많은 크리에이터 섹션
   │  └─ MainHomeCreatorGroupSection 재사용
   ├─ 인기 커뮤니티 섹션
   │  ├─ SectionTitle 재사용
   │  └─ CommunityPostCard 재사용
   └─ 사업자 정보 섹션
      └─ MainHomeBusinessInfoSection 신규
         └─ ExpandableTextView 재사용
└─ MainTabBarView 재사용 가능 여부 확인 후 적용

8.3 재사용 컴포넌트 후보

  • SodaLive/Sources/V2/Component/HomeTitleBar.swift: 홈 상단 로고/메뉴 타이틀바
  • SodaLive/Sources/V2/Component/SectionTitle.swift: 섹션 타이틀과 우측 액션 아이콘
  • SodaLive/Sources/V2/Component/AudioContentCard.swift: firstAudioContents 카드 기본 구조
  • SodaLive/Sources/V2/Main/MainTabBarView.swift: 하단 탭바가 V2 홈 구조와 맞는 경우 재사용
  • SodaLive/Sources/Chat/Character/Banner/AutoSlideCharacterBannerView.swift: 배너 carousel 패턴 참고. 홈 배너 타입과 이동 규칙이 달라 직접 재사용 여부는 계획 단계에서 재확인한다.
  • SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swift: 커뮤니티 카드의 작성자/본문/리액션 패턴 참고. 이번 홈 추천 응답 타입과 더보기 요구가 달라 직접 재사용보다는 신규 위젯 생성이 우선이다.

8.4 신규 파일/그룹 후보

MainView의 홈 탭에서 표시되는 페이지 조립 계층은 SodaLive/Sources/V2/Main/Home 아래에 둔다.

SodaLive/Sources/V2/Main/Home
├─ MainHomeView.swift
├─ MainHomeViewModel.swift
├─ Repository
│  ├─ MainHomeApi.swift
│  └─ MainHomeRepository.swift
├─ Models
│  ├─ MainHomeRecommendationResponse.swift
│  ├─ FollowRecommendedCreatorsRequest.swift
│  └─ RecommendedActivityType.swift
└─ Components
   ├─ MainHomeLiveSection.swift
   ├─ MainHomeLiveItem.swift
   ├─ MainHomeActiveCreatorSection.swift
   ├─ MainHomeActiveCreatorItem.swift
   ├─ MainHomeRecentDebutCreatorSection.swift
   ├─ MainHomeFirstAudioContentSection.swift
   ├─ MainHomeAiCharacterSection.swift
   ├─ MainHomeGenreCreatorSection.swift
   ├─ MainHomeCheerCreatorSection.swift
   ├─ MainHomeCreatorGroupSection.swift
   ├─ MainHomeCreatorGrid.swift
   ├─ MainHomePopularCommunitySection.swift
   └─ MainHomeBusinessInfoSection.swift

MainHome에서만 사용하는 섹션 조립 컴포넌트는 SodaLive/Sources/V2/Main/Home/Components 아래에 둔다. 여러 페이지에서 재사용 가능성이 있는 UI widget은 SodaLive/Sources/V2/Component 아래에서 형태별 폴더에 둔다. 공용 widget은 특정 페이지나 API 이름 접두사를 붙이지 않고, 가능한 한 API 모델에 직접 의존하지 않으며 표시용 프로퍼티 또는 작은 display model을 받아 재사용성을 확보한다.

SodaLive/Sources/V2/Component
├─ Banner
│  └─ BannerCarousel.swift
├─ Card
│  ├─ AiCharacterCard.swift
│  └─ CommunityPostCard.swift
├─ Creator
│  ├─ CreatorProfileGrid.swift
│  └─ CreatorProfileItem.swift
├─ Button
│  └─ FollowAllButton.swift
└─ Text
   └─ ExpandableTextView.swift

단, 구현 중 특정 widget이 홈 탭에서만 의미가 있고 재사용성이 없다고 판단되면 SodaLive/Sources/V2/Main/Home/Components 안에 유지한다. 반대로 이미 존재하는 공용 컴포넌트로 충분한 경우 신규 파일을 만들지 않는다.

8.5 컴포넌트 위치 결정 기준

  • BannerCarousel: 다른 페이지에서도 배너 carousel로 재사용 가능하므로 SodaLive/Sources/V2/Component/Banner에 둔다.
  • 방금 활동한 크리에이터 UI: MainHome에서만 사용하므로 MainHomeActiveCreatorSection, MainHomeActiveCreatorItemSodaLive/Sources/V2/Main/Home/Components에 둔다.
  • 현재 라이브 UI: 별도 재사용 요구가 없으므로 MainHomeLiveSection, MainHomeLiveItemSodaLive/Sources/V2/Main/Home/Components에 둔다.
  • 최근 데뷔한 크리에이터 UI: 다른 페이지에서도 재사용 가능하므로 공용 CreatorProfileGrid, CreatorProfileItem을 사용하고, 섹션 조립만 MainHomeRecentDebutCreatorSection에 둔다.
  • AiCharacterCard: 다른 페이지에서도 캐릭터 카드로 재사용 가능하므로 SodaLive/Sources/V2/Component/Card에 둔다.
  • 장르/응원이 많은 크리에이터: 그리드 그룹 구조는 MainHome 전용이므로 MainHomeCreatorGroupSection, MainHomeCreatorGridSodaLive/Sources/V2/Main/Home/Components에 둔다. 개별 크리에이터 아이템만 CreatorProfileItem으로 재사용한다.
  • 커뮤니티 섹션: 섹션 조립은 MainHomePopularCommunitySection에 두고, CommunityPostCard는 다른 페이지에서도 재사용 가능하므로 SodaLive/Sources/V2/Component/Card에 둔다.
  • 사업자 정보 섹션: 섹션 wrapper는 MainHomeBusinessInfoSection으로 SodaLive/Sources/V2/Main/Home/Components에 둔다. 3줄 말줄임표, 더보기, 접기를 담당하는 텍스트 UI는 다른 곳에서도 사용할 수 있는 ExpandableTextView로 분리해 SodaLive/Sources/V2/Component/Text에 둔다.

9. Technical Constraints

  • 앱 소스 변경은 기본적으로 SodaLive/Sources/**에서 수행한다.
  • MainView 홈 탭에서 표시되는 페이지 루트, ViewModel, Repository, API, Models와 MainHome 전용 섹션 컴포넌트는 SodaLive/Sources/V2/Main/Home/** 아래에 작성한다.
  • 여러 페이지에서 재사용 가능한 UI widget은 SodaLive/Sources/V2/Component/** 아래에 형태별 폴더로 작성한다.
  • 순수 공용성이 더 큰 컴포넌트는 구현 시점에 SodaLive/Sources/V2/Component/**의 더 적합한 하위 그룹으로 이동할 수 있다.
  • Pods/**, generated/**, build/**는 수정하지 않는다.
  • 기존 HomeApi에 추천 API를 추가하지 않는다.
  • 외부 라이브러리를 추가하지 않는다.
  • 이미지 로딩은 기존 앱에서 사용하는 이미지 컴포넌트/패턴을 따른다.
  • 인증 헤더는 기존 UserDefaultsKey.token 기반 패턴을 따른다.
  • creatorId, liveRoomId, bannerId, postId 등 서버 Long 값은 Swift에서 Int 또는 Int64 중 기존 라우팅/모델 관례와 맞는 타입을 사용한다. 계획 단계에서 실제 이동 대상 API 타입과 맞춰 확정한다.
  • API 날짜 문자열(beginDateTime, activityAt, releaseDate, createdAt)은 기존 날짜 포맷 유틸이 있으면 재사용한다.

10. Success Criteria

  • 추천 탭에서 /api/v2/home/recommendations를 호출하고 success == true 응답 데이터를 섹션별로 렌더링한다.
  • 응답 배열이 비어 있는 섹션은 화면에 표시하지 않는다.
  • 제외 섹션인 추천 필모그래피, 또 다른 모습은 코드와 화면에 포함하지 않는다.
  • 모두 팔로우 API 성공 시 해당 버튼이 Figma 완료 디자인에 맞게 모두 팔로우 완료ic_new_following 상태로 변경된다.
  • 사업자 정보는 기본 3줄 말줄임표, 더보기, 전체 표시, 접기 전환이 동작한다.
  • LIVE, LIVE_REPLAY, AUDIO, COMMUNITY 활동 타입이 I18n 문구로 표시된다.
  • 반복 UI가 Custom Widget으로 분리되어 같은 프로필/그룹/버튼 구조를 중복 구현하지 않는다.
  • 빌드가 성공하고, 가능하면 ViewModel 단위의 응답 디코딩 및 모두 팔로우 성공 상태 테스트가 통과한다.

11. Metrics

  • 추천 API 성공/실패 여부
  • 모두 팔로우 API 호출 성공/실패 여부
  • 추천 탭 첫 로딩 완료 시간
  • 모두 팔로우 버튼 터치 후 완료 상태 전환 여부

12. Open Questions

  • 배너 type별 이동 규칙(eventId, creatorId, seriesId, link)을 어떤 기존 라우팅과 연결할지 확인이 필요하다.
  • 각 섹션별 최대 표시 개수와 가로/세로 스크롤 정책이 Figma 기준 그대로인지, 서버 응답 전체를 모두 표시해야 하는지 확인이 필요하다.
  • creatorIds == null 요청을 허용해야 하는지, 앱에서는 빈 배열/비어 있는 섹션일 때 버튼을 숨기는 것으로 제한할지 확인이 필요하다.
  • 모두 팔로우 완료 상태를 앱 세션 동안만 유지할지, 추천 API 재조회 후에도 서버 상태 기반으로 유지할지 확인이 필요하다.
  • 각 카드 터치 시 상세 이동 대상이 모두 정의되어 있는지 확인이 필요하다.

13. Verification Plan

  • PRD 검증: 요구사항, 제외 범위, API URL, 응답 모델, Figma 노드가 문서에 반영되었는지 확인한다.
  • 구현 후 빌드 검증: docs/agent-guides/build-test-verification.md 기준 명령으로 iOS 빌드 또는 가능한 최소 검증을 실행한다.
  • 구현 후 기능 검증: 추천 API 성공/실패, 빈 섹션, 모두 팔로우 성공/실패, 사업자 정보 더보기/접기, 활동 타입 I18n 표시를 확인한다.