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

31 KiB

크리에이터 채널 라이브 탭 구현 계획

기준 문서

  • PRD: docs/20260703_크리에이터_채널_라이브_탭/prd.md
  • 기존 크리에이터 채널 홈 PRD: docs/20260701_크리에이터_채널_홈/prd.md
  • 기존 크리에이터 채널 홈 구현 계획: docs/20260701_크리에이터_채널_홈/plan-task.md
  • 본인 채널 플로팅 버튼 PRD: docs/20260703_크리에이터_채널_본인_진입_플로팅_버튼/prd.md
  • 코드 스타일: docs/agent-guides/code-style.md
  • 빌드/검증: docs/agent-guides/build-test-verification.md
  • 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
  • Figma 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
  • Figma 라이브 다시듣기 아이템: 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
  • Figma 라이브 다시듣기 아이템 대여/무료 상태: 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
  • Figma 본인 채널 하단 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
  • Figma 정렬 컨텍스트 메뉴 예시: 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

구현 원칙

  • 기존 CreatorChannelView의 공통 shell, header, sticky tab-bar, title bar 동작은 유지한다.
  • CreatorChannelTab.live 콘텐츠만 신규 CreatorChannelLiveTabView로 교체한다.
  • 홈 탭의 CreatorChannelCurrentLiveSection과 기존 showLiveDetail(_:), showContentDetail(_:), showCreateLive() 흐름을 재사용한다.
  • CreatorChannelLiveResponseCreatorChannelAudioContentResponse는 기존 SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift 타입을 우선 재사용한다. 타입 이동은 이번 구현에 필수일 때만 수행한다.
  • ContentSort는 라이브 탭 외 콘텐츠 탭에서 재사용 가능하도록 SodaLive/Sources/V2/CreatorChannel/Models/** 아래에 둔다.
  • sort-bar와 정렬 BottomSheet는 SodaLive/Sources/V2/CreatorChannel/Components/** 아래에 둔다.
  • 라이브 탭 전용 API, Repository, ViewModel, View, item UI는 SodaLive/Sources/V2/CreatorChannel/Live/** 아래에 둔다.
  • 최초 요청은 page = 0, size = 20, sort = LATEST로 고정한다.
  • hasNext == true일 때만 page + 1 추가 요청을 수행하고, 추가 요청 중 중복 호출을 막는다.
  • 본인 채널 라이브 탭의 하단 CTA는 ic_new_create_live asset과 I18n.CreatorChannelLive.startLive 문구를 사용한다.
  • Figma localhost asset URL은 앱 코드에 사용하지 않는다.

주요 대상 파일

생성

  • SodaLive/Sources/V2/CreatorChannel/Models/ContentSort.swift
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/Models/CreatorChannelLiveTabResponse.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveApi.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveRepository.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveViewModel.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
  • SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveStartButton.swift

수정

  • SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift
  • SodaLive/Sources/I18n/I18n.swift
  • 필요 시 SodaLive.xcodeproj/project.pbxproj

확인

  • SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift
  • SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift
  • SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHomeAudioContentListItem.swift
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingActionMenu.swift
  • SodaLive/Sources/App/AppStep.swift
  • SodaLive/Sources/ContentView.swift
  • SodaLive/Resources/Assets.xcassets/v2/ic_new_create_live.imageset

TASK 체크리스트

Phase 1: PRD 결정 반영과 API 모델

  • Task 1.1: 라이브 시작하기 CTA 아이콘 결정 반영 확인

    • 대상 파일:
      • 확인: docs/20260703_크리에이터_채널_라이브_탭/prd.md
      • 확인: SodaLive/Resources/Assets.xcassets/v2/ic_new_create_live.imageset
    • 작업 내용:
      • PRD의 본인 채널 하단 CTA 요구사항, Success Criteria, Decisions에 ic_new_create_live 사용이 명시되어 있는지 확인한다.
      • asset 경로가 존재하는지 확인한다.
    • 검증 기준:
      • 실행 명령: rg "ic_new_create_live|라이브 시작하기" docs/20260703_크리에이터_채널_라이브_탭/prd.md SodaLive/Resources/Assets.xcassets/v2
      • 기대 결과: PRD와 asset 경로에서 ic_new_create_live가 확인된다.
  • Task 1.2: ContentSort enum 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Models/ContentSort.swift
      • 수정: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • enum ContentSort: String, Decodable, CaseIterable을 생성한다.
      • case는 latest = "LATEST", popular = "POPULAR", priceHigh = "PRICE_HIGH", priceLow = "PRICE_LOW"로 둔다.
      • var title: String에서 I18n.CreatorChannelLive.Sort.latest, popular, priceHigh, priceLow를 반환한다.
      • I18n.CreatorChannelLive.Sort에 ko/en/ja 문구를 추가한다.
    • 검증 기준:
      • 실행 명령: rg "enum ContentSort|case latest = \"LATEST\"|case popular = \"POPULAR\"|case priceHigh = \"PRICE_HIGH\"|case priceLow = \"PRICE_LOW\"|var title" SodaLive/Sources/V2/CreatorChannel/Models/ContentSort.swift
      • 기대 결과: API enum 값 4개와 title mapping이 존재한다.
      • 실행 명령: rg "enum CreatorChannelLive|enum Sort|latest|popular|priceHigh|priceLow" SodaLive/Sources/I18n/I18n.swift
      • 기대 결과: 라이브 탭 정렬 다국어 문구가 존재한다.
  • Task 1.3: 라이브 탭 Response model 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/Models/CreatorChannelLiveTabResponse.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift
    • 작업 내용:
      • CreatorChannelLiveTabResponseDecodable로 생성한다.
      • 필드는 liveReplayContentCount, currentLive, liveReplayContents, sort, page, size, hasNext로 둔다.
      • currentLive는 기존 CreatorChannelLiveResponse?, liveReplayContents는 기존 [CreatorChannelAudioContentResponse]를 사용한다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelLiveTabResponse|liveReplayContentCount|currentLive: CreatorChannelLiveResponse\\?|liveReplayContents: \\[CreatorChannelAudioContentResponse\\]|sort: ContentSort|page: Int|size: Int|hasNext: Bool" SodaLive/Sources/V2/CreatorChannel/Live/Models/CreatorChannelLiveTabResponse.swift
      • 기대 결과: PRD 기준 응답 필드가 모두 존재한다.

Phase 2: API, Repository, ViewModel

  • Task 2.1: 라이브 탭 API endpoint 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveApi.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeApi.swift
    • 작업 내용:
      • CreatorChannelLiveApiTargetType으로 생성한다.
      • case는 getLive(creatorId: Int, page: Int, size: Int, sort: ContentSort)로 둔다.
      • path는 /api/v2/creator-channels/{creatorId}/live로 구성한다.
      • method는 .get으로 둔다.
      • task는 .requestParameters(parameters: ["page": page, "size": size, "sort": sort.rawValue], encoding: URLEncoding.queryString)로 둔다.
      • headers는 기존 V2 API와 동일하게 Authorization: Bearer {token} 패턴을 따른다.
    • 검증 기준:
      • 실행 명령: rg "enum CreatorChannelLiveApi|getLive\\(creatorId: Int, page: Int, size: Int, sort: ContentSort\\)|/api/v2/creator-channels/|/live|requestParameters|URLEncoding.queryString|sort.rawValue|Authorization" SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveApi.swift
      • 기대 결과: live endpoint, query parameters, 인증 헤더가 확인된다.
  • Task 2.2: 라이브 탭 Repository 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveRepository.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift
    • 작업 내용:
      • CreatorChannelLiveRepository를 만들고 MoyaProvider<CreatorChannelLiveApi>를 사용한다.
      • getLive(creatorId:page:size:sort:) -> AnyPublisher<Response, MoyaError> 메서드를 제공한다.
    • 검증 기준:
      • 실행 명령: rg "final class CreatorChannelLiveRepository|MoyaProvider<CreatorChannelLiveApi>|getLive\\(creatorId: Int, page: Int, size: Int, sort: ContentSort\\)|AnyPublisher<Response, MoyaError>" SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveRepository.swift
      • 기대 결과: 기존 V2 Repository와 같은 형태가 확인된다.
  • Task 2.3: 라이브 탭 ViewModel 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveViewModel.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift
      • 확인: SodaLive/Sources/Content/All/ContentAllViewModel.swift
    • 작업 내용:
      • CreatorChannelLiveViewModelObservableObject로 생성한다.
      • 상태는 isLoading, isLoadingNextPage, response, liveReplayContents, selectedSort, page, size, hasNext, hasLoaded, errorMessage, isShowPopup를 둔다.
      • size 기본값은 20, selectedSort 기본값은 .latest, 최초 page0으로 둔다.
      • fetchFirstPage(creatorId:)page = 0 첫 페이지를 조회하되 요청 전 기존 목록과 pagination 상태를 비우지 않는다.
      • fetchNextPageIfNeeded(creatorId:currentItem:)는 마지막 item 노출, hasNext == true, 추가 로딩 아님 조건에서 page + 1을 조회한다.
      • selectSort(_:creatorId:)는 sort가 바뀔 때만 selectedSort를 갱신하고 첫 페이지를 다시 조회한다.
      • 첫 페이지 성공 시 response, page, size, hasNext를 갱신하고 liveReplayContents를 조회 결과로 교체한다. 다음 페이지 성공 시 같은 상태를 갱신하고 liveReplayContents 뒤에 append한다.
      • 첫 페이지 실패와 다음 페이지 실패 모두 기존 목록과 pagination 상태를 유지하고 errorMessage, isShowPopup를 갱신한다.
    • 검증 기준:
      • 실행 명령: rg "final class CreatorChannelLiveViewModel|isLoadingNextPage|liveReplayContents|selectedSort|private let pageSize|fetchFirstPage\\(creatorId: Int\\)|fetchNextPageIfNeeded|selectSort|ApiResponse<CreatorChannelLiveTabResponse>|page \\+ 1|hasNext" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveViewModel.swift
      • 기대 결과: 첫 페이지 로딩, 정렬 변경, 다음 페이지 로딩 상태와 성공 시에만 목록 교체/append하는 동작이 확인된다.

Phase 3: sort-bar와 정렬 BottomSheet

  • Task 3.1: sort-bar 공통 컴포넌트 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift
      • 수정: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • CreatorChannelSortBarView로 생성한다.
      • 입력값은 selectedSort: ContentSort, onTapSort: () -> Void로 둔다.
      • selectedSort.title과 정렬 아이콘을 표시한다.
      • 높이는 Figma 기준 52pt로 둔다.
      • 배경은 black, 텍스트는 기존 Color.gray400, Color.gray500, white 토큰을 따른다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelSortBar|selectedSort: ContentSort|onTapSort|selectedSort.title|frame\\(height: 52" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift
      • 기대 결과: 현재 정렬과 터치 action이 있는 sort-bar가 확인된다.
  • Task 3.2: 정렬 BottomSheet 컴포넌트 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift
    • 작업 내용:
      • CreatorChannelSortBottomSheetView로 생성한다.
      • 입력값은 selectedSort: ContentSort, sorts: [ContentSort] = ContentSort.allCases, onSelect: (ContentSort) -> Void로 둔다.
      • 항목은 LATEST, POPULAR, PRICE_HIGH, PRICE_LOW 순서로 표시한다.
      • Figma context menu의 추천순은 표시하지 않는다.
      • 선택된 항목은 배경색 또는 체크 아이콘 없이도 충분히 구분되는 foreground/background 상태를 적용한다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelSortBottomSheet|selectedSort: ContentSort|ContentSort.allCases|onSelect|ForEach|sort.title" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift
      • 기대 결과: ContentSort 4개 항목 기반 BottomSheet가 확인된다.
      • 실행 명령: rg "추천순|recommend" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift
      • 기대 결과: 검색 결과가 없다.

Phase 4: 라이브 탭 UI

  • Task 4.1: 라이브 다시듣기 item 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveReplayListItem.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelAudioContentListItem.swift
      • 확인: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • CreatorChannelLiveReplayListItemView로 생성한다.
      • 입력값은 audioContent: CreatorChannelAudioContentResponse, action: () -> Void로 둔다.
      • 88pt 썸네일, 제목, duration, 우측 가격/상태 영역을 구성한다.
      • price == 0이면 무료 태그를 표시한다.
      • isPointAvailable == true이면 point 태그 또는 point 가격 표시를 적용한다.
      • isAdult == true이면 19금 shield 태그를 표시한다.
      • isOwned == true이면 우측 상태 영역에 play button과 I18n.Content.Status.owned 또는 신규 I18n.CreatorChannelLive.owned 문구를 표시한다.
      • isRented == true이고 isOwned == false이면 play button과 I18n.Content.Status.rented 또는 신규 I18n.CreatorChannelLive.rented 문구를 표시한다.
      • isOwned == true && isRented == true이면 소장중을 우선 표시한다.
      • tap 시 action()을 호출한다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelLiveReplayListItem|CreatorChannelAudioContentResponse|audioContent.isOwned|audioContent.isRented|audioContent.isAdult|audioContent.isPointAvailable|audioContent.price == 0|소장중|대여중|action\\(\\)" SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveReplayListItem.swift
      • 기대 결과: 다시듣기 item의 상태 표시 조건과 tap action이 확인된다.
  • Task 4.2: 라이브 시작하기 하단 CTA 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveStartButton.swift
      • 수정: SodaLive/Sources/I18n/I18n.swift
      • 확인: SodaLive/Resources/Assets.xcassets/v2/ic_new_create_live.imageset
    • 작업 내용:
      • CreatorChannelLiveStartButtonView로 생성한다.
      • 입력값은 action: () -> Void로 둔다.
      • full-width capsule button으로 표시한다.
      • 아이콘은 Image("ic_new_create_live")를 사용한다.
      • 문구는 I18n.CreatorChannelLive.startLive를 사용한다.
      • safe area bottom과 겹치지 않도록 부모 view에서 bottom padding을 줄 수 있게 고정 높이를 안정적으로 유지한다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelLiveStartButton|ic_new_create_live|I18n.CreatorChannelLive.startLive|Button\\(action: action\\)|Capsule|frame\\(maxWidth: \\.infinity" SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveStartButton.swift
      • 기대 결과: 지정 아이콘과 문구를 사용하는 CTA 컴포넌트가 확인된다.
  • Task 4.3: 라이브 탭 화면 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift
    • 작업 내용:
      • CreatorChannelLiveTabViewView로 생성한다.
      • 입력값은 creatorId: Int, isOwnCreatorChannel: Bool, onTapLive: (Int) -> Void, onTapContent: (Int) -> Void로 둔다.
      • 내부에서 @StateObject private var viewModel = CreatorChannelLiveViewModel()을 사용한다.
      • onAppear에서 hasLoaded == false이면 fetchFirstPage(creatorId:)를 호출한다.
      • sort-bar 터치 시 .sheet 또는 기존 앱 sheet 패턴으로 CreatorChannelSortBottomSheet를 표시한다.
      • currentLive != nil이면 CreatorChannelCurrentLiveSection(currentLive:onTapLive:)를 표시한다.
      • liveReplayContentsLazyVStack으로 표시하고 각 item의 onAppear에서 fetchNextPageIfNeeded를 호출한다.
      • item tap은 onTapContent(audioContent.audioContentId)로 연결한다.
      • CTA가 부모 화면의 고정 overlay와 겹치지 않도록 scroll content bottom padding을 추가한다.
    • 검증 기준:
      • 실행 명령: rg "struct CreatorChannelLiveTabView|CreatorChannelLiveViewModel|fetchFirstPage\\(creatorId: creatorId\\)|CreatorChannelSortBar|CreatorChannelSortBottomSheet|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|fetchNextPageIfNeeded|onTapContent\\(audioContent.audioContentId\\)|isOwnCreatorChannel" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift
      • 기대 결과: 라이브 탭 화면, 정렬, 현재 라이브, 다시듣기 목록, pagination이 확인된다.
  • Task 4.4: 라이브 탭 Empty 상태 추가

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift
      • 수정: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • currentLive == nil이고 liveReplayContentCount == 0이면 Empty 상태를 표시한다.
      • Empty 상태에서는 CreatorChannelSortBar, CreatorChannelCurrentLiveSection, 다시듣기 list를 숨긴다.
      • Empty 문구는 I18n.CreatorChannelLive.emptyMessage를 사용하고 크리에이터가 라이브를 준비중입니다.\n기대해 주세요!로 표시한다.
      • Empty 문구 영역에는 vertical padding 48pt를 적용한다.
    • 검증 기준:
      • 실행 명령: rg "emptyMessage|isEmptyState|padding\(\.vertical, 48\)|CreatorChannelSortBar" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift SodaLive/Sources/I18n/I18n.swift
      • 기대 결과: Empty 조건, 문구, vertical padding, sort-bar 숨김 분기가 확인된다.

Phase 5: CreatorChannelView 연결

  • Task 5.1: 라이브 탭 콘텐츠 연결

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift
    • 작업 내용:
      • selectedTabContent에서 viewModel.selectedTab == .live일 때 CreatorChannelLiveTabView를 표시한다.
      • creatorId, isOwnCreatorChannel, showLiveDetail, showContentDetail을 전달한다.
      • 본인 채널의 라이브 탭 CTA는 CreatorChannelView의 최상위 overlay에서 CreatorChannelLiveStartButton(action: showCreateLive)로 표시한다.
      • 기존 홈 탭 분기는 유지한다.
      • 홈 외 준비 중 화면 분기는 live를 제외한 탭에만 적용한다.
    • 검증 기준:
      • 실행 명령: rg "CreatorChannelLiveTabView|viewModel\\.selectedTab == \\.live|isOwnCreatorChannel|onTapLive: showLiveDetail|onTapContent: showContentDetail|CreatorChannelLiveStartButton\\(action: showCreateLive\\)" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift
      • 기대 결과: 라이브 탭이 준비 중 화면이 아니라 신규 view로 연결된다.
  • Task 5.2: 본인 채널 홈 플로팅 메뉴와 라이브 CTA 조건 분리 확인

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingActionMenu.swift
    • 작업 내용:
      • 기존 CreatorChannelFloatingActionMenu는 본인 채널 home 탭에서만 유지한다.
      • 라이브 탭에서는 CreatorChannelView 최상위 overlay의 하단 CTA만 표시되도록 한다.
      • tab 변경 시 기존 isCreatorActionMenuPresented = false 동작은 유지한다.
    • 검증 기준:
      • 실행 명령: rg "isOwnCreatorChannel && viewModel\\.selectedTab == \\.home|isCreatorActionMenuPresented = false|CreatorChannelLiveTabView" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift
      • 기대 결과: 홈 floating menu와 라이브 하단 CTA 표시 조건이 분리되어 있다.

Phase 6: 오디오 콘텐츠 아이템 컴포넌트 명명 리팩토링

  • Task 6.1: 공용 오디오 콘텐츠 아이템 이동과 이름 변경

    • 대상 파일:
      • 이동/변경: SodaLive/Sources/V2/CreatorChannel/Live/Components/CreatorChannelLiveReplayListItem.swift -> SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
      • 수정: SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift
      • 수정: SodaLive.xcodeproj/project.pbxproj
    • 작업 내용:
      • 여러 탭에서 재사용 가능한 다시듣기 아이템을 CreatorChannel/Components 공용 위치로 이동한다.
      • 타입명을 CreatorChannelLiveReplayListItem에서 CreatorChannelAudioContentListItem으로 변경한다.
      • 라이브 탭 사용처를 CreatorChannelAudioContentListItem으로 변경한다.
    • 검증 기준:
      • 실행 명령: rg "CreatorChannelLiveReplayListItem" SodaLive/Sources/V2/CreatorChannel SodaLive.xcodeproj/project.pbxproj
      • 기대 결과: 검색 결과가 없다.
      • 실행 명령: rg "CreatorChannelAudioContentListItem" SodaLive/Sources/V2/CreatorChannel SodaLive.xcodeproj/project.pbxproj
      • 기대 결과: 공용 컴포넌트, 라이브 탭 사용처, 프로젝트 등록이 확인된다.
  • Task 6.2: 홈 전용 오디오 콘텐츠 아이템 이름 변경

    • 대상 파일:
      • 이동/변경: SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelAudioContentListItem.swift -> SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHomeAudioContentListItem.swift
      • 수정: SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelAudioSection.swift
      • 수정: SodaLive.xcodeproj/project.pbxproj
    • 작업 내용:
      • 홈 탭 전용 아이템 타입명을 CreatorChannelHomeAudioContentListItem으로 변경한다.
      • 기존 홈 탭 사용처를 CreatorChannelHomeAudioContentListItem으로 변경한다.
    • 검증 기준:
      • 실행 명령: rg "CreatorChannelHomeAudioContentListItem" SodaLive/Sources/V2/CreatorChannel SodaLive.xcodeproj/project.pbxproj
      • 기대 결과: 홈 전용 컴포넌트, 홈 섹션 사용처, 프로젝트 등록이 확인된다.
  • Task 6.3: 리팩토링 후 정적/빌드 검증

    • 대상 파일:
      • 확인: SodaLive/Sources/V2/CreatorChannel
      • 확인: SodaLive.xcodeproj/project.pbxproj
    • 작업 내용:
      • rename/move 후 남은 이전 심볼이 없는지 확인한다.
      • 프로젝트 파일 문법과 Debug 빌드를 확인한다.
    • 검증 기준:
      • 실행 명령: plutil -lint SodaLive.xcodeproj/project.pbxproj
      • 기대 결과: OK
      • 실행 명령: xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
      • 기대 결과: ** BUILD SUCCEEDED **

Phase 7: 검증

  • Task 7.1: 정적 검색 검증

    • 대상 파일:
      • 확인: SodaLive/Sources/V2/CreatorChannel
      • 확인: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • API endpoint, query parameter, 정렬 enum, page=0, size=20, sort-bar, BottomSheet, current live 재사용, 다시듣기 item, ic_new_create_live, 오디오 상세 이동 연결을 검색한다.
    • 검증 기준:
      • 실행 명령: rg "CreatorChannelLiveApi|/api/v2/creator-channels/|/live|URLEncoding.queryString|ContentSort|pageSize|20|page = 0|CreatorChannelSortBar|CreatorChannelSortBottomSheet|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|ic_new_create_live|contentDetail\\(contentId:" SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/App/AppStep.swift
      • 기대 결과: PRD 핵심 구현 지점이 모두 검색된다.
  • Task 7.2: 빌드 검증

    • 대상 파일:
      • 확인: SodaLive.xcworkspace
    • 작업 내용:
      • Debug 빌드를 실행한다.
    • 검증 기준:
      • 실행 명령: xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
      • 기대 결과: ** BUILD SUCCEEDED **
  • Task 7.3: 수동 동작 검증 [blocked]

    • 대상 화면:
      • 타인 크리에이터 채널 라이브 탭
      • 본인 크리에이터 채널 라이브 탭
    • 작업 내용:
      • 라이브 탭 진입 시 첫 요청이 page=0, size=20, sort=LATEST로 호출되는지 확인한다.
      • sort-bar에 최신순이 표시되는지 확인한다.
      • sort-bar 터치 시 BottomSheet가 표시되고 4개 정렬만 보이는지 확인한다.
      • 정렬 변경 시 첫 페이지부터 다시 조회되는지 확인한다.
      • currentLive != nil인 경우 홈 탭 현재 라이브 UI와 동일한 카드가 표시되는지 확인한다.
      • 마지막 item 노출 시 hasNext == true이면 page + 1이 호출되고 목록이 append되는지 확인한다.
      • hasNext == false이면 추가 호출이 없는지 확인한다.
      • 다시듣기 item의 무료, 가격, 포인트, 19금, 소장중, 대여중 상태가 조건에 맞게 표시되는지 확인한다.
      • 다시듣기 item tap 시 오디오 상세 화면으로 이동하는지 확인한다.
      • 본인 채널 라이브 탭에서 ic_new_create_live 아이콘이 포함된 라이브 시작하기 버튼이 하단에 표시되는지 확인한다.
      • 타인 채널 또는 라이브 외 탭에서는 라이브 시작하기 버튼이 표시되지 않는지 확인한다.
    • 검증 기준:
      • 기대 결과: PRD Success Criteria를 모두 만족한다.
      • blocked 사유: 실제 로그인 세션, 타인/본인 크리에이터 채널 테스트 데이터, 라이브/다시듣기 응답 데이터, 네트워크 요청 관찰 수단이 필요해 현재 CLI 환경에서는 완료 검증하지 않는다.

검증 기록

  • 2026-07-03: 문서 작성 단계. 구현/빌드 검증은 아직 수행하지 않음.
  • 2026-07-03: Phase 1~3 구현 완료. rg 기반 정적 검증, plutil -lint SodaLive.xcodeproj/project.pbxproj, xcodebuild -workspace "SodaLive.xcworkspace" -list를 확인함.
  • 2026-07-03: xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build는 실행했으나, DerivedData manifest.json 작성 중 디스크 용량 부족(No space left on device, 여유 598Mi)으로 Swift 컴파일 전 실패함.
  • 2026-07-03: Phase 2.3 보완. 첫 페이지/정렬 변경/다음 페이지 조회 모두 성공 시에만 목록 교체 또는 append를 수행하고, 실패 시 기존 목록과 pagination 상태를 유지하도록 PRD/계획/구현을 정렬함.
  • 2026-07-04: Phase 4 구현 완료. CreatorChannelLiveReplayListItem, CreatorChannelLiveStartButton, CreatorChannelLiveTabView를 추가하고 I18n.CreatorChannelLive.startLive 문구를 추가함. 저장소에 별도 XCTest 파일이 없어 Phase 4는 rg 정적 검색, plutil -lint SodaLive.xcodeproj/project.pbxproj, xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build로 검증했으며 ** BUILD SUCCEEDED **를 확인함.
  • 2026-07-04: Phase 6 리팩토링 완료. CreatorChannelLiveReplayListItem을 공용 CreatorChannelAudioContentListItem으로 이동/이름 변경하고, 홈 전용 CreatorChannelAudioContentListItemCreatorChannelHomeAudioContentListItem으로 변경함. rg 정적 검색, plutil -lint SodaLive.xcodeproj/project.pbxproj, xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build로 검증했으며 ** BUILD SUCCEEDED **를 확인함.
  • 2026-07-04: Phase 5 구현 완료. CreatorChannelView.live 탭 분기에 CreatorChannelLiveTabView를 연결하고 creatorId, isOwnCreatorChannel, showLiveDetail, showContentDetail, showCreateLive를 전달함. 기존 CreatorChannelFloatingActionMenu는 본인 채널 home 탭 조건으로 유지함. rg 정적 검색, plutil -lint SodaLive.xcodeproj/project.pbxproj, xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build로 검증했으며 ** BUILD SUCCEEDED **를 확인함.
  • 2026-07-04: ContentSort에서 소장 정렬 옵션을 제거하고 PRD/계획서의 정렬 옵션을 4개 기준으로 갱신함. rg 정적 검색, plutil -lint SodaLive.xcodeproj/project.pbxproj, xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build로 검증했으며 ** BUILD SUCCEEDED **를 확인함.
  • 2026-07-04: Phase 7.1 정적 검색 검증 완료. API endpoint, query parameter, 정렬 enum 4개, pagination 기본값, sort-bar, BottomSheet, 현재 라이브 재사용, 공용 다시듣기 item, ic_new_create_live, 오디오 상세 이동 연결을 확인함. plutil -lint SodaLive.xcodeproj/project.pbxprojOK를 확인함.
  • 2026-07-04: Phase 7.2 빌드 검증 완료. 최초 xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build는 DerivedData build.db lock으로 실패했으나, 5초 후 동일 명령 재시도에서 ** BUILD SUCCEEDED **를 확인함.
  • 2026-07-04: Phase 7.3 수동 동작 검증은 blocked. iOS Simulator는 사용 가능하나 실제 로그인 세션, 타인/본인 크리에이터 채널 테스트 데이터, 라이브/다시듣기 응답 데이터, 네트워크 요청 관찰 수단이 없어 CLI 환경에서 PRD Success Criteria 전체를 완료 검증하지 않음.
  • 2026-07-04: 리뷰 반영. sort-bar 카운트 표시 요구사항이 제거되어 PRD/계획서를 현재 정렬 표시 기준으로 갱신함.
  • 2026-07-04: 리뷰 반영. 본인 채널 라이브 시작하기 CTA를 CreatorChannelLiveTabView 내부 scroll content overlay에서 CreatorChannelView 최상위 overlay로 이동해 화면 하단 고정 구조로 변경함.
  • 2026-07-04: 요구사항 추가 반영. 진행 중인 라이브가 없고 라이브 다시듣기 콘텐츠가 0개인 경우 sort-bar/list를 숨기고 Empty 문구만 표시하도록 PRD/계획/구현을 갱신함.