Files
sodalive-ios/docs/20260704_크리에이터_채널_오디오_탭/plan-task.md

163 lines
12 KiB
Markdown

# 크리에이터 채널 오디오 탭 구현 계획
## 기준 문서
- PRD: `docs/20260704_크리에이터_채널_오디오_탭/prd.md`
- 기존 라이브 탭 PRD: `docs/20260703_크리에이터_채널_라이브_탭/prd.md`
- 기존 라이브 탭 구현 계획: `docs/20260703_크리에이터_채널_라이브_탭/plan-task.md`
- 코드 스타일: `docs/agent-guides/code-style.md`
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
- Figma 전체 화면: `290:9015`, `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-9015&m=dev`
- Figma 소장률: `290:9029`, `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-9029&m=dev`
- Figma Empty: `290:8965`, `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-8965&m=dev`
- Figma 본인 채널 CTA: `665:19008`, `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-19008&m=dev`
## 구현 원칙
- 기존 `CreatorChannelView`의 공통 shell, header, sticky tab-bar, title bar 동작은 유지한다.
- `CreatorChannelTab.audio` 콘텐츠만 신규 `CreatorChannelAudioTabView`로 교체한다.
- `ContentSort`, `CreatorChannelSortBar`, 정렬 context popup, `CapsuleTabBar`, `CreatorChannelAudioContentListItem`을 재사용한다.
- 전체 테마는 API 모델에 섞지 않고 오디오 탭 표시 모델에서만 합성한다.
- `themeId == nil`이면 query parameter를 보내지 않는다.
- `duration == nil` 또는 빈 문자열인 콘텐츠는 ViewModel에서 목록에 포함하지 않는다.
- 본인 채널 CTA는 기존 `showAudioContentUpload()` 흐름을 재사용한다.
## 주요 대상 파일
### 생성
- `SodaLive/Sources/V2/CreatorChannel/Audio/Models/CreatorChannelAudioTabResponse.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioApi.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioRepository.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioViewModel.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioOwnershipRateView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioUploadButton.swift`
### 수정
- `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- `SodaLive/Sources/I18n/I18n.swift`
- 필요 시 `SodaLive.xcodeproj/project.pbxproj`
## TASK 체크리스트
### Phase 1: 문서와 API 모델
- [x] **Task 1.1: PRD와 계획 문서 생성**
- 대상 파일:
- 생성: `docs/20260704_크리에이터_채널_오디오_탭/prd.md`
- 생성: `docs/20260704_크리에이터_채널_오디오_탭/plan-task.md`
- 검증 기준:
- 실행 명령: `rg "290:9015|290:9029|290:8965|665:19008|CreatorChannelAudioTabResponse" docs/20260704_크리에이터_채널_오디오_탭`
- 기대 결과: Figma URL과 응답 모델 요구사항이 문서에 기록된다.
- [x] **Task 1.2: 오디오 탭 Response model 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/Models/CreatorChannelAudioTabResponse.swift`
- 작업 내용:
- `CreatorChannelAudioTabResponse``CreatorChannelAudioThemeResponse``Decodable`로 생성한다.
- `CreatorChannelAudioTabResponse.audioContents`는 기존 `[CreatorChannelAudioContentResponse]`를 사용한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelAudioTabResponse|audioContentCount|paidAudioContentCount|purchasedAudioContentCount|purchasedAudioContentRate|themes|audioContents|themeId: Int\\?|struct CreatorChannelAudioThemeResponse" SodaLive/Sources/V2/CreatorChannel/Audio/Models/CreatorChannelAudioTabResponse.swift`
### Phase 2: API, Repository, ViewModel
- [x] **Task 2.1: 오디오 탭 API endpoint 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioApi.swift`
- 작업 내용:
- `CreatorChannelAudioApi.getAudio(creatorId:page:size:sort:themeId:)`를 만든다.
- path는 `/api/v2/creator-channels/{creatorId}/audio`로 구성한다.
- `themeId`는 nil이 아닐 때만 query에 포함한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioApi|getAudio|/api/v2/creator-channels/|/audio|themeId|URLEncoding.queryString" SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioApi.swift`
- [x] **Task 2.2: 오디오 탭 Repository 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioRepository.swift`
- 작업 내용:
- `MoyaProvider<CreatorChannelAudioApi>`를 사용하는 `getAudio(...)` publisher를 제공한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioRepository|MoyaProvider<CreatorChannelAudioApi>|AnyPublisher<Response, MoyaError>|getAudio" SodaLive/Sources/V2/CreatorChannel/Audio/Repository/CreatorChannelAudioRepository.swift`
- [x] **Task 2.3: 오디오 탭 ViewModel 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioViewModel.swift`
- 작업 내용:
- 상태는 loading, response, filtered `audioContents`, selectedSort, selectedTheme, page, size, hasNext, hasLoaded, error toast를 둔다.
- `displayThemes`에서 서버 `themes` 조회 완료 후 전체 항목을 맨 앞에 합성한다.
- 정렬/테마 변경 시 첫 페이지부터 다시 조회한다.
- `duration`이 없는 콘텐츠는 `visibleAudioContents`에 포함하지 않는다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioViewModel|displayThemes|selectedTheme|fetchFirstPage|fetchNextPageIfNeeded|selectSort|selectTheme|compactMap|duration" SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioViewModel.swift`
### Phase 3: Figma 섹션별 UI
- [x] **Task 3.1: Theme CapsuleTabBar 적용**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 검증 기준:
- 실행 명령: `rg "CapsuleTabBar|displayThemes|selectedTheme" SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- [x] **Task 3.2: 소장률 UI 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioOwnershipRateView.swift`
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioOwnershipRateView|purchasedAudioContentRate|purchasedAudioContentCount|paidAudioContentCount|ProgressView|progress" SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioOwnershipRateView.swift`
- [x] **Task 3.3: 콘텐츠 목록과 empty 구현**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 수정: `SodaLive/Sources/I18n/I18n.swift`
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioContentListItem|audioEmptyMessage|fetchNextPageIfNeeded|isEmptyState" SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift SodaLive/Sources/I18n/I18n.swift`
- [x] **Task 3.4: 본인 채널 CTA 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioUploadButton.swift`
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioUploadButton|ic_new_upload_audio|uploadAudioContent|Capsule|frame\\(maxWidth: \\.infinity" SodaLive/Sources/V2/CreatorChannel/Audio/Components/CreatorChannelAudioUploadButton.swift`
### Phase 4: CreatorChannelView 연결과 검증
- [x] **Task 4.1: 오디오 탭 콘텐츠와 CTA 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 검증 기준:
- 실행 명령: `rg "CreatorChannelAudioTabView|viewModel\\.selectedTab == \\.audio|CreatorChannelAudioUploadButton|showAudioContentUpload" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- [ ] **Task 4.2: 프로젝트 파일 등록과 정적 검증**
- 대상 파일:
- 확인/수정: `SodaLive.xcodeproj/project.pbxproj`
- 검증 기준:
- 실행 명령: `git diff --check -- docs/20260704_크리에이터_채널_오디오_탭 SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: 빌드 성공.
### Phase 5: 공용 sort-bar 후속 변경 반영
- [x] **Task 5.1: 전체 개수와 context popup 재사용 확인**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- 작업 내용:
- `CreatorChannelSortBar``audioContentCount`를 전달한다.
- 정렬 선택은 `.sheet`가 아닌 공용 context popup overlay를 사용한다.
- popup 항목은 기존 `ContentSort.allCases` 4개를 유지한다.
- 검증 기준:
- 실행 명령: `rg "audioContentCount|CreatorChannelSortContextPopup|isSortPopupPresented|\\.sheet\\(isPresented: \\$isSort" SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift SodaLive/Sources/V2/CreatorChannel/Components`
- 기대 결과: 오디오 탭의 count 전달과 popup 사용이 확인되고 정렬 sheet 사용처는 검색되지 않는다.
## 검증 기록
- 2026-07-04: `get_design_context``get_screenshot`으로 Figma `290:9015`, `290:9029`, `290:8965`, `665:19008`을 확인했다.
- 2026-07-04: `rg``sed`로 기존 라이브 탭, 정렬 컴포넌트, `CapsuleTabBar`, 오디오 콘텐츠 item, 본인 채널 CTA 흐름을 확인했다.
- 2026-07-04: `git diff --check -- docs/20260704_크리에이터_채널_오디오_탭 SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj` 실행 결과 출력 없이 성공했다.
- 2026-07-04: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build``SodaLive.xcworkspace`를 workspace로 인식하지 못해 실패했다.
- 2026-07-04: 대체로 `xcodebuild -project "SodaLive.xcodeproj" -scheme "SodaLive-dev" -configuration Debug build`를 샌드박스 밖에서 실행했다. SwiftPM 패키지 해석과 외부 패키지 컴파일은 진행됐으나 기존 외부 모듈 `Bootpay`, `BootpayUI`, `AgoraRtmKit`, `GoogleSignIn`, `YandexMobileAds`를 찾지 못해 앱 타깃 SwiftDriver 단계에서 실패했다.
- 2026-07-04: Figma `290:9034`, `290:9031`을 확인하고 Phase 5를 추가함. 오디오 탭도 공용 sort-bar 전체 개수 표시와 정렬 context popup 전환을 재사용하도록 범위를 갱신함.
- 2026-07-04: Phase 5 구현 완료. `CreatorChannelAudioTabView`에서 `audioContentCount``CreatorChannelSortBar`에 전달하고 정렬 UI를 `.sheet`에서 `CreatorChannelSortContextPopup` overlay로 변경함.
- 2026-07-04: Phase 5 검증 완료. `rg "audioContentCount|CreatorChannelSortContextPopup|isSortPopupPresented|\\.sheet\\(isPresented: \\$isSort" ...`, `git diff --check -- docs/20260703_크리에이터_채널_라이브_탭 docs/20260704_크리에이터_채널_오디오_탭 SodaLive/Sources/V2/CreatorChannel`, `plutil -lint SodaLive.xcodeproj/project.pbxproj`를 확인함. 샌드박스 밖 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`에서 `** BUILD SUCCEEDED **`를 확인함.
- 2026-07-04: 후속 반영. 정렬 popup 파일명을 `CreatorChannelSortContextPopup.swift`로 변경하고 Xcode project 참조와 문서 참조를 갱신함. 이전 정렬 파일명/타입 참조가 남지 않았음을 확인하고, 샌드박스 밖 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`에서 `** BUILD SUCCEEDED **`를 확인함.