Files
sodalive-ios/docs/20260703_크리에이터_채널_라이브_탭/prd.md

18 KiB

PRD: 크리에이터 채널 라이브 탭

1. Overview

크리에이터 채널 공통 shell의 라이브 탭에서 현재 진행 중인 라이브와 라이브 다시듣기 목록을 제공한다. 화면 상단의 title bar, 크리에이터 프로필 header, tab-bar는 기존 크리에이터 채널 홈 구현을 재사용하고, tab-bar 아래 콘텐츠만 라이브 탭 전용 API 응답으로 교체한다.

라이브 탭은 GET /api/v2/creator-channels/{creatorId}/live 응답을 사용한다. 정렬 기본값은 LATEST이며, 사용자는 sort-bar에서 정렬을 변경할 수 있다. CreatorChannelLiveTabResponse.hasNext == true이면 현재 응답의 page + 1 페이지를 추가로 조회해 스크롤 로딩한다.

Figma 참조:

  • 전체 화면: 290:8945, 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=290-8945&m=dev
  • Sort-bar: 290:8949, 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=290-8949&m=dev
  • 라이브 다시듣기 아이템: 290:8954, 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=290-8954&m=dev
  • 라이브 다시듣기 아이템 대여/무료 상태: 290:8956, 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=290-8956&m=dev
  • 본인 채널 하단 CTA: 665:19359, 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=665-19359&m=dev
  • 정렬 컨텍스트 메뉴 예시: 290:9041, 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=290-9041&m=dev

추후 구현 계획 문서(plan-task.md)를 작성할 때 위 Figma URL을 기준 문서 섹션에 모두 포함한다.

2. Problem

  • 현재 크리에이터 채널 공통 shell은 홈 탭 중심으로 구현되어 있고, 라이브 탭의 실제 목록 API, 정렬, 페이지네이션이 연결되어 있지 않다.
  • 라이브 탭은 홈 탭의 현재 진행 중인 라이브 UI를 재사용해야 하지만, 라이브 다시듣기 목록은 가격, 포인트 사용 가능, 19금, 소장중, 대여중 상태를 함께 표시해야 하므로 별도 item 요구사항이 필요하다.
  • sort-bar는 라이브 탭 전용이 아니라 다른 탭에서도 같은 형태로 재사용될 수 있어 채널 공통 컴포넌트 경계가 필요하다.
  • 정렬 선택 UI는 Figma에 anchored context menu 형태가 있으나, 모바일 하단 접근성과 옵션 확장성을 고려해 앱 구현 방식 결정이 필요하다.
  • 본인 채널의 라이브 탭에서는 하단에 고정된 라이브 시작하기 CTA가 필요하며, 일반 사용자 채널 진입과 표시 조건이 달라야 한다.

3. Goals

  • CreatorChannelTab.live 선택 시 라이브 탭 API를 호출하고 응답 데이터로 화면을 구성한다.
  • GET /api/v2/creator-channels/{creatorId}/livepage, size, sort query parameter를 전달한다.
  • 기본 정렬은 LATEST로 호출하고, 서버 응답의 sort를 현재 선택 정렬 상태로 반영한다.
  • sort-bar는 SodaLive/Sources/V2/CreatorChannel/Components/** 아래에 생성해 라이브 탭 외 다른 탭에서도 재사용할 수 있게 한다.
  • 정렬 문구는 I18n에 ko/en/ja 다국어로 추가한다.
  • 정렬 선택 UI는 BottomSheet 방식을 채택한다.
  • 현재 진행 중인 라이브는 크리에이터 채널 홈의 CreatorChannelCurrentLiveSection UI를 재사용한다.
  • 라이브 다시듣기 item은 Figma 기준으로 썸네일, 제목, duration, 가격/포인트/무료/19금/소장중/대여중 상태를 표시한다.
  • hasNext == true이면 스크롤 하단 진입 시 현재 응답의 page + 1 페이지를 추가 조회한다.
  • 본인 채널이고 선택 탭이 라이브일 때 하단 고정 라이브 시작하기 버튼을 표시한다.

4. Non-Goals

  • 크리에이터 채널 공통 shell, header, sticky tab-bar 동작을 다시 설계하지 않는다.
  • 홈 탭의 현재 라이브 UI를 새로 만들지 않는다.
  • 라이브룸 또는 라이브 생성 화면 자체를 새로 구현하지 않는다. 기존 라이브 시작/생성 라우팅이 있으면 재사용한다.
  • 다시듣기 구매, 대여, 재생 권한 mutation을 새로 구현하지 않는다. item 표시와 기존 상세/재생 진입 연결까지만 범위로 본다.
  • API enum에 없는 추천순 정렬은 구현하지 않는다.
  • Figma localhost asset URL을 앱 코드에 직접 사용하지 않는다.
  • Pods/**, generated/**, build/**는 수정하지 않는다.

5. Target Users

  • 크리에이터 채널에서 진행 중인 라이브와 라이브 다시듣기를 탐색하려는 사용자
  • 가격, 포인트 사용 가능 여부, 소장/대여 상태를 보고 다시듣기 콘텐츠를 선택하려는 사용자
  • 자신의 채널 라이브 탭에서 새 라이브를 시작하려는 크리에이터

6. User Stories

  • 사용자는 크리에이터 채널 라이브 탭에서 현재 진행 중인 라이브가 있으면 상단에서 바로 확인하고 싶다.
  • 사용자는 라이브 다시듣기 전체 개수와 현재 정렬 기준을 확인하고 싶다.
  • 사용자는 최신순, 인기순, 소장순, 가격 높은 순, 가격 낮은 순으로 다시듣기 목록을 정렬하고 싶다.
  • 사용자는 목록을 아래로 스크롤하면 다음 페이지가 자동으로 이어서 로드되기를 기대한다.
  • 사용자는 다시듣기 item에서 무료, 가격, 포인트 사용 가능, 19금, 소장중, 대여중 상태를 구분하고 싶다.
  • 크리에이터는 본인 채널의 라이브 탭에서 하단 고정 버튼으로 라이브 시작 화면에 빠르게 진입하고 싶다.

7. Core Requirements

7.1 API

  • Method: GET
  • Path: /api/v2/creator-channels/{creatorId}/live
  • Path parameter: creatorId
  • Query parameters:
    • page
    • size
    • sort
  • 인증: 기존 V2 API 인증 헤더 패턴을 따른다.
  • 응답 래퍼: 기존 관례대로 ApiResponse<CreatorChannelLiveTabResponse> 디코딩을 우선한다.

Response 기준

data class CreatorChannelLiveTabResponse(
    val liveReplayContentCount: Int,
    val currentLive: CreatorChannelLiveResponse?,
    val liveReplayContents: List<CreatorChannelAudioContentResponse>,
    val sort: ContentSort,
    val page: Int,
    val size: Int,
    val hasNext: Boolean
)

data class CreatorChannelAudioContentResponse(
    val audioContentId: Long,
    val title: String,
    val duration: String?,
    val imageUrl: String?,
    val price: Int,
    val isAdult: Boolean,
    val isPointAvailable: Boolean,
    val isFirstContent: Boolean,
    val seriesName: String?,
    val isOriginalSeries: Boolean?,
    val isOwned: Boolean,
    val isRented: Boolean
)

enum class ContentSort {
    LATEST,
    POPULAR,
    OWNED,
    PRICE_HIGH,
    PRICE_LOW
}

Swift 모델 기준

struct CreatorChannelLiveTabResponse: Decodable {
    let liveReplayContentCount: Int
    let currentLive: CreatorChannelLiveResponse?
    let liveReplayContents: [CreatorChannelAudioContentResponse]
    let sort: ContentSort
    let page: Int
    let size: Int
    let hasNext: Bool
}

enum ContentSort: String, Decodable, CaseIterable {
    case latest = "LATEST"
    case popular = "POPULAR"
    case owned = "OWNED"
    case priceHigh = "PRICE_HIGH"
    case priceLow = "PRICE_LOW"
}
  • CreatorChannelLiveResponseCreatorChannelAudioContentResponse는 기존 SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift의 모델을 우선 재사용한다.
  • audioContentId 같은 Kotlin Long 값은 기존 V2 모델 관례대로 Swift Int로 선언한다.
  • ContentSort는 라이브 탭뿐 아니라 다른 콘텐츠 탭에서도 재사용될 수 있으므로 계획 단계에서 공용 위치를 정한다.

7.2 라이브 탭 상태

  • CreatorChannelViewModel 또는 라이브 탭 전용 ViewModel은 선택 탭이 live가 될 때 라이브 탭 데이터를 조회한다.
  • 동일 화면 생명주기에서 같은 creatorId, sort, 첫 페이지 데이터가 이미 있으면 불필요한 중복 호출을 피한다.
  • 정렬 변경 시 첫 페이지부터 다시 조회하고, 조회 성공 시에만 기존 liveReplayContents를 새 정렬 결과로 교체한다.
  • API 실패 시 크리에이터 채널 공통 shell은 유지하고, 이미 표시 중인 liveReplayContents는 유지한 채 기존 V2 패턴의 오류/빈 상태를 표시한다.
  • liveReplayContentCount == 0이고 currentLive == nil이면 sort-bar의 전체 개수는 0으로 표시하고 목록 empty state를 표시한다.

7.3 Sort-bar

  • 생성 위치: SodaLive/Sources/V2/CreatorChannel/Components/**
  • sort-bar는 좌측에 전체 개수, 우측에 현재 정렬명과 아래 방향 아이콘을 표시한다.
  • Figma 기준 높이는 52pt, 좌우 horizontal padding은 기존 SodaSpacing 토큰으로 맞춘다.
  • 전체 개수는 liveReplayContentCount를 사용한다.
  • 문구 전체, 정렬명은 I18n에 추가한다.
  • sort-bar는 다른 탭에서도 재사용 가능하도록 totalCount, selectedSort, availableSorts, onTapSort 같은 일반화된 입력을 받는다.
  • 이번 범위에서는 Figma의 리스트형 view mode 옵션은 사용하지 않는다.

7.4 정렬 선택 UI

  • 정렬 선택 UI는 BottomSheet 방식으로 구현한다.
  • 선택 사유:
    • 모바일 한 손 조작에서 화면 하단 sheet가 우측 상단 anchored menu보다 터치 접근성이 좋다.
    • 정렬 옵션이 5개이고 이후 탭별 옵션 추가 가능성이 있어 sheet가 선택 영역과 dismiss 동작을 안정적으로 제공한다.
    • iOS/Material 계열 최신 모바일 앱에서 필터/정렬 같은 목록 제어는 sheet 패턴이 일반적이며, context menu는 짧은 보조 action에 더 적합하다.
  • 정렬 sheet 항목:
    • LATEST: 최신순
    • POPULAR: 인기순
    • OWNED: 소장순
    • PRICE_HIGH: 가격 높은 순
    • PRICE_LOW: 가격 낮은 순
  • Figma 290:9041에는 추천순이 있으나 API ContentSort에 없으므로 구현하지 않는다.
  • 현재 선택된 sort는 시각적으로 선택 상태를 표시한다.
  • sort item 터치 시 sheet를 닫고 첫 페이지를 재조회한다.

7.5 현재 진행 중인 라이브

  • currentLive != nil이면 sort-bar 아래 또는 Figma 배치 기준 위치에 현재 진행 중인 라이브 UI를 표시한다.
  • UI는 홈 탭의 CreatorChannelCurrentLiveSection / CreatorChannelCurrentLiveCard를 재사용한다.
  • currentLive == nil이면 현재 라이브 영역은 숨긴다.
  • 현재 라이브 card tap은 기존 홈 탭과 동일한 라이브 상세/라이브룸 진입 흐름을 사용한다.

7.6 라이브 다시듣기 item

  • 목록 데이터는 liveReplayContents를 사용한다.
  • item은 Figma 기준으로 가로 row 형태를 사용한다.
  • 표시 필드:
    • imageUrl: 88pt 정사각 썸네일, corner radius는 기존 컴포넌트/Figma 기준을 따른다.
    • title: 1줄 또는 Figma 상태처럼 최대 2줄까지 표시하고 영역 초과 시 말줄임 처리한다.
    • duration: 값이 있으면 subtitle로 표시한다.
    • price: 0이면 무료 태그, 0보다 크면 can 가격 표시
    • isPointAvailable: point 태그 또는 point 가격 표시
    • isAdult: 19금 shield 태그 표시
    • isFirstContent: 첫 회차 태그 표시
    • seriesName: 필요 시 duration 보조 정보로 표시하되 Figma 라이브 다시듣기 item 우선순위를 해치지 않는다.
    • isOriginalSeries: original series 태그 표시
    • isOwned: 우측 상태 영역에 소장중 표시
    • isRented: 우측 상태 영역에 대여중 표시
  • isOwned == true 또는 isRented == true이면 가격 대신 play button과 상태 텍스트를 우선 표시한다.
  • isOwned == trueisRented == true가 동시에 내려오면 소장중 표시를 우선한다.
  • item tap은 오디오 상세 화면으로 이동한다.

7.7 스크롤 로딩

  • 최초 요청은 page = 0, size = 20, 기본 sort LATEST로 첫 페이지를 조회한다.
  • 스크롤 하단 또는 마지막 item 노출 시점에 hasNext == true이고 추가 로딩 중이 아니면 다음 페이지를 요청한다.
  • 다음 페이지 query의 page는 현재 응답의 page + 1을 사용한다.
  • 다음 페이지 query의 size20으로 유지한다.
  • 첫 페이지 조회 성공 시 기존 liveReplayContents를 조회 결과로 교체한다.
  • 첫 페이지 조회 실패 시 기존 목록과 pagination 상태는 유지하고, 필요하면 기존 토스트/오류 표시 패턴을 따른다.
  • 추가 조회 성공 시 기존 liveReplayContents 뒤에 새 page의 목록을 append한다.
  • 추가 조회 실패 시 기존 목록과 pagination 상태는 유지하고, 필요하면 기존 토스트/오류 표시 패턴을 따른다.
  • sort 변경 중이거나 첫 페이지 로딩 중에는 pagination 요청을 중복 실행하지 않는다.

7.8 본인 채널 하단 라이브 시작하기 CTA

  • 본인 채널 여부는 기존 docs/20260703_크리에이터_채널_본인_진입_플로팅_버튼/prd.md의 판별 기준을 재사용한다.
  • 본인 채널이고 selectedTab == .live일 때만 하단 고정 라이브 시작하기 버튼을 표시한다.
  • 라이브 시작하기 버튼 아이콘은 ic_new_create_live asset을 사용한다.
  • CTA는 화면 하단에 고정하고 safe area bottom을 고려한다.
  • CTA가 목록 마지막 item을 가리지 않도록 라이브 탭 scroll content에 bottom inset/padding을 추가한다.
  • 버튼 터치 시 기존 라이브 생성/시작 라우팅을 재사용한다.
  • Figma node 665:19359의 버튼 텍스트는 현재 후원하기로 확인되지만, 사용자 요구사항이 라이브 시작하기이므로 구현 문구와 액션은 라이브 시작하기를 기준으로 한다.

8. UX / UI Expectations

  • 크리에이터 채널 공통 header, sticky tab-bar, title bar background progress 동작은 홈 탭과 동일하게 유지한다.
  • 라이브 탭 선택 시 tab-bar indicator는 기존 CreatorChannelTabBar의 선택 상태를 따른다.
  • 라이브 탭 콘텐츠 배경은 기존 크리에이터 채널 dark theme와 맞춘다.
  • sort-bar는 tab-bar 아래에 붙어 보이고, 목록 콘텐츠와 겹치지 않아야 한다.
  • 다시듣기 item의 썸네일, 제목, 가격/상태 영역은 긴 제목과 다국어 문구에서도 겹치지 않아야 한다.
  • BottomSheet는 dim 또는 drag/dismiss 동작이 기존 앱 sheet 패턴과 일관되어야 한다.
  • 로딩 중에는 기존 목록을 과도하게 흔들지 않고, 첫 페이지 로딩과 추가 페이지 로딩 상태를 구분한다. 목록 교체와 append는 조회 성공 시에만 수행한다.

9. Technical Constraints

  • 기능 변경은 SodaLive/Sources/V2/CreatorChannel/** 하위에서 해결한다.
  • 재사용 가능한 sort-bar는 SodaLive/Sources/V2/CreatorChannel/Components/** 아래에 둔다.
  • 라이브 탭 전용 View, ViewModel, Repository, API, 모델은 SodaLive/Sources/V2/CreatorChannel/Live/** 아래에 둔다.
  • 현재 라이브 UI는 SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift를 재사용한다.
  • 기존 CreatorChannelAudioContentResponse를 재사용하되, 모델 위치가 홈 탭에 종속되어 유지보수성이 떨어지면 계획 단계에서 SodaLive/Sources/V2/CreatorChannel/Models/**로 이동을 검토한다.
  • 신규 사용자 노출 문구는 SodaLive/Sources/I18n/I18n.swift에 ko/en/ja를 추가한다.
  • API 구현은 기존 V2 TargetType, Repository, ApiResponse<T> 디코딩 패턴을 따른다.
  • Figma의 localhost asset URL은 코드에 넣지 않고 기존 asset 또는 기존 태그 컴포넌트를 사용한다.

10. Success Criteria

  • 라이브 탭 선택 시 GET /api/v2/creator-channels/{creatorId}/live?page={page}&size={size}&sort={sort}가 호출된다.
  • 기본 sort는 LATEST이며 sort-bar에는 최신순이 표시된다.
  • 최초 요청은 page=0, size=20으로 호출된다.
  • sort-bar 좌측에 liveReplayContentCount 기준 전체 개수가 표시된다.
  • sort-bar 터치 시 BottomSheet가 표시되고 LATEST, POPULAR, OWNED, PRICE_HIGH, PRICE_LOW 5개 옵션만 보인다.
  • 정렬 변경 시 첫 페이지부터 다시 조회되고, 조회 성공 시에만 목록이 새 정렬 결과로 교체된다.
  • currentLive != nil이면 홈 탭과 동일한 현재 라이브 UI가 라이브 탭에 표시된다.
  • 라이브 다시듣기 item에 가격, 포인트 사용 가능, 19금, 소장중, 대여중 상태가 조건에 맞게 표시된다.
  • hasNext == true 상태에서 하단 스크롤 시 page + 1 페이지가 호출되고, 조회 성공 시에만 기존 목록 뒤에 append된다.
  • hasNext == false이면 추가 페이지 호출을 하지 않는다.
  • 본인 채널의 라이브 탭 하단에는 라이브 시작하기 고정 버튼이 표시된다.
  • 라이브 시작하기 고정 버튼에는 ic_new_create_live 아이콘이 표시된다.
  • 타인 채널 또는 라이브 외 탭에서는 라이브 시작하기 버튼이 표시되지 않는다.
  • xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build가 성공한다.

11. Decisions

  • 정렬 메뉴 표시는 BottomSheet 방식을 채택한다.
  • 첫 페이지의 page 시작값은 0으로 한다.
  • 요청 size 기본값은 20으로 한다.
  • Figma 정렬 메뉴 예시에 있는 추천순은 API enum에 없으므로 제외한다.
  • Sort-bar는 라이브 탭 전용 위치가 아니라 V2/CreatorChannel/Components/**에 둔다.
  • 현재 진행 중인 라이브 UI는 홈 탭 구현을 재사용한다.
  • isOwned == true일 때 표시 문구는 소장중으로 한다.
  • 라이브 다시듣기 item tap은 오디오 상세 화면으로 이동한다.
  • 본인 채널 하단 CTA 문구와 action은 사용자 요구사항의 라이브 시작하기를 기준으로 한다.
  • 라이브 시작하기 버튼 아이콘은 ic_new_create_live를 사용한다.

12. Open Questions

  • 해당 없음.