docs(creator): 채널 홈 공통 셸 계획을 갱신한다

This commit is contained in:
Yu Sung
2026-07-02 15:13:09 +09:00
parent cc14353fa4
commit d87d761670
2 changed files with 207 additions and 102 deletions

View File

@@ -6,15 +6,20 @@
- 코드 스타일: `docs/agent-guides/code-style.md` - 코드 스타일: `docs/agent-guides/code-style.md`
- 빌드/검증: `docs/agent-guides/build-test-verification.md` - 빌드/검증: `docs/agent-guides/build-test-verification.md`
- Figma 전체 화면: `296:14890` - Figma 전체 화면: `296:14890`
- Figma Unfollow title bar: `296:14287` - Figma Unfollow title bar: `296:14287`, `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=296-14287&m=dev`
- Figma Follow + 알림 설정 title bar: `296:14288` - Figma Follow + 알림 설정 title bar: `296:14288`, `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=296-14288&m=dev`
- Figma Follow + 알림 해제 title bar: `296:14289` - Figma Follow + 알림 해제 title bar: `296:14289`, `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=296-14289&m=dev`
## 구현 원칙 ## 구현 원칙
- API, Response model, Repository, ViewModel을 먼저 구현한 뒤 UI phase를 진행한다. - API, Response model, Repository, ViewModel을 먼저 구현한 뒤 UI phase를 진행한다.
- 신규 크리에이터 채널 홈 구현은 `SodaLive/Sources/V2/CreatorChannel/Home/**` 아래에 둔다. - 크리에이터 채널 진입점은 `CreatorChannelView`로 두고, 최초 선택 tab은 `.home`으로 둔다.
- 크리에이터 채널 홈 내부에서만 쓰는 UI는 `SodaLive/Sources/V2/CreatorChannel/Home/Components/**` 아래에 둔다. - `CreatorChannelView`는 title bar, 크리에이터 프로필 header, tab-bar, 선택된 tab 상태, 홈 API 호출 상태를 소유한다.
- `CreatorChannelHomeView`는 tab-bar 아래 홈 탭 콘텐츠만 렌더링한다.
- 홈 API 응답의 `creator` 정보는 공통 shell의 header/title/follow/notification 상태와 홈 탭의 대화하기 action 상태가 공유한다.
- 크리에이터 채널 공통 shell 구현은 `SodaLive/Sources/V2/CreatorChannel/**` 아래에 둔다.
- 크리에이터 채널 홈 탭 내부에서만 쓰는 UI는 `SodaLive/Sources/V2/CreatorChannel/Home/Components/**` 아래에 둔다.
- 크리에이터 채널 공통 shell 내부에서만 쓰는 UI는 `SodaLive/Sources/V2/CreatorChannel/Components/**` 아래에 둔다.
- 여러 화면에서 재사용할 근거가 명확한 UI만 `SodaLive/Sources/V2/Component/**`로 올린다. - 여러 화면에서 재사용할 근거가 명확한 UI만 `SodaLive/Sources/V2/Component/**`로 올린다.
- 기존 V2 홈의 `TargetType`, Repository, ViewModel, `ApiResponse<T>` 디코딩 패턴을 따른다. - 기존 V2 홈의 `TargetType`, Repository, ViewModel, `ApiResponse<T>` 디코딩 패턴을 따른다.
- `Long` 계열 id/count는 Swift `Int`로 통일한다. - `Long` 계열 id/count는 Swift `Int`로 통일한다.
@@ -23,24 +28,26 @@
- `화보` 탭, 화보 섹션, 활동의 화보 row는 구현하지 않는다. - `화보` 탭, 화보 섹션, 활동의 화보 row는 구현하지 않는다.
- DM 버튼과 DM 진입은 구현하지 않는다. - DM 버튼과 DM 진입은 구현하지 않는다.
- Figma localhost asset URL은 앱 코드에 사용하지 않는다. - Figma localhost asset URL은 앱 코드에 사용하지 않는다.
- title bar 아이콘은 `ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill` asset을 사용한다.
- SNS 아이콘은 `ic_sns_instagram`, `ic_sns_youtube`, `ic_sns_x`, `ic_sns_kakao`, `ic_sns_fancimm` asset을 사용한다. - SNS 아이콘은 `ic_sns_instagram`, `ic_sns_youtube`, `ic_sns_x`, `ic_sns_kakao`, `ic_sns_fancimm` asset을 사용한다.
- 기존 `AppStep.creatorDetail(userId:)` 진입은 새 크리에이터 채널 화면으로 연결한다. - 기존 `AppStep.creatorDetail(userId:)` 진입은 새 크리에이터 채널 화면으로 연결한다.
## 주요 대상 파일 ## 주요 대상 파일
### 생성 ### 생성
- `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeTab.swift` - `SodaLive/Sources/V2/CreatorChannel/Models/CreatorChannelTab.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeApi.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeApi.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Repository/CreatorChannelHomeRepository.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeViewModel.swift` - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift`
- `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift` - `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHeaderSection.swift` - `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelPlaceholderTabView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTalkActionSection.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTalkActionSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelLatestAudioSection.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelLatestAudioSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelDonationSection.swift` - `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelDonationSection.swift`
@@ -92,7 +99,7 @@
- 실행 명령: `rg "struct CreatorChannelHomeResponse|struct CreatorChannelCreatorResponse|struct CreatorChannelLiveResponse|struct CreatorChannelAudioContentResponse|struct CreatorChannelDonationResponse|struct CreatorChannelScheduleResponse|struct CreatorChannelSeriesResponse|struct CreatorChannelCommunityPostResponse|struct CreatorChannelFanTalkSummaryResponse|struct CreatorChannelActivityResponse|struct CreatorChannelSnsResponse" SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift` - 실행 명령: `rg "struct CreatorChannelHomeResponse|struct CreatorChannelCreatorResponse|struct CreatorChannelLiveResponse|struct CreatorChannelAudioContentResponse|struct CreatorChannelDonationResponse|struct CreatorChannelScheduleResponse|struct CreatorChannelSeriesResponse|struct CreatorChannelCommunityPostResponse|struct CreatorChannelFanTalkSummaryResponse|struct CreatorChannelActivityResponse|struct CreatorChannelSnsResponse" SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift`
- 기대 결과: PRD의 응답 모델이 Swift 파일에 모두 존재하고 id/count 타입이 `Int`이다. - 기대 결과: PRD의 응답 모델이 Swift 파일에 모두 존재하고 id/count 타입이 `Int`이다.
- [x] **Task 1.2: tab enum 생성** - [x] **Task 1.2: 채널 tab enum 생성**
- 대상 파일: - 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeTab.swift` - 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeTab.swift`
- 작업 내용: - 작업 내용:
@@ -103,6 +110,8 @@
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "enum CreatorChannelHomeTab|case home|case live|case audio|case series|case community|case fanTalk|case donation|화보|photo" SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeTab.swift` - 실행 명령: `rg "enum CreatorChannelHomeTab|case home|case live|case audio|case series|case community|case fanTalk|case donation|화보|photo" SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeTab.swift`
- 기대 결과: 7개 tab case가 있고 `화보`/`photo` case가 없다. - 기대 결과: 7개 tab case가 있고 `화보`/`photo` case가 없다.
- 후속 구조 변경:
- tab enum은 홈 탭 전용이 아니라 채널 공통 shell 상태이므로 Task 2.4에서 `CreatorChannelTab`으로 이름과 위치를 정리한다.
- [x] **Task 1.3: API endpoint 생성** - [x] **Task 1.3: API endpoint 생성**
- 대상 파일: - 대상 파일:
@@ -141,11 +150,13 @@
- `fetchHome(creatorId:)`에서 `ApiResponse<CreatorChannelHomeResponse>`를 디코딩한다. - `fetchHome(creatorId:)`에서 `ApiResponse<CreatorChannelHomeResponse>`를 디코딩한다.
- 성공 시 `response`, `selectedTab = .home`, `isApiFailedPlaceholderVisible = false`를 갱신한다. - 성공 시 `response`, `selectedTab = .home`, `isApiFailedPlaceholderVisible = false`를 갱신한다.
- 실패/디코딩 실패 시 `response = nil`, `isApiFailedPlaceholderVisible = true`, `hasLoaded = true`로 두어 기본 shell은 유지할 수 있게 한다. - 실패/디코딩 실패 시 `response = nil`, `isApiFailedPlaceholderVisible = true`, `hasLoaded = true`로 두어 기본 shell은 유지할 수 있게 한다.
- `follow(creatorId:notify:)`, `unfollow(creatorId:)` 기존 `UserRepository.creatorFollow`, `UserRepository.creatorUnFollow` 패턴을 재사용한다. - 초기 구현에서는 `follow(creatorId:notify:)`, `unfollow(creatorId:)` 기존 `UserRepository.creatorFollow`, `UserRepository.creatorUnFollow` 패턴을 재사용한다.
- mutation 성공 시 `response?.creator.isFollow`, `response?.creator.isNotify`가 갱신될 수 있도록 모델 갱신 방식을 구현한다. struct 불변성 때문에 필요한 경우 creator 복사 helper를 둔다. - mutation 성공 시 `response?.creator.isFollow`, `response?.creator.isNotify`가 갱신될 수 있도록 모델 갱신 방식을 구현한다. struct 불변성 때문에 필요한 경우 creator 복사 helper를 둔다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "final class CreatorChannelHomeViewModel|fetchHome\\(creatorId: Int\\)|ApiResponse<CreatorChannelHomeResponse>|isApiFailedPlaceholderVisible|creatorFollow|creatorUnFollow|selectedTab|hasLoaded" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeViewModel.swift` - 실행 명령: `rg "final class CreatorChannelHomeViewModel|fetchHome\\(creatorId: Int\\)|ApiResponse<CreatorChannelHomeResponse>|isApiFailedPlaceholderVisible|creatorFollow|creatorUnFollow|selectedTab|hasLoaded" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeViewModel.swift`
- 기대 결과: 홈 API 디코딩, API 실패 placeholder 상태, 기존 follow/unfollow API 호출이 확인된다. - 기대 결과: 홈 API 디코딩, API 실패 placeholder 상태, 기존 follow/unfollow API 호출이 확인된다.
- 후속 구조 변경:
- title bar mutation은 기존 `UserProfile` 현재 동작과 동일하게 Task 2.4에서 `CreatorChannelViewModel``creatorFollow(follow:notify:)` 기반으로 정리한다.
### Phase 2: 라우팅과 기본 화면 shell ### Phase 2: 라우팅과 기본 화면 shell
@@ -158,10 +169,12 @@
- `AppStep.creatorDetail(userId:)` case는 유지한다. - `AppStep.creatorDetail(userId:)` case는 유지한다.
- `ContentView`에서 `.creatorDetail(let userId)` destination을 `CreatorChannelHomeView(creatorId: userId)`로 변경한다. - `ContentView`에서 `.creatorDetail(let userId)` destination을 `CreatorChannelHomeView(creatorId: userId)`로 변경한다.
- 기존 `UserProfileView`를 삭제하거나 수정하지 않는다. - 기존 `UserProfileView`를 삭제하거나 수정하지 않는다.
- `CreatorChannelHomeView` `creatorId: Int`를 받아 기본 shell을 렌더링한다. - 초기 구현에서는 `CreatorChannelHomeView` `creatorId: Int`를 받아 화면을 구성한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "case \\.creatorDetail|CreatorChannelHomeView\\(creatorId: userId\\)|UserProfileView\\(userId:" SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "case \\.creatorDetail|CreatorChannelHomeView\\(creatorId: userId\\)|UserProfileView\\(userId:" SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: `.creatorDetail`이 신규 `CreatorChannelHomeView`로 연결되고 기존 `UserProfileView` 파일은 유지된다. - 기대 결과: `.creatorDetail`이 신규 `CreatorChannelHomeView`로 연결되고 기존 `UserProfileView` 파일은 유지된다.
- 후속 구조 변경:
- 크리에이터 채널 공통 shell 분리 결정에 따라 Task 2.4에서 `.creatorDetail` destination을 `CreatorChannelView(creatorId:)`로 변경한다.
- [x] **Task 2.2: 기본 shell과 로딩/API 실패 placeholder 구성** - [x] **Task 2.2: 기본 shell과 로딩/API 실패 placeholder 구성**
- 대상 파일: - 대상 파일:
@@ -177,6 +190,8 @@
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "@StateObject private var viewModel|fetchHome\\(creatorId: creatorId\\)|isApiFailedPlaceholderVisible|CreatorChannelPlaceholderTabView|LoadingView|Color.black" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift` - 실행 명령: `rg "@StateObject private var viewModel|fetchHome\\(creatorId: creatorId\\)|isApiFailedPlaceholderVisible|CreatorChannelPlaceholderTabView|LoadingView|Color.black" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift`
- 기대 결과: API 호출, 실패 placeholder, 홈 외 tab placeholder shell이 확인된다. - 기대 결과: API 호출, 실패 placeholder, 홈 외 tab placeholder shell이 확인된다.
- 후속 구조 변경:
- 공통 shell과 선택 tab/API 상태는 Task 2.4에서 `CreatorChannelView`/`CreatorChannelViewModel`로 이동하고, `CreatorChannelHomeView`는 홈 탭 콘텐츠만 담당한다.
- [x] **Task 2.3: I18n 문구 추가** - [x] **Task 2.3: I18n 문구 추가**
- 대상 파일: - 대상 파일:
@@ -191,89 +206,128 @@
- 실행 명령: `rg "enum CreatorChannelHome|currentLive|latestAudio|fanTalk|introduce|activity|viewAll|liveDurationHours|seriesCount" SodaLive/Sources/I18n/I18n.swift` - 실행 명령: `rg "enum CreatorChannelHome|currentLive|latestAudio|fanTalk|introduce|activity|viewAll|liveDurationHours|seriesCount" SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: 신규 사용자 노출 문구가 `I18n`에 존재한다. - 기대 결과: 신규 사용자 노출 문구가 `I18n`에 존재한다.
- [ ] **Task 2.4: 공통 CreatorChannel shell로 구조 정리**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelViewModel.swift`
- 생성/이동: `SodaLive/Sources/V2/CreatorChannel/Models/CreatorChannelTab.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 수정/정리: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeViewModel.swift`
- 수정: `SodaLive/Sources/ContentView.swift`
- 확인: `SodaLive/Sources/App/AppStep.swift`
- 작업 내용:
- `ContentView``.creatorDetail(let userId)` destination을 `CreatorChannelView(creatorId: userId)`로 연결한다.
- `CreatorChannelView``@StateObject private var viewModel = CreatorChannelViewModel()`을 사용한다.
- `CreatorChannelViewModel``isLoading`, `response`, `hasLoaded`, `selectedTab`, `isApiFailedPlaceholderVisible`, follow/notify mutation을 소유한다.
- 기존 `CreatorChannelHomeViewModel`의 홈 API 호출/공통 상태 책임은 `CreatorChannelViewModel`로 이동하고, 남는 책임이 없으면 파일을 제거한다.
- title bar의 팔로우/팔로우 취소/알림 설정/알림 설정 취소는 기존 `UserProfile` 현재 UI 흐름과 동일하게 `UserRepository.creatorFollow(creatorId:follow:notify:)`를 사용한다.
- 팔로우는 `creatorFollow(follow: true, notify: true)`, 팔로우 취소는 `creatorFollow(follow: false, notify: false)`로 호출한다.
- `ic_bar_bell` 터치 시 `creatorFollow(follow: true, notify: true)`, `ic_bar_bell_fill` 터치 시 `creatorFollow(follow: true, notify: false)`로 호출한다.
- `CreatorChannelHomeTab``CreatorChannelTab`으로 이름을 바꾸고 공통 shell 경로로 이동한다.
- `CreatorChannelHomeView``CreatorChannelHomeResponse`와 tab 전환 closure를 주입받아 tab-bar 아래 홈 탭 콘텐츠만 렌더링한다.
- API 실패 placeholder 상태에서는 `CreatorChannelView`가 서버 데이터 없이 가능한 title bar, tab-bar, 선택된 tab placeholder를 유지한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelView|final class CreatorChannelViewModel|enum CreatorChannelTab|CreatorChannelView\\(creatorId: userId\\)|CreatorChannelHomeView\\(|selectedTab|fetchHome\\(creatorId:|creatorFollow\\(.*follow: true, notify: true|creatorFollow\\(.*follow: false, notify: false|creatorFollow\\(.*follow: true, notify: false" SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/ContentView.swift`
- 기대 결과: 라우팅 진입점과 선택 tab/API 상태가 `CreatorChannelView`/`CreatorChannelViewModel`로 이동했고, title bar follow/notify mutation이 기존 `UserProfile``creatorFollow(follow:notify:)` 기준으로 연결되어 있으며, `CreatorChannelHomeView`는 홈 탭 콘텐츠 렌더링만 담당한다.
### Phase 3: Header / Title Bar / Tab Bar / Sticky ### Phase 3: Header / Title Bar / Tab Bar / Sticky
- [ ] **Task 3.1: title bar 컴포넌트 생성** - [ ] **Task 3.1: title bar 컴포넌트 생성**
- 대상 파일: - 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift` - 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`
- 확인: `SodaLive/Sources/Common/FollowButtonImageAsset.swift` - 확인: `SodaLive/Sources/Common/FollowButtonImageAsset.swift`
- 작업 내용: - 작업 내용:
- back 버튼, 팔로우/팔로잉 상태 버튼, 알림 상태 아이콘, 더보기 버튼을 표시한다. - back 버튼, 팔로우/팔로잉 상태 버튼, 알림 상태 아이콘, 더보기 버튼을 표시한다.
- back 버튼은 `ic_new_bar_back` asset을 사용한다.
- 더보기 버튼은 `ic_new_more` asset을 사용한다.
- `isFollow == false`이면 `팔로우` capsule을 표시한다. - `isFollow == false`이면 `팔로우` capsule을 표시한다.
- `isFollow == true && isNotify == true`이면 팔로잉 상태 capsule과 알림 설정 아이콘을 표시한다. - `isFollow == false` 상태의 follow capsule에는 `ic_new_follow` asset을 사용한다.
- `isFollow == true && isNotify == false`이면 팔로잉 상태 capsule과 알림 해제 아이콘을 표시한다. - `isFollow == true` 상태의 following capsule에는 `ic_new_following` asset을 사용한다.
- `isFollow == true && isNotify == true`이면 팔로잉 상태 capsule과 알림 설정 아이콘 `ic_bar_bell_fill`을 표시한다.
- `isFollow == true && isNotify == false`이면 팔로잉 상태 capsule과 알림 해제 아이콘 `ic_bar_bell`을 표시한다.
- `ic_new_follow` 터치 시 부모의 팔로우 action으로 `creatorFollow(follow: true, notify: true)`가 호출되도록 연결한다.
- `ic_new_following` 터치 시 부모의 팔로우 취소 action으로 `creatorFollow(follow: false, notify: false)`가 호출되도록 연결한다.
- `ic_bar_bell` 터치 시 부모의 알림 설정 action으로 `creatorFollow(follow: true, notify: true)`가 호출되도록 연결한다.
- `ic_bar_bell_fill` 터치 시 부모의 알림 설정 취소 action으로 `creatorFollow(follow: true, notify: false)`가 호출되도록 연결한다.
- 더보기 버튼은 항상 표시한다. - 더보기 버튼은 항상 표시한다.
- 배경 opacity는 외부에서 전달받는 `backgroundProgress`로 제어한다. - 배경 opacity는 외부에서 전달받는 `backgroundProgress`로 제어한다.
- 구현 기준은 Figma `296:14287`, `296:14288`, `296:14289` 3개 title bar 상태를 따른다.
- title bar 아이콘은 `ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill` 외 다른 asset으로 대체하지 않는다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelTitleBar|isFollow|isNotify|backgroundProgress|onTapBack|onTapFollow|onTapUnfollow|onTapMore|팔로우" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift` - 실행 명령: `rg "struct CreatorChannelTitleBar|isFollow|isNotify|backgroundProgress|onTapBack|onTapFollow|onTapUnfollow|onTapNotify|onTapUnnotify|onTapMore|ic_new_bar_back|ic_new_follow|ic_new_following|ic_new_more|ic_bar_bell|ic_bar_bell_fill|팔로우" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`
- 기대 결과: Figma 3개 title bar 상태를 표현하는 조건 분기가 확인된다. - 기대 결과: Figma 3개 title bar 상태를 표현하는 조건 분기, 지정된 6개 icon asset 사용, 팔로우/팔로우 취소/알림 설정/알림 설정 취소 tap closure가 확인된다.
- [ ] **Task 3.2: 크리에이터 이미지/프로필 헤더 생성** - [ ] **Task 3.2: 크리에이터 이미지/프로필 헤더 생성**
- 대상 파일: - 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHeaderSection.swift` - 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 작업 내용: - 작업 내용:
- `creator.profileImageUrl`을 큰 배경 이미지와 프로필 이미지에 동일하게 사용한다. - HeaderView의 표시 내용은 `CreatorChannelHomeResponse.creator`를 사용해 채운다.
- 이미지 영역은 OS status bar까지 확장되도록 top safe area를 무시한다. - 별도 원형/소형 프로필 이미지는 표시하지 않는다.
- `creator.profileImageUrl`은 큰 배경 이미지에만 사용한다.
- 큰 배경 이미지 영역은 OS status bar까지 확장되도록 top safe area를 무시한다.
- `creator.nickname`, `creator.followerCount`를 표시한다. - `creator.nickname`, `creator.followerCount`를 표시한다.
- 이미지 로드 실패 시 기존 `DownsampledKFImage` 또는 `KFImage` placeholder 관례를 따른다. - 이미지 로드 실패 시 기존 `DownsampledKFImage` 또는 `KFImage` placeholder 관례를 따른다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelHeaderSection|profileImageUrl|ignoresSafeArea\\(.*top|followerCount|DownsampledKFImage|KFImage" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHeaderSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelHeaderSection|CreatorChannelCreatorResponse|profileImageUrl|ignoresSafeArea\\(.*top|nickname|followerCount|DownsampledKFImage|KFImage" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: profile image가 헤더 큰 이미지로 쓰이고 top safe area 확장이 확인된다. - 기대 결과: `CreatorChannelHomeResponse.creator` 기반 데이터 표시, 큰 배경 이미지의 top safe area 확장, 별도 프로필 이미지 미표시가 확인된다.
- [ ] **Task 3.3: horizontal tab-bar 생성** - [ ] **Task 3.3: horizontal tab-bar 생성**
- 대상 파일: - 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift` - 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 확인: `SodaLive/Sources/V2/Component/TextTabBar.swift` - 확인: `SodaLive/Sources/V2/Component/TextTabBar.swift`
- 작업 내용: - 작업 내용:
- `CreatorChannelHomeTab.allCases`를 horizontal scroll로 표시한다. - `CreatorChannelTab.allCases`를 horizontal scroll로 표시한다.
- 선택된 tab은 white text와 soda color bottom indicator로 표시한다. - 선택된 tab은 white text와 soda color bottom indicator로 표시한다.
- 비선택 tab은 gray text로 표시한다. - 비선택 tab은 gray text로 표시한다.
- `화보` tab은 표시하지 않는다. - `화보` tab은 표시하지 않는다.
- sticky 상태일 때도 같은 컴포넌트를 사용한다. - sticky 상태일 때도 같은 컴포넌트를 사용한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelTabBar|ScrollView\\(\\.horizontal|CreatorChannelHomeTab.allCases|selectedTab|soda|indicator|화보" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelTabBar|ScrollView\\(\\.horizontal|CreatorChannelTab.allCases|selectedTab|soda|indicator|화보" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: 7개 tab만 표시되고 선택 상태 UI가 확인된다. - 기대 결과: 7개 tab만 표시되고 선택 상태 UI가 확인된다.
- [ ] **Task 3.4: scroll progress와 sticky 계산 연결** - [ ] **Task 3.4: scroll progress와 sticky 계산 연결**
- 대상 파일: - 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift`
- 작업 내용: - 작업 내용:
- scroll offset 또는 geometry preference로 tab-bar와 title bar의 거리를 계산한다. - `GeometryReader``PreferenceKey` 기반 scroll offset 또는 geometry preference로 tab-bar와 title bar의 거리를 계산한다.
- 거리에 따라 title bar background progress를 0...1로 clamp한다. - 거리에 따라 title bar background progress를 0...1로 clamp한다.
- title bar는 header view 위에 `ZStack` 또는 `overlay` 형태로 띄운다.
- title bar 뒤로 큰 배경 이미지가 보이도록 기본 `backgroundProgress`는 0, 기본 배경은 투명으로 둔다.
- tab-bar가 title bar에 닿으면 tab-bar를 sticky 상태로 고정한다. - tab-bar가 title bar에 닿으면 tab-bar를 sticky 상태로 고정한다.
- sticky 이후 OS status bar 영역이 title bar와 동일한 black 배경으로 보이게 상단 overlay를 둔다. - sticky 이후 OS status bar 영역이 title bar와 동일한 black 배경으로 보이게 상단 overlay를 둔다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "PreferenceKey|GeometryReader|backgroundProgress|isTabBarSticky|clamp|safeAreaInsets|CreatorChannelTitleBar|CreatorChannelTabBar" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift` - 실행 명령: `rg "PreferenceKey|GeometryReader|ZStack|overlay|backgroundProgress|Color\\.clear|opacity|isTabBarSticky|clamp|safeAreaInsets|CreatorChannelTitleBar|CreatorChannelTabBar" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.swift SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift`
- 기대 결과: scroll 기반 title bar opacity sticky tab-bar 상태 계산이 확인된다. - 기대 결과: geometry preference 기반 계산, 기본 투명 title bar overlay, scroll 기반 title bar opacity, sticky tab-bar 상태 계산이 확인된다.
- [ ] **Task 3.5: Header/TitleBar/TabBar만으로 sticky 조기 검증** - [ ] **Task 3.5: Header/TitleBar/TabBar만으로 sticky 조기 검증**
- 대상 파일: - 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 작업 내용: - 작업 내용:
- `#if DEBUG` preview 전용 sticky scaffold를 `CreatorChannelHomeView_Previews` 또는 같은 파일의 private preview view로 둔다. - `#if DEBUG` preview 전용 sticky scaffold를 `CreatorChannelView_Previews` 또는 같은 파일의 private preview view로 둔다.
- preview scaffold는 sample creator, `CreatorChannelHeaderSection`, `CreatorChannelTitleBar`, `CreatorChannelTabBar`, 충분한 높이의 dummy rows를 사용한다. - preview scaffold는 sample creator, 큰 배경 이미지 기반 `CreatorChannelHeaderSection`, 기본 투명 배경의 `CreatorChannelTitleBar`, `CreatorChannelTabBar`, 충분한 높이의 dummy rows를 사용한다.
- preview에서 초기 상태의 title bar 뒤로 header 큰 배경 이미지가 보이는지 확인한다.
- production runtime에는 preview 전용 dummy rows가 노출되지 않게 `#if DEBUG` 범위에만 둔다. - production runtime에는 preview 전용 dummy rows가 노출되지 않게 `#if DEBUG` 범위에만 둔다.
- Xcode Preview 또는 simulator preview route에서 스크롤하며 title bar black 전환 tab-bar sticky 고정 수동 확인한다. - Xcode Preview 또는 simulator preview route에서 스크롤하며 title bar black 전환, tab-bar sticky 고정, title bar/header overlay 상태를 수동 확인한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "#if DEBUG|CreatorChannelHomeView_Previews|CreatorChannelHeaderSection|CreatorChannelTitleBar|CreatorChannelTabBar|dummy|isTabBarSticky|backgroundProgress" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "#if DEBUG|CreatorChannelView_Previews|CreatorChannelHeaderSection|CreatorChannelTitleBar|CreatorChannelTabBar|dummy|isTabBarSticky|backgroundProgress|Color\\.clear|ZStack|overlay" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: API와 하위 섹션 구현 전에도 Header/TitleBar/TabBar sticky 동작 확인할 수 있는 debug-only preview scaffold가 존재한다. - 기대 결과: API와 하위 섹션 구현 전에도 Header/TitleBar/TabBar sticky 동작과 title bar/header overlay 상태를 확인할 수 있는 debug-only preview scaffold가 존재한다.
### Phase 4: 홈 외 placeholder ### Phase 4: 홈 외 placeholder
- [ ] **Task 4.1: 홈 외 tab placeholder 연결** - [ ] **Task 4.1: 홈 외 tab placeholder 연결**
- 대상 파일: - 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift` - 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelPlaceholderTabView.swift`
- 작업 내용: - 작업 내용:
- `selectedTab != .home`이면 실제 목록 대신 `CreatorChannelPlaceholderTabView(title: selectedTab.title)`을 표시한다. - `selectedTab != .home`이면 실제 목록 대신 `CreatorChannelPlaceholderTabView(title: selectedTab.title)`을 표시한다.
- 홈 섹션의 전체보기 action 중 별도 기존 route가 없는 경우 대응 tab을 선택하도록 연결한다. - 홈 섹션의 전체보기 action 중 별도 기존 route가 없는 경우 대응 tab을 선택하도록 연결한다.
- 팬Talk 전체보기는 `selectedTab = .fanTalk`로 이동한다. - 팬Talk 전체보기는 `selectedTab = .fanTalk`로 이동한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "selectedTab != \\.home|CreatorChannelPlaceholderTabView\\(title: selectedTab.title\\)|selectedTab = \\.fanTalk|selectedTab = \\.audio|selectedTab = \\.series|selectedTab = \\.community|selectedTab = \\.donation" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "selectedTab != \\.home|CreatorChannelPlaceholderTabView\\(title: selectedTab.title\\)|selectedTab = \\.fanTalk|selectedTab = \\.audio|selectedTab = \\.series|selectedTab = \\.community|selectedTab = \\.donation" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: 홈 외 tab과 전체보기 placeholder 전환이 확인된다. - 기대 결과: 홈 외 tab과 전체보기 placeholder 전환이 확인된다.
### Phase 5: 현재 라이브 섹션 ### Phase 5: 현재 라이브 섹션
@@ -368,10 +422,10 @@
- `audioContents.isEmpty`이면 섹션을 표시하지 않는다. - `audioContents.isEmpty`이면 섹션을 표시하지 않는다.
- Figma 홈 노출 개수만 표시한다. - Figma 홈 노출 개수만 표시한다.
- `AudioContentCard`와 채널 홈 전용 thumbnail wrapper를 재사용한다. - `AudioContentCard`와 채널 홈 전용 thumbnail wrapper를 재사용한다.
- 전체보기는 `selectedTab = .audio`로 연결한다. - 전체보기는 부모에서 주입받은 `onSelectTab(.audio)` closure로 연결한다.
- item tap은 기존 `AppStep.contentDetail(contentId:)` 흐름으로 연결한다. - item tap은 기존 `AppStep.contentDetail(contentId:)` 흐름으로 연결한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelAudioSection|audioContents|AudioContentCard|prefix|selectedTab = \\.audio|contentDetail" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelAudioSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelAudioSection|audioContents|AudioContentCard|prefix|onSelectTab|\\.audio|contentDetail" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelAudioSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: 빈 배열 숨김, 홈 노출 개수 제한, 오디오 tab 전체보기 연결이 확인된다. - 기대 결과: 빈 배열 숨김, 홈 노출 개수 제한, 오디오 tab 전체보기 연결이 확인된다.
### Phase 11: 시리즈 섹션 ### Phase 11: 시리즈 섹션
@@ -383,10 +437,10 @@
- 작업 내용: - 작업 내용:
- `series.isEmpty`이면 섹션을 표시하지 않는다. - `series.isEmpty`이면 섹션을 표시하지 않는다.
- 각 item의 `seriesId`, `title`, `coverImageUrl`, `numberOfContent`, `isNew`, `isOriginal`을 표시한다. - 각 item의 `seriesId`, `title`, `coverImageUrl`, `numberOfContent`, `isNew`, `isOriginal`을 표시한다.
- 전체보기는 `selectedTab = .series`로 연결한다. - 전체보기는 부모에서 주입받은 `onSelectTab(.series)` closure로 연결한다.
- item tap은 기존 `AppStep.seriesDetail(seriesId:)` 흐름으로 연결한다. - item tap은 기존 `AppStep.seriesDetail(seriesId:)` 흐름으로 연결한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelSeriesSection|seriesId|coverImageUrl|numberOfContent|isNew|isOriginal|selectedTab = \\.series|seriesDetail" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelSeriesSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelSeriesSection|seriesId|coverImageUrl|numberOfContent|isNew|isOriginal|onSelectTab|\\.series|seriesDetail" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelSeriesSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: 시리즈 카드, 전체보기, 상세 진입이 확인된다. - 기대 결과: 시리즈 카드, 전체보기, 상세 진입이 확인된다.
### Phase 12: 커뮤니티 섹션 ### Phase 12: 커뮤니티 섹션
@@ -400,9 +454,9 @@
- `communities.isEmpty`이면 섹션을 표시하지 않는다. - `communities.isEmpty`이면 섹션을 표시하지 않는다.
- `CommunityPostCard`가 요구사항과 맞으면 재사용한다. - `CommunityPostCard`가 요구사항과 맞으면 재사용한다.
- 유료/잠금 게시물은 기존 `price`, `existOrdered` 처리 패턴을 따른다. - 유료/잠금 게시물은 기존 `price`, `existOrdered` 처리 패턴을 따른다.
- 전체보기는 `selectedTab = .community`로 연결한다. - 전체보기는 부모에서 주입받은 `onSelectTab(.community)` closure로 연결한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelCommunitySection|communities|CommunityPostCard|existOrdered|onTapPurchase|selectedTab = \\.community" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCommunitySection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelCommunitySection|communities|CommunityPostCard|existOrdered|onTapPurchase|onSelectTab|\\.community" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCommunitySection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: 커뮤니티 섹션과 전체보기 tab 전환이 확인된다. - 기대 결과: 커뮤니티 섹션과 전체보기 tab 전환이 확인된다.
### Phase 13: 팬Talk 섹션 ### Phase 13: 팬Talk 섹션
@@ -414,9 +468,9 @@
- 작업 내용: - 작업 내용:
- `fanTalk.latestFanTalk == nil`이면 섹션을 표시하지 않는다. - `fanTalk.latestFanTalk == nil`이면 섹션을 표시하지 않는다.
- `totalCount`, `nickname`, `profileImageUrl`, `content`, `languageCode`, `createdAtUtc`를 표시한다. - `totalCount`, `nickname`, `profileImageUrl`, `content`, `languageCode`, `createdAtUtc`를 표시한다.
- 전체보기는 `selectedTab = .fanTalk`로 연결한다. - 전체보기는 부모에서 주입받은 `onSelectTab(.fanTalk)` closure로 연결한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelFanTalkSection|latestFanTalk|totalCount|languageCode|createdAtUtc|selectedTab = \\.fanTalk" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelFanTalkSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "struct CreatorChannelFanTalkSection|latestFanTalk|totalCount|languageCode|createdAtUtc|onSelectTab|\\.fanTalk" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelFanTalkSection.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: 최신 팬Talk가 있을 때만 표시되고 전체보기는 팬Talk tab으로 이동한다. - 기대 결과: 최신 팬Talk가 있을 때만 표시되고 전체보기는 팬Talk tab으로 이동한다.
### Phase 14: 소개 섹션 ### Phase 14: 소개 섹션
@@ -476,38 +530,45 @@
- 버튼은 영역 중앙에 배치한다. - 버튼은 영역 중앙에 배치한다.
- 버튼이 없어도 영역 높이와 vertical spacing을 유지한다. - 버튼이 없어도 영역 높이와 vertical spacing을 유지한다.
- `isDmAvailable``ic_new_dm`은 이번 UI에 사용하지 않는다. - `isDmAvailable``ic_new_dm`은 이번 UI에 사용하지 않는다.
- 버튼 tap은 부모에서 주입받은 `onTapTalk` closure로 전달한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "struct CreatorChannelTalkActionSection|isAiChatAvailable|ic_new_talk|ic_new_dm|frame\\(height|onTapTalk" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTalkActionSection.swift` - 실행 명령: `rg "struct CreatorChannelTalkActionSection|isAiChatAvailable|ic_new_talk|ic_new_dm|frame\\(height|onTapTalk" SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTalkActionSection.swift`
- 기대 결과: 대화하기만 조건부 표시되고 DM 버튼 구현이 없다. - 기대 결과: 대화하기만 조건부 표시되고 DM 버튼 구현이 없다.
- [ ] **Task 17.2: 기존 AI 채팅방 진입 guard 연결** - [ ] **Task 17.2: 기존 AI 채팅방 진입 guard 연결**
- 대상 파일: - 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 확인: `SodaLive/Sources/Chat/ChatTabView.swift` - 확인: `SodaLive/Sources/Chat/ChatTabView.swift`
- 확인: `SodaLive/Sources/App/AppStep.swift` - 확인: `SodaLive/Sources/App/AppStep.swift`
- 작업 내용: - 작업 내용:
- `CreatorChannelView``CreatorChannelHomeView``onTapTalk` closure를 주입한다.
- token이 없으면 `.login`으로 이동한다. - token이 없으면 `.login`으로 이동한다.
- 국가 코드를 trim/uppercase 처리하고, 비어 있거나 `KR`이면 한국으로 취급한다. - 국가 코드를 trim/uppercase 처리하고, 비어 있거나 `KR`이면 한국으로 취급한다.
- 한국 사용자는 `auth == false`일 때 기존 본인인증 confirm/fullScreenCover 흐름을 재사용하거나 같은 조건으로 연결한다. - 한국 사용자는 `auth == false`일 때 기존 본인인증 confirm/fullScreenCover 흐름을 재사용하거나 같은 조건으로 연결한다.
- 비한국 사용자는 기존 `ChatTabView`의 비한국 분기와 동일하게 `auth` 조건 비교를 적용한다. - 비한국 사용자는 기존 `ChatTabView`의 비한국 분기와 동일하게 `auth` 조건 비교를 적용한다.
- 본인인증과 콘텐츠 보기 설정 guard를 통과하면 `creator.characterId`로 기존 AI 채팅방/캐릭터 상세 진입 흐름을 연결한다. - 본인인증과 콘텐츠 보기 설정 guard를 통과하면 `creator.characterId`로 기존 AI 채팅방/캐릭터 상세 진입 흐름을 연결한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg "countryCode|uppercased\\(\\)|isKoreanCountry|auth == false|isShowAuthConfirmView|characterId|characterDetail|setAppStep\\(step: \\.login\\)" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg "onTapTalk|countryCode|uppercased\\(\\)|isKoreanCountry|auth == false|isShowAuthConfirmView|characterId|characterDetail|setAppStep\\(step: \\.login\\)" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: 기존 AI 채팅 guard와 동일한 국가/본인인증 분기가 확인된다. - 기대 결과: 기존 AI 채팅 guard와 동일한 국가/본인인증 분기가 확인된다.
### Phase 18: 최종 조립과 검증 ### Phase 18: 최종 조립과 검증
- [ ] **Task 18.1: 홈 섹션 순서 조립** - [ ] **Task 18.1: 홈 섹션 순서 조립**
- 대상 파일: - 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 작업 내용: - 작업 내용:
- 홈 탭 섹션 순서를 PRD와 동일하게 조립한다. - `CreatorChannelView`에서 공통 shell 순서를 PRD와 동일하게 조립한다.
- 순서: 헤더, 대화하기, 현재 라이브, 최신 오디오, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS. - 공통 shell 배치: 크리에이터 이미지 header를 먼저 배치하고, title bar는 header 위에 overlay로 띄우며, 그 아래 tab-bar와 선택된 tab content를 배치한다.
- title bar 기본 배경은 투명이고 scroll progress에 따라 black으로 전환된다.
- `CreatorChannelHomeView`에서 홈 탭 콘텐츠 순서를 PRD와 동일하게 조립한다.
- 홈 탭 콘텐츠 순서: 대화하기, 현재 라이브, 최신 오디오, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS.
- 각 섹션은 자신의 데이터가 없으면 숨긴다. - 각 섹션은 자신의 데이터가 없으면 숨긴다.
- API 실패 placeholder 상태에서는 데이터 기반 섹션을 표시하지 않는다. - API 실패 placeholder 상태에서는 데이터 기반 섹션을 표시하지 않는다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg -n "CreatorChannelHeaderSection|CreatorChannelTalkActionSection|CreatorChannelCurrentLiveSection|CreatorChannelLatestAudioSection|CreatorChannelDonationSection|CreatorChannelNoticeSection|CreatorChannelScheduleSection|CreatorChannelAudioSection|CreatorChannelSeriesSection|CreatorChannelCommunitySection|CreatorChannelFanTalkSection|CreatorChannelIntroduceSection|CreatorChannelActivitySection|CreatorChannelSnsSection" SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift` - 실행 명령: `rg -n "ZStack|overlay|Color\\.clear|backgroundProgress|CreatorChannelTitleBar|CreatorChannelHeaderSection|CreatorChannelTabBar|CreatorChannelHomeView|CreatorChannelTalkActionSection|CreatorChannelCurrentLiveSection|CreatorChannelLatestAudioSection|CreatorChannelDonationSection|CreatorChannelNoticeSection|CreatorChannelScheduleSection|CreatorChannelAudioSection|CreatorChannelSeriesSection|CreatorChannelCommunitySection|CreatorChannelFanTalkSection|CreatorChannelIntroduceSection|CreatorChannelActivitySection|CreatorChannelSnsSection" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 기대 결과: PRD의 홈 섹션 순서대로 조립되어 있다. - 기대 결과: PRD의 공통 shell과 홈 탭 콘텐츠 순서대로 조립되어 있다.
- [ ] **Task 18.2: 프로젝트 파일 포함 여부 확인** - [ ] **Task 18.2: 프로젝트 파일 포함 여부 확인**
- 대상 파일: - 대상 파일:
@@ -523,13 +584,14 @@
- [ ] **Task 18.3: PRD 성공 기준 회귀 점검** - [ ] **Task 18.3: PRD 성공 기준 회귀 점검**
- 대상 파일: - 대상 파일:
- 확인: `docs/20260701_크리에이터_채널_홈/prd.md` - 확인: `docs/20260701_크리에이터_채널_홈/prd.md`
- 확인: `SodaLive/Sources/V2/CreatorChannel/**`
- 확인: `SodaLive/Sources/V2/CreatorChannel/Home/**` - 확인: `SodaLive/Sources/V2/CreatorChannel/Home/**`
- 작업 내용: - 작업 내용:
- PRD `Success Criteria`를 한 줄씩 대조한다. - PRD `Success Criteria`를 한 줄씩 대조한다.
- `화보`, DM 버튼, API 실패 전체 error state가 구현에 들어가지 않았는지 검색한다. - `화보`, DM 버튼, API 실패 전체 error state가 구현에 들어가지 않았는지 검색한다.
- `ic_sns_*`, `ic_new_talk`, follow/unfollow, AI 채팅 guard가 구현에 포함됐는지 검색한다. - `ic_sns_*`, `ic_new_talk`, title bar icon asset, 기존 `UserProfile` 기준 follow/notify mutation, title bar/header overlay, AI 채팅 guard가 구현에 포함됐는지 검색한다.
- 검증 기준: - 검증 기준:
- 실행 명령: `rg -n "화보|photo|ic_new_dm|DM|ic_new_talk|ic_sns_|creatorFollow|creatorUnFollow|isApiFailedPlaceholderVisible|countryCode|auth == false" SodaLive/Sources/V2/CreatorChannel/Home SodaLive/Sources/ContentView.swift` - 실행 명령: `rg -n "화보|photo|ic_new_dm|DM|ic_new_talk|ic_sns_|ic_new_bar_back|ic_new_follow|ic_new_following|ic_new_more|ic_bar_bell|ic_bar_bell_fill|creatorFollow|follow: true, notify: true|follow: false, notify: false|follow: true, notify: false|onTapNotify|onTapUnnotify|ZStack|overlay|Color\\.clear|isApiFailedPlaceholderVisible|countryCode|auth == false" SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/ContentView.swift`
- 기대 결과: 제외 범위는 UI 구현에 없고, 필수 요구사항 키워드는 확인된다. - 기대 결과: 제외 범위는 UI 구현에 없고, 필수 요구사항 키워드는 확인된다.
- [ ] **Task 18.4: 빌드 검증** - [ ] **Task 18.4: 빌드 검증**
@@ -549,5 +611,10 @@
- 2026-07-01: `docs/agent-guides/build-test-verification.md`를 확인해 최종 빌드 검증 명령을 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 반영했다. - 2026-07-01: `docs/agent-guides/build-test-verification.md`를 확인해 최종 빌드 검증 명령을 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 반영했다.
- 2026-07-01: 기존 V2 홈 API/Repository/ViewModel, AI 채팅 guard, follow/unfollow API, SNS asset 사용처를 확인해 대상 파일과 검증 기준에 반영했다. - 2026-07-01: 기존 V2 홈 API/Repository/ViewModel, AI 채팅 guard, follow/unfollow API, SNS asset 사용처를 확인해 대상 파일과 검증 기준에 반영했다.
- 2026-07-01: 사용자 요청에 따라 기존 Phase 4 `대화하기 액션 영역`을 Phase 17로 이동하고, 기존 Phase 5~17을 Phase 4~16으로 한 칸씩 당겼다. - 2026-07-01: 사용자 요청에 따라 기존 Phase 4 `대화하기 액션 영역`을 Phase 17로 이동하고, 기존 Phase 5~17을 Phase 4~16으로 한 칸씩 당겼다.
- 2026-07-01: Header, TitleBar, TabBar만으로 sticky 동작을 먼저 확인할 수 있도록 Phase 3에 `CreatorChannelHomeView_Previews` 기반 debug-only sticky scaffold 검증 task를 추가했다. - 2026-07-01: Header, TitleBar, TabBar만으로 sticky 동작을 먼저 확인할 수 있도록 Phase 3에 `CreatorChannelHomeView_Previews` 기반 debug-only sticky scaffold 검증 task를 추가했다. 2026-07-02 구조 변경으로 실제 검증 대상은 `CreatorChannelView_Previews`로 갱신했다.
- 2026-07-01: Phase 1~2 구현을 완료하고 `rg` 체크리스트 및 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 성공으로 신규 API/상태 모델, 라우팅, 기본 shell, I18n, 프로젝트 파일 포함을 검증했다. - 2026-07-01: Phase 1~2 구현을 완료하고 `rg` 체크리스트 및 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 성공으로 신규 API/상태 모델, 라우팅, 기본 shell, I18n, 프로젝트 파일 포함을 검증했다.
- 2026-07-02: 사용자 확인 사항을 반영해 title bar, 크리에이터 프로필 header, tab-bar를 `CreatorChannelView` 공통 shell로 분리하고, `CreatorChannelHomeView`는 tab-bar 아래 홈 탭 콘텐츠만 담당하도록 PRD와 계획을 갱신했다.
- 2026-07-02: 이미 완료된 Phase 1~2 기록은 보존하고, 후속 구조 정리를 위해 Task 2.4 `공통 CreatorChannel shell로 구조 정리`를 추가했다.
- 2026-07-02: 사용자 확인 사항을 반영해 title bar Figma URL 3종과 사용할 아이콘 asset(`ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill`)을 Task 3.1 및 최종 회귀 검색 기준에 추가했다.
- 2026-07-02: 기존 `UserProfile` 구현을 확인해 title bar 팔로우/팔로우 취소/알림 설정/알림 설정 취소를 `UserRepository.creatorFollow(creatorId:follow:notify:)`로 처리하고, `ic_bar_bell`/`ic_bar_bell_fill` 터치 시 각각 `notify: true`/`notify: false` 호출이 일어나도록 계획에 반영했다.
- 2026-07-02: 사용자 확인 사항을 반영해 HeaderView는 `CreatorChannelHomeResponse.creator` 데이터로 채우고, 별도 프로필 이미지 없이 큰 배경 이미지만 표시하며, title bar는 기본 투명 배경으로 header 위에 overlay되도록 Task 3.2/3.4/3.5와 최종 검증 기준을 갱신했다.

View File

@@ -1,41 +1,49 @@
# PRD: 크리에이터 채널 홈 # PRD: 크리에이터 채널 홈
## 1. Overview ## 1. Overview
크리에이터 채널 화면에서 신규 API `GET /api/v2/creator-channels/{creatorId}/home` 응답을 사용해 크리에이터 프로필, 현재 라이브, 최신 오디오 콘텐츠, 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS 정보를 표시한다. 크리에이터 채널 화면은 title bar, 크리에이터 프로필 header, tab-bar를 공통 shell로 유지하고, tab-bar 아래 콘텐츠만 선택된 탭에 따라 교체한다. 채널 최초 진입 시 기본 선택 탭은 `홈`이며, 홈 탭에서 신규 API `GET /api/v2/creator-channels/{creatorId}/home` 응답을 사용해 현재 라이브, 최신 오디오 콘텐츠, 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS 정보를 표시한다.
API 연동을 먼저 구현하고, UI는 API 응답 데이터를 기준으로 Figma 섹션 단위로 나누어 순차 구현한다. API 연동을 먼저 구현하고, UI는 공통 shell과 홈 탭 콘텐츠를 분리한 뒤 API 응답 데이터를 기준으로 Figma 섹션 단위로 나누어 순차 구현한다.
Figma 전체 화면 기준 홈 탭 섹션 순서는 아래를 따른다. Figma 전체 화면 기준 공통 shell은 아래를 따른다.
1. 크리에이터 이미지/프로필 헤더 1. title bar
2. 대화하기 액션 영역 2. 크리에이터 이미지/프로필 header
3. 현재 라이브 3. tab-bar
4. 최신 오디오 콘텐츠
5. 채널 후원 Figma 전체 화면 기준 홈 탭 콘텐츠 순서는 아래를 따른다.
6. 공지
7. 스케줄 1. 대화하기 액션 영역
8. 오디오 2. 현재 라이브
9. 시리즈 3. 최신 오디오 콘텐츠
10. 커뮤니티 4. 채널 후원
11. 팬Talk 5. 공지
12. 소개 6. 스케줄
13. 활동 7. 오디오
14. SNS 8. 시리즈
9. 커뮤니티
10. 팬Talk
11. 소개
12. 활동
13. SNS
상단 tab-bar 항목은 `홈`, `라이브`, `오디오`, `시리즈`, `커뮤니티`, `팬Talk`, `후원`으로 구성한다. Figma에는 `화보` 탭과 활동 항목이 보이지만 이번 구현 범위에서는 제외한다. 상단 tab-bar 항목은 `홈`, `라이브`, `오디오`, `시리즈`, `커뮤니티`, `팬Talk`, `후원`으로 구성한다. Figma에는 `화보` 탭과 활동 항목이 보이지만 이번 구현 범위에서는 제외한다.
## 2. Problem ## 2. Problem
- 기존 크리에이터 상세/채널 화면은 신규 `creator-channels` 홈 API 응답 구조와 Figma의 채널 홈 구성을 그대로 반영하지 못한다. - 기존 크리에이터 상세/채널 화면은 신규 `creator-channels` 홈 API 응답 구조와 Figma의 채널 홈 구성을 그대로 반영하지 못한다.
-화면은 여러 도메인 데이터를 한 endpoint에서 받지만, 각 섹션의 UI와 전체보기 진입 대상이 달라 한 파일에 직접 구현하면 유지보수와 검증이 어려워진다. -API는 크리에이터 프로필과 여러 도메인 데이터를 한 endpoint에서 받지만, 크리에이터 프로필은 채널 공통 shell에 쓰이고 나머지 섹션은 홈 탭 콘텐츠에 쓰인다. 이를 한 View에 직접 구현하면 이후 탭별 화면 확장 시 유지보수와 검증이 어려워진다.
- 크리에이터 이미지 영역이 OS status bar 영역까지 확장되고, scroll 위치에 따라 title bar/tab-bar/status bar 상태가 함께 변해야 하므로 일반적인 정적 navigation bar로 처리하기 어렵다. - 크리에이터 이미지 영역이 OS status bar 영역까지 확장되고, scroll 위치에 따라 title bar/tab-bar/status bar 상태가 함께 변해야 하므로 일반적인 정적 navigation bar로 처리하기 어렵다.
- tab-bar는 title bar와 붙는 순간 sticky 상태가 되어야 하고, sticky 이후에는 아래 콘텐츠만 스크롤되어야 한다. - tab-bar는 title bar와 붙는 순간 sticky 상태가 되어야 하고, sticky 이후에는 아래 콘텐츠만 스크롤되어야 한다.
- `isAiChatAvailable` 값에 따라 대화하기 버튼 노출이 달라지지만, 버튼이 없어도 액션 영역 높이는 유지해야 한다. - `isAiChatAvailable` 값에 따라 대화하기 버튼 노출이 달라지지만, 버튼이 없어도 액션 영역 높이는 유지해야 한다.
- 선택된 tab 및 홈 섹션의 `전체보기` 진입 화면은 전체 기능 구현 전에도 placeholder destination이 필요하다. - 선택된 tab 및 홈 섹션의 `전체보기` 진입 화면은 전체 기능 구현 전에도 placeholder destination이 필요하다.
## 3. Goals ## 3. Goals
- `creatorId``GET /api/v2/creator-channels/{creatorId}/home`을 호출하고 응답 데이터로 홈 화면을 구성한다. - `creatorId``GET /api/v2/creator-channels/{creatorId}/home`을 호출하고 응답 데이터로 크리에이터 채널 공통 shell과 홈 탭 콘텐츠를 구성한다.
- 신규 API, Repository, ViewModel, Response model은 `SodaLive/Sources/V2/**` 하위에 둔다. - 신규 API, Repository, ViewModel, Response model은 `SodaLive/Sources/V2/**` 하위에 둔다.
- API 구현을 UI보다 먼저 완료해 섹션들이 동일한 response model을 사용하도록 한다. - API 구현을 UI보다 먼저 완료해 공통 shell과 홈 탭 섹션들이 동일한 response model을 사용하도록 한다.
- `CreatorChannelView`는 title bar, header, tab-bar, 선택된 tab 상태, 홈 API 호출 상태를 소유한다.
- `CreatorChannelHomeView`는 tab-bar 아래 홈 탭 콘텐츠만 렌더링한다.
- 홈 API 응답의 `creator` 정보는 공통 shell의 header/title/follow/notification/action 상태에 반영한다.
- Figma 기준 섹션을 이후 `plan-task.md`에서 phase 단위로 나눌 수 있도록 요구사항을 분리한다. - Figma 기준 섹션을 이후 `plan-task.md`에서 phase 단위로 나눌 수 있도록 요구사항을 분리한다.
- 크리에이터 대표 이미지는 화면 최상단에서 OS status bar 영역까지 확장해 표시한다. - 크리에이터 대표 이미지는 화면 최상단에서 OS status bar 영역까지 확장해 표시한다.
- scroll 진행도에 따라 title bar 배경색을 점진적으로 black에 가깝게 변경한다. - scroll 진행도에 따라 title bar 배경색을 점진적으로 black에 가깝게 변경한다.
@@ -128,7 +136,8 @@ struct CreatorChannelHomeResponse: Decodable {
- 최초 진입 시 홈 API를 호출한다. - 최초 진입 시 홈 API를 호출한다.
- 로딩 중에는 기존 V2 화면 패턴에 맞는 loading state를 표시한다. - 로딩 중에는 기존 V2 화면 패턴에 맞는 loading state를 표시한다.
- API 실패 시에는 전체 화면 error state로 전환하지 않고 placeholder 기반으로 일부 UI를 유지한다. - API 실패 시에는 전체 화면 error state로 전환하지 않고 placeholder 기반으로 일부 UI를 유지한다.
- API 실패 placeholder는 title bar, tab-bar, 홈 외 tab placeholder destination처럼 서버 데이터 없이 표시 가능한 기본 shell을 유지하는 방식으로 처리한다. - API 실패 placeholder는 title bar, tab-bar, 홈 외 tab placeholder destination처럼 서버 데이터 없이 표시 가능한 공통 shell을 유지하는 방식으로 처리한다.
- 선택된 tab 상태와 홈 API 호출 상태는 공통 shell의 ViewModel에서 관리한다.
- 응답을 성공적으로 받은 뒤 각 섹션은 자신의 데이터가 비어 있으면 section title과 empty state를 표시하지 않고 섹션 전체를 숨긴다. - 응답을 성공적으로 받은 뒤 각 섹션은 자신의 데이터가 비어 있으면 section title과 empty state를 표시하지 않고 섹션 전체를 숨긴다.
- 동일 화면 생명주기 안에서 불필요한 중복 호출은 피한다. - 동일 화면 생명주기 안에서 불필요한 중복 호출은 피한다.
- pull-to-refresh가 기존 크리에이터 상세 화면에 있다면 같은 패턴을 검토한다. 기존 패턴이 없으면 이번 범위에서 새로 추가하지 않는다. - pull-to-refresh가 기존 크리에이터 상세 화면에 있다면 같은 패턴을 검토한다. 기존 패턴이 없으면 이번 범위에서 새로 추가하지 않는다.
@@ -137,35 +146,51 @@ struct CreatorChannelHomeResponse: Decodable {
Figma 참조: Figma 참조:
- 전체 화면: `node-id=296:14890` - 전체 화면: `node-id=296:14890`
- Unfollow title bar: `node-id=296:14287` - Unfollow title bar: `node-id=296:14287`, `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=296-14287&m=dev`
- Follow + 알림 설정 title bar: `node-id=296:14288` - Follow + 알림 설정 title bar: `node-id=296:14288`, `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=296-14288&m=dev`
- Follow + 알림 해제 title bar: `node-id=296:14289` - Follow + 알림 해제 title bar: `node-id=296:14289`, `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=296-14289&m=dev`
요구사항: 요구사항:
- title bar, 크리에이터 프로필 header, status bar 처리는 `CreatorChannelView` 공통 shell에서 담당한다.
- 크리에이터 이미지 영역은 OS status bar 영역까지 확장한다. - 크리에이터 이미지 영역은 OS status bar 영역까지 확장한다.
- title bar는 화면 최상단에 overlay 형태로 배치한다. - title bar는 화면 최상단에 `ZStack` 또는 `overlay` 형태로 띄워 header view와 겹치게 배치한다.
- 초기 title bar 배경은 투명 또는 이미지 위 overlay 상태로 시작한다. - title bar 뒤에 큰 배경 이미지가 보이도록 기본 배경은 투명이다.
- scroll 진행도에 따라 `backgroundProgress`가 증가할 때만 title bar 배경색을 black에 가깝게 점진적으로 표시한다.
- tab-bar와 title bar가 가까워질수록 title bar 배경색은 점진적으로 black으로 변한다. - tab-bar와 title bar가 가까워질수록 title bar 배경색은 점진적으로 black으로 변한다.
- tab-bar가 sticky 상태가 되면 OS status bar 영역도 title bar 배경색과 동일하게 보이도록 한다. - tab-bar가 sticky 상태가 되면 OS status bar 영역도 title bar 배경색과 동일하게 보이도록 한다.
- title bar 좌측에는 back 버튼을 둔다. - title bar 좌측에는 back 버튼을 둔다.
- title bar 우측에는 팔로우/알림 상태 버튼과 더보기 버튼을 둔다. - title bar 우측에는 팔로우/알림 상태 버튼과 더보기 버튼을 둔다.
- title bar 좌측 back 버튼은 `ic_new_bar_back` asset을 사용한다.
- title bar 우측 더보기 버튼은 `ic_new_more` asset을 사용한다.
- `isFollow == false`이면 `팔로우` capsule 버튼을 표시한다. - `isFollow == false`이면 `팔로우` capsule 버튼을 표시한다.
- `isFollow == true && isNotify == true`이면 팔로우 상태 아이콘 capsule과 알림 설정 아이콘을 표시한다. - `isFollow == false` 상태의 follow capsule에는 `ic_new_follow` asset을 사용한다.
- `isFollow == true && isNotify == false`이면 팔로우 상태 아이콘 capsule과 알림 해제 아이콘을 표시한다. - `isFollow == true` 상태의 following capsule에는 `ic_new_following` asset을 사용한다.
- `isFollow == true && isNotify == true`이면 following capsule과 알림 설정 아이콘 `ic_bar_bell_fill`을 표시한다.
- `isFollow == true && isNotify == false`이면 following capsule과 알림 해제 아이콘 `ic_bar_bell`을 표시한다.
- 더보기 버튼은 항상 표시한다. - 더보기 버튼은 항상 표시한다.
- 팔로우/팔로우 mutation은 기존 follow, unfollow API를 사용한다. - 팔로우/팔로우 취소/알림 설정/알림 설정 취소 mutation은 기존 `UserProfile`에서 사용하는 `UserRepository.creatorFollow(creatorId:follow:notify:)` 흐름을 그대로 사용한다.
- 알림 상태 버튼은 신규 API를 만들지 않고 기존 follow/unfollow 및 알림 상태 처리 흐름에서 제공되는 범위 안에서 연결한다. - 팔로우 action은 `creatorFollow(follow: true, notify: true)`를 호출한다.
- 팔로우 취소 action은 기존 `UserProfile``CreatorFollowNotifyDialog.onClickUnFollow`와 동일하게 `creatorFollow(follow: false, notify: false)`를 호출한다.
- `ic_bar_bell` 터치 시 알림 설정 action으로 `creatorFollow(follow: true, notify: true)`를 호출한다.
- `ic_bar_bell_fill` 터치 시 알림 설정 취소 action으로 `creatorFollow(follow: true, notify: false)`를 호출한다.
- 알림 상태 변경용 신규 API는 만들지 않는다.
- title bar에 사용하는 아이콘은 `ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill`로 고정하고, 다른 back/follow/more/bell asset으로 대체하지 않는다.
### 7.4 크리에이터 프로필 헤더 ### 7.4 크리에이터 프로필 헤더
- `creator.profileImageUrl`은 프로필 이미지와 크리에이터 이미지 헤더의 큰 배경 이미지에 동일하게 사용한다. - 크리에이터 프로필 헤더는 홈 탭 콘텐츠가 아니라 `CreatorChannelView` 공통 shell에 속한다.
- HeaderView의 표시 내용은 `CreatorChannelHomeResponse.creator`를 사용해 채운다.
- HeaderView에는 별도 원형/소형 프로필 이미지를 표시하지 않는다.
- `creator.profileImageUrl`은 크리에이터 이미지 헤더의 큰 배경 이미지에만 사용한다.
- 큰 배경 이미지 영역은 OS status bar까지 확장되도록 top safe area를 무시한다.
- `creator.nickname`, `creator.followerCount`를 Figma 위치와 계층에 맞게 표시한다. - `creator.nickname`, `creator.followerCount`를 Figma 위치와 계층에 맞게 표시한다.
- `creator.characterId`가 있는 경우 캐릭터/AI 대화 연결 여부를 판단하는 데이터로 사용한다. - `creator.characterId`가 있는 경우 캐릭터/AI 대화 연결 여부를 판단하는 데이터로 사용한다.
- 이미지 로드 실패 시 기존 프로젝트의 기본 프로필 이미지 처리 패턴을 따른다. - 이미지 로드 실패 시 기존 프로젝트의 이미지 placeholder 패턴을 따른다.
- 텍스트가 길어도 이미지/버튼/탭 영역과 겹치지 않도록 한 줄 말줄임 또는 기존 nickname 표시 관례를 따른다. - 텍스트가 길어도 이미지/버튼/탭 영역과 겹치지 않도록 한 줄 말줄임 또는 기존 nickname 표시 관례를 따른다.
### 7.5 대화하기 액션 영역 ### 7.5 대화하기 액션 영역
- 대화하기 액션 영역은 tab-bar 아래 홈 탭 콘텐츠에 속한다.
- `creator.isAiChatAvailable == true`이면 대화하기 버튼을 표시한다. - `creator.isAiChatAvailable == true`이면 대화하기 버튼을 표시한다.
- 대화하기 아이콘은 `ic_new_talk` asset을 사용한다. - 대화하기 아이콘은 `ic_new_talk` asset을 사용한다.
- 대화하기 버튼은 영역 중앙에 배치한다. - 대화하기 버튼은 영역 중앙에 배치한다.
@@ -177,6 +202,7 @@ Figma 참조:
### 7.6 Tab Bar ### 7.6 Tab Bar
- tab-bar는 `CreatorChannelView` 공통 shell에 속하며, 선택된 tab에 따라 tab-bar 아래 콘텐츠만 교체한다.
- tab-bar 항목은 `홈`, `라이브`, `오디오`, `시리즈`, `커뮤니티`, `팬Talk`, `후원` 순서로 표시한다. - tab-bar 항목은 `홈`, `라이브`, `오디오`, `시리즈`, `커뮤니티`, `팬Talk`, `후원` 순서로 표시한다.
- `화보` 탭은 표시하지 않는다. - `화보` 탭은 표시하지 않는다.
- tab-bar는 horizontal scroll 가능 구조로 구현한다. - tab-bar는 horizontal scroll 가능 구조로 구현한다.
@@ -288,8 +314,8 @@ Figma 참조:
- OS status bar 영역까지 확장되는 크리에이터 이미지 헤더 - OS status bar 영역까지 확장되는 크리에이터 이미지 헤더
- scroll progress에 따라 black으로 변하는 overlay title bar - scroll progress에 따라 black으로 변하는 overlay title bar
- 팔로우/알림 상태별 title bar - 팔로우/알림 상태별 title bar
- 대화하기 액션 영역
- sticky horizontal tab-bar - sticky horizontal tab-bar
- 공통 shell 아래 홈 탭의 대화하기 액션 영역
- 홈 탭의 섹션별 summary UI - 홈 탭의 섹션별 summary UI
- 홈 외 탭의 placeholder page - 홈 외 탭의 placeholder page
- 홈 섹션 전체보기 placeholder destination - 홈 섹션 전체보기 placeholder destination
@@ -313,11 +339,13 @@ Figma 참조:
## 9. Technical Constraints ## 9. Technical Constraints
- 신규 View, ViewModel, Repository 및 연결 하위 코드는 `SodaLive/Sources/V2/**` 아래에 작성한다. - 신규 View, ViewModel, Repository 및 연결 하위 코드는 `SodaLive/Sources/V2/**` 아래에 작성한다.
- 여러 페이지에서 재사용 가능한 공용 컴포넌트는 `SodaLive/Sources/V2/Component/**` 아래에 둔다. - 여러 페이지에서 재사용 가능한 공용 컴포넌트는 `SodaLive/Sources/V2/Component/**` 아래에 둔다.
- 특정 채널 홈 내부에서만 쓰는 컴포넌트는 채널 홈 폴더 하위 `Components`에 둔다. - 크리에이터 채널 공통 shell 내부에서만 쓰는 컴포넌트는 `SodaLive/Sources/V2/CreatorChannel/Components/**` 아래에 둔다.
- 홈 탭 내부에서만 쓰는 컴포넌트는 `SodaLive/Sources/V2/CreatorChannel/Home/Components/**` 아래에 둔다.
- API/Repository/ViewModel 구성은 기존 `SodaLive/Sources/V2/Main/Home/Following/**`, `Ranking/**`, `Recommendation/**` 패턴을 따른다. - API/Repository/ViewModel 구성은 기존 `SodaLive/Sources/V2/Main/Home/Following/**`, `Ranking/**`, `Recommendation/**` 패턴을 따른다.
- `SodaLive/Sources/V2/Component/SectionTitle.swift`, `AudioContentCard.swift`, `CommunityPostCard.swift`, `CapsuleTabBar.swift` 등 기존 V2 컴포넌트 재사용 가능성을 먼저 검토한다. - `SodaLive/Sources/V2/Component/SectionTitle.swift`, `AudioContentCard.swift`, `CommunityPostCard.swift`, `CapsuleTabBar.swift` 등 기존 V2 컴포넌트 재사용 가능성을 먼저 검토한다.
- `CreatorActivityType`은 중복 선언하지 않고 기존 enum 재사용 또는 공용 위치 이동을 검토한다. - `CreatorActivityType`은 중복 선언하지 않고 기존 enum 재사용 또는 공용 위치 이동을 검토한다.
- 신규 asset은 프로젝트 asset catalog에 이미 존재하는 `ic_new_talk`을 사용한다. `ic_new_dm`은 추후 DM 버튼 작업에서 사용한다. - 신규 asset은 프로젝트 asset catalog에 이미 존재하는 `ic_new_talk`을 사용한다. `ic_new_dm`은 추후 DM 버튼 작업에서 사용한다.
- title bar 아이콘은 프로젝트 asset catalog의 `ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill`을 사용한다.
- 신규 문구가 다국어 대상이면 `SodaLive/Sources/I18n/I18n.swift`에 추가한다. - 신규 문구가 다국어 대상이면 `SodaLive/Sources/I18n/I18n.swift`에 추가한다.
- Figma에서 제공되는 localhost asset URL은 앱 코드에 넣지 않는다. - Figma에서 제공되는 localhost asset URL은 앱 코드에 넣지 않는다.
- 프로젝트 설정 변경은 신규 파일이 Xcode project에 자동 포함되지 않는 구조일 때만 수행한다. - 프로젝트 설정 변경은 신규 파일이 Xcode project에 자동 포함되지 않는 구조일 때만 수행한다.
@@ -325,21 +353,27 @@ Figma 참조:
- SNS 섹션 아이콘은 Figma localhost asset이 아니라 프로젝트 asset catalog의 `ic_sns_*` 아이콘을 사용한다. - SNS 섹션 아이콘은 Figma localhost asset이 아니라 프로젝트 asset catalog의 `ic_sns_*` 아이콘을 사용한다.
## 10. Success Criteria ## 10. Success Criteria
- 크리에이터 채널 진입 시 `GET /api/v2/creator-channels/{creatorId}/home` 호출이 발생한다. - 크리에이터 채널 진입 시 `CreatorChannelView`가 표시되고, 기본 선택 탭은 `홈`다.
- 크리에이터 채널 진입 시 `GET /api/v2/creator-channels/{creatorId}/home` 호출이 발생한다.
- response model이 PRD의 API 초안 필드를 디코딩한다. - response model이 PRD의 API 초안 필드를 디코딩한다.
- API 실패 시 전체 화면 error state 대신 placeholder 기반으로 표시 가능한 기본 UI가 유지된다. - API 실패 시 전체 화면 error state 대신 placeholder 기반으로 표시 가능한 공통 shell UI가 유지된다.
- `creator` 정보가 header/title/action/tab UI 상태에 반영된다. - `creator` 정보가 공통 shell의 header/title 상태와 홈 탭의 action 상태에 반영된다.
- 크리에이터 이미지가 OS status bar 영역까지 표시된다. - 크리에이터 이미지가 OS status bar 영역까지 표시된다.
- HeaderView는 `CreatorChannelHomeResponse.creator`를 기반으로 표시되고, 별도 프로필 이미지를 추가로 표시하지 않는다.
- title bar의 기본 배경은 투명이고 큰 배경 이미지 위에 overlay되어 보인다.
- scroll 위치에 따라 title bar 배경이 점진적으로 black으로 변한다. - scroll 위치에 따라 title bar 배경이 점진적으로 black으로 변한다.
- tab-bar가 title bar와 맞닿으면 sticky 상태가 되고 아래 콘텐츠만 스크롤된다. - tab-bar가 title bar와 맞닿으면 sticky 상태가 되고 아래 콘텐츠만 스크롤된다.
- sticky 상태에서 OS status bar 영역도 title bar와 동일한 black 배경으로 보인다. - sticky 상태에서 OS status bar 영역도 title bar와 동일한 black 배경으로 보인다.
- `isFollow`, `isNotify` 조합에 따라 title bar의 팔로우/알림 상태가 Figma 3개 상태와 일치한다. - `isFollow`, `isNotify` 조합에 따라 title bar의 팔로우/알림 상태가 Figma 3개 상태와 일치한다.
- 팔로우/언팔로우 action은 기존 follow, unfollow API를 호출한다. - title bar는 `ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill` asset만 사용한다.
- 팔로우/팔로우 취소/알림 설정/알림 설정 취소 action은 기존 `UserProfile`과 동일하게 `UserRepository.creatorFollow(creatorId:follow:notify:)`를 호출한다.
- `ic_bar_bell` 터치 시 `creatorFollow(follow: true, notify: true)`, `ic_bar_bell_fill` 터치 시 `creatorFollow(follow: true, notify: false)`가 호출된다.
- `isAiChatAvailable`에 따라 대화하기 버튼이 조건부 표시된다. - `isAiChatAvailable`에 따라 대화하기 버튼이 조건부 표시된다.
- 대화하기 버튼은 기존 AI 채팅방 페이지로 이동하고, 기존 본인인증 guard와 비한국 국가 분기 guard를 적용한다. - 대화하기 버튼은 기존 AI 채팅방 페이지로 이동하고, 기존 본인인증 guard와 비한국 국가 분기 guard를 적용한다.
- 대화하기 버튼이 없어도 액션 영역 높이는 유지된다. - 대화하기 버튼이 없어도 액션 영역 높이는 유지된다.
- DM 버튼은 표시되지 않는다. - DM 버튼은 표시되지 않는다.
- tab에는 현재 라이브, 최신 오디오 콘텐츠, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS 섹션이 API 데이터 기반으로 표시된다. - tab-bar 아래 콘텐츠는 선택된 tab에 따라 교체된다.
- 홈 tab에는 대화하기, 현재 라이브, 최신 오디오 콘텐츠, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS 섹션이 API 데이터 기반으로 표시된다.
- SNS 섹션은 `ic_sns_*` asset 아이콘과 유효한 SNS URL만 사용해 표시된다. - SNS 섹션은 `ic_sns_*` asset 아이콘과 유효한 SNS URL만 사용해 표시된다.
- 데이터가 비어 있는 섹션은 표시되지 않는다. - 데이터가 비어 있는 섹션은 표시되지 않는다.
- `화보` 탭, 화보 섹션, 활동의 화보 row가 표시되지 않는다. - `화보` 탭, 화보 섹션, 활동의 화보 row가 표시되지 않는다.
@@ -357,6 +391,10 @@ Figma 참조:
- Figma `node-id=296:14287`, `296:14288`, `296:14289`의 design context와 screenshot을 확인해 title bar의 Unfollow, Follow+알림 설정, Follow+알림 해제 상태를 검토했다. - Figma `node-id=296:14287`, `296:14288`, `296:14289`의 design context와 screenshot을 확인해 title bar의 Unfollow, Follow+알림 설정, Follow+알림 해제 상태를 검토했다.
- `SodaLive/Sources/V2/**` 구조를 확인해 기존 V2 홈 API/Repository/ViewModel/Components 배치 패턴을 검토했다. - `SodaLive/Sources/V2/**` 구조를 확인해 기존 V2 홈 API/Repository/ViewModel/Components 배치 패턴을 검토했다.
- `SodaLive/Sources/V2/Component/SectionTitle.swift`, `AudioContentCard.swift`, `CommunityPostCard.swift`, `CapsuleTabBar.swift` 등 재사용 후보 파일 존재를 확인했다. - `SodaLive/Sources/V2/Component/SectionTitle.swift`, `AudioContentCard.swift`, `CommunityPostCard.swift`, `CapsuleTabBar.swift` 등 재사용 후보 파일 존재를 확인했다.
- 사용자 확인 사항을 반영해 `creator.profileImageUrl`을 헤더 큰 이미지로 사용, 빈 섹션 숨김, 기존 follow/unfollow API 사용, 대화하기의 기존 AI 채팅방 및 본인인증 guard 적용, DM 버튼 추후 작업, 홈 외 탭 제목 placeholder, 팬Talk 전체보기의 팬Talk 탭 이동, Swift `Int` 통일 정책을 확정 요구사항으로 이동했다. - 사용자 확인 사항을 반영해 `creator.profileImageUrl`을 헤더 큰 이미지로 사용, 빈 섹션 숨김, 기존 `UserProfile` follow/notify API 사용, 대화하기의 기존 AI 채팅방 및 본인인증 guard 적용, DM 버튼 추후 작업, 홈 외 탭 제목 placeholder, 팬Talk 전체보기의 팬Talk 탭 이동, Swift `Int` 통일 정책을 확정 요구사항으로 이동했다.
- 사용자 확인 사항을 반영해 API 실패 시 placeholder 기반으로 일부 UI를 유지하고, SNS 섹션 아이콘은 `ic_sns_*` asset을 사용하도록 확정 요구사항으로 이동했다. - 사용자 확인 사항을 반영해 API 실패 시 placeholder 기반으로 일부 UI를 유지하고, SNS 섹션 아이콘은 `ic_sns_*` asset을 사용하도록 확정 요구사항으로 이동했다.
- 저장소의 `ic_sns_fancimm`, `fancimmUrl`, `I18n.ProfileUpdate.fancimm`을 확인해 Figma의 Fansim 표기를 `Fancimm(팬심M)` 기준으로 정리했다. - 저장소의 `ic_sns_fancimm`, `fancimmUrl`, `I18n.ProfileUpdate.fancimm`을 확인해 Figma의 Fansim 표기를 `Fancimm(팬심M)` 기준으로 정리했다.
- 2026-07-02: 사용자 확인 사항을 반영해 title bar, 크리에이터 프로필 header, tab-bar를 `CreatorChannelView` 공통 shell로 두고, tab-bar 아래 홈 탭 콘텐츠만 `CreatorChannelHomeView`가 담당하도록 구조를 변경했다.
- 2026-07-02: 사용자 확인 사항을 반영해 title bar Figma URL 3종과 사용할 아이콘 asset(`ic_new_bar_back`, `ic_new_follow`, `ic_new_following`, `ic_new_more`, `ic_bar_bell`, `ic_bar_bell_fill`)을 확정 요구사항으로 추가했다.
- 2026-07-02: 기존 `UserProfile` 동작을 확인해 title bar의 팔로우/팔로우 취소/알림 설정/알림 설정 취소를 `UserRepository.creatorFollow(creatorId:follow:notify:)`로 처리하도록 확정했다.
- 2026-07-02: 사용자 확인 사항을 반영해 HeaderView는 `CreatorChannelHomeResponse.creator` 데이터로 채우고, 별도 프로필 이미지 없이 큰 배경 이미지만 표시하며, title bar는 기본 투명 배경으로 header 위에 overlay되도록 요구사항을 정리했다.