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/build-test-verification.md`
- Figma 전체 화면: `296:14890`
- Figma Unfollow title bar: `296:14287`
- Figma Follow + 알림 설정 title bar: `296:14288`
- Figma Follow + 알림 해제 title bar: `296:14289`
- 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`, `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`, `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를 진행한다.
- 신규 크리에이터 채널 홈 구현은 `SodaLive/Sources/V2/CreatorChannel/Home/**` 아래에 둔다.
- 크리에이터 채널 홈 내부에서만 쓰는 UI는 `SodaLive/Sources/V2/CreatorChannel/Home/Components/**` 아래에 둔다.
- 크리에이터 채널 진입점은 `CreatorChannelView`로 두고, 최초 선택 tab은 `.home`으로 둔다.
- `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/**`로 올린다.
- 기존 V2 홈의 `TargetType`, Repository, ViewModel, `ApiResponse<T>` 디코딩 패턴을 따른다.
- `Long` 계열 id/count는 Swift `Int`로 통일한다.
@@ -23,24 +28,26 @@
- `화보` 탭, 화보 섹션, 활동의 화보 row는 구현하지 않는다.
- DM 버튼과 DM 진입은 구현하지 않는다.
- 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을 사용한다.
- 기존 `AppStep.creatorDetail(userId:)` 진입은 새 크리에이터 채널 화면으로 연결한다.
- 기존 `AppStep.creatorDetail(userId:)` 진입은 새 크리에이터 채널 화면으로 연결한다.
## 주요 대상 파일
### 생성
- `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/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/Components/CreatorChannelTitleBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHeaderSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.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/CreatorChannelTabBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelCurrentLiveSection.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelLatestAudioSection.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`
- 기대 결과: 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`
- 작업 내용:
@@ -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`
- 기대 결과: 7개 tab case가 있고 `화보`/`photo` case가 없다.
- 후속 구조 변경:
- tab enum은 홈 탭 전용이 아니라 채널 공통 shell 상태이므로 Task 2.4에서 `CreatorChannelTab`으로 이름과 위치를 정리한다.
- [x] **Task 1.3: API endpoint 생성**
- 대상 파일:
@@ -141,11 +150,13 @@
- `fetchHome(creatorId:)`에서 `ApiResponse<CreatorChannelHomeResponse>`를 디코딩한다.
- 성공 시 `response`, `selectedTab = .home`, `isApiFailedPlaceholderVisible = false`를 갱신한다.
- 실패/디코딩 실패 시 `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를 둔다.
- 검증 기준:
- 실행 명령: `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 호출이 확인된다.
- 후속 구조 변경:
- title bar mutation은 기존 `UserProfile` 현재 동작과 동일하게 Task 2.4에서 `CreatorChannelViewModel``creatorFollow(follow:notify:)` 기반으로 정리한다.
### Phase 2: 라우팅과 기본 화면 shell
@@ -158,10 +169,12 @@
- `AppStep.creatorDetail(userId:)` case는 유지한다.
- `ContentView`에서 `.creatorDetail(let userId)` destination을 `CreatorChannelHomeView(creatorId: userId)`로 변경한다.
- 기존 `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`
- 기대 결과: `.creatorDetail`이 신규 `CreatorChannelHomeView`로 연결되고 기존 `UserProfileView` 파일은 유지된다.
- 후속 구조 변경:
- 크리에이터 채널 공통 shell 분리 결정에 따라 Task 2.4에서 `.creatorDetail` destination을 `CreatorChannelView(creatorId:)`로 변경한다.
- [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`
- 기대 결과: API 호출, 실패 placeholder, 홈 외 tab placeholder shell이 확인된다.
- 후속 구조 변경:
- 공통 shell과 선택 tab/API 상태는 Task 2.4에서 `CreatorChannelView`/`CreatorChannelViewModel`로 이동하고, `CreatorChannelHomeView`는 홈 탭 콘텐츠만 담당한다.
- [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`
- 기대 결과: 신규 사용자 노출 문구가 `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
- [ ] **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`
- 작업 내용:
- back 버튼, 팔로우/팔로잉 상태 버튼, 알림 상태 아이콘, 더보기 버튼을 표시한다.
- back 버튼은 `ic_new_bar_back` asset을 사용한다.
- 더보기 버튼은 `ic_new_more` asset을 사용한다.
- `isFollow == false`이면 `팔로우` capsule을 표시한다.
- `isFollow == true && isNotify == true`이면 팔로잉 상태 capsule과 알림 설정 아이콘을 표시한다.
- `isFollow == true && isNotify == false`이면 팔로잉 상태 capsule과 알림 해제 아이콘을 표시한다.
- `isFollow == false` 상태의 follow capsule에는 `ic_new_follow` asset을 사용한다.
- `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`로 제어한다.
- 구현 기준은 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`
- 기대 결과: Figma 3개 title bar 상태를 표현하는 조건 분기가 확인된다.
- 실행 명령: `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 상태를 표현하는 조건 분기, 지정된 6개 icon asset 사용, 팔로우/팔로우 취소/알림 설정/알림 설정 취소 tap closure가 확인된다.
- [ ] **Task 3.2: 크리에이터 이미지/프로필 헤더 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelHeaderSection.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 작업 내용:
- `creator.profileImageUrl`을 큰 배경 이미지와 프로필 이미지에 동일하게 사용한다.
- 이미지 영역은 OS status bar까지 확장되도록 top safe area를 무시한다.
- HeaderView의 표시 내용은 `CreatorChannelHomeResponse.creator`를 사용해 채운다.
- 별도 원형/소형 프로필 이미지는 표시하지 않는다.
- `creator.profileImageUrl`은 큰 배경 이미지에만 사용한다.
- 큰 배경 이미지 영역은 OS status bar까지 확장되도록 top safe area를 무시한다.
- `creator.nickname`, `creator.followerCount`를 표시한다.
- 이미지 로드 실패 시 기존 `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`
- 기대 결과: profile image가 헤더 큰 이미지로 쓰이고 top safe area 확장이 확인된다.
- 실행 명령: `rg "struct CreatorChannelHeaderSection|CreatorChannelCreatorResponse|profileImageUrl|ignoresSafeArea\\(.*top|nickname|followerCount|DownsampledKFImage|KFImage" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelHeaderSection.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: `CreatorChannelHomeResponse.creator` 기반 데이터 표시, 큰 배경 이미지의 top safe area 확장, 별도 프로필 이미지 미표시가 확인된다.
- [ ] **Task 3.3: horizontal tab-bar 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTabBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 확인: `SodaLive/Sources/V2/Component/TextTabBar.swift`
- 작업 내용:
- `CreatorChannelHomeTab.allCases`를 horizontal scroll로 표시한다.
- `CreatorChannelTab.allCases`를 horizontal scroll로 표시한다.
- 선택된 tab은 white text와 soda color bottom indicator로 표시한다.
- 비선택 tab은 gray text로 표시한다.
- `화보` tab은 표시하지 않는다.
- 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가 확인된다.
- [ ] **Task 3.4: scroll progress와 sticky 계산 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTitleBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelTabBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelTitleBar.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는 header view 위에 `ZStack` 또는 `overlay` 형태로 띄운다.
- title bar 뒤로 큰 배경 이미지가 보이도록 기본 `backgroundProgress`는 0, 기본 배경은 투명으로 둔다.
- tab-bar가 title bar에 닿으면 tab-bar를 sticky 상태로 고정한다.
- 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`
- 기대 결과: scroll 기반 title bar opacity sticky tab-bar 상태 계산이 확인된다.
- 실행 명령: `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`
- 기대 결과: geometry preference 기반 계산, 기본 투명 title bar overlay, scroll 기반 title bar opacity, sticky tab-bar 상태 계산이 확인된다.
- [ ] **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로 둔다.
- preview scaffold는 sample creator, `CreatorChannelHeaderSection`, `CreatorChannelTitleBar`, `CreatorChannelTabBar`, 충분한 높이의 dummy rows를 사용한다.
- `#if DEBUG` preview 전용 sticky scaffold를 `CreatorChannelView_Previews` 또는 같은 파일의 private preview view로 둔다.
- preview scaffold는 sample creator, 큰 배경 이미지 기반 `CreatorChannelHeaderSection`, 기본 투명 배경의 `CreatorChannelTitleBar`, `CreatorChannelTabBar`, 충분한 높이의 dummy rows를 사용한다.
- preview에서 초기 상태의 title bar 뒤로 header 큰 배경 이미지가 보이는지 확인한다.
- 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`
- 기대 결과: API와 하위 섹션 구현 전에도 Header/TitleBar/TabBar sticky 동작 확인할 수 있는 debug-only preview scaffold가 존재한다.
- 실행 명령: `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 동작과 title bar/header overlay 상태를 확인할 수 있는 debug-only preview scaffold가 존재한다.
### Phase 4: 홈 외 placeholder
- [ ] **Task 4.1: 홈 외 tab placeholder 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelPlaceholderTabView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelPlaceholderTabView.swift`
- 작업 내용:
- `selectedTab != .home`이면 실제 목록 대신 `CreatorChannelPlaceholderTabView(title: selectedTab.title)`을 표시한다.
- 홈 섹션의 전체보기 action 중 별도 기존 route가 없는 경우 대응 tab을 선택하도록 연결한다.
- 팬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 전환이 확인된다.
### Phase 5: 현재 라이브 섹션
@@ -368,10 +422,10 @@
- `audioContents.isEmpty`이면 섹션을 표시하지 않는다.
- Figma 홈 노출 개수만 표시한다.
- `AudioContentCard`와 채널 홈 전용 thumbnail wrapper를 재사용한다.
- 전체보기는 `selectedTab = .audio`로 연결한다.
- 전체보기는 부모에서 주입받은 `onSelectTab(.audio)` closure로 연결한다.
- 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 전체보기 연결이 확인된다.
### Phase 11: 시리즈 섹션
@@ -383,10 +437,10 @@
- 작업 내용:
- `series.isEmpty`이면 섹션을 표시하지 않는다.
- 각 item의 `seriesId`, `title`, `coverImageUrl`, `numberOfContent`, `isNew`, `isOriginal`을 표시한다.
- 전체보기는 `selectedTab = .series`로 연결한다.
- 전체보기는 부모에서 주입받은 `onSelectTab(.series)` closure로 연결한다.
- 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: 커뮤니티 섹션
@@ -400,9 +454,9 @@
- `communities.isEmpty`이면 섹션을 표시하지 않는다.
- `CommunityPostCard`가 요구사항과 맞으면 재사용한다.
- 유료/잠금 게시물은 기존 `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 전환이 확인된다.
### Phase 13: 팬Talk 섹션
@@ -414,9 +468,9 @@
- 작업 내용:
- `fanTalk.latestFanTalk == nil`이면 섹션을 표시하지 않는다.
- `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으로 이동한다.
### Phase 14: 소개 섹션
@@ -476,38 +530,45 @@
- 버튼은 영역 중앙에 배치한다.
- 버튼이 없어도 영역 높이와 vertical spacing을 유지한다.
- `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`
- 기대 결과: 대화하기만 조건부 표시되고 DM 버튼 구현이 없다.
- [ ] **Task 17.2: 기존 AI 채팅방 진입 guard 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 확인: `SodaLive/Sources/Chat/ChatTabView.swift`
- 확인: `SodaLive/Sources/App/AppStep.swift`
- 작업 내용:
- `CreatorChannelView``CreatorChannelHomeView``onTapTalk` closure를 주입한다.
- token이 없으면 `.login`으로 이동한다.
- 국가 코드를 trim/uppercase 처리하고, 비어 있거나 `KR`이면 한국으로 취급한다.
- 한국 사용자는 `auth == false`일 때 기존 본인인증 confirm/fullScreenCover 흐름을 재사용하거나 같은 조건으로 연결한다.
- 비한국 사용자는 기존 `ChatTabView`의 비한국 분기와 동일하게 `auth` 조건 비교를 적용한다.
- 본인인증과 콘텐츠 보기 설정 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와 동일한 국가/본인인증 분기가 확인된다.
### Phase 18: 최종 조립과 검증
- [ ] **Task 18.1: 홈 섹션 순서 조립**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/CreatorChannelHomeView.swift`
- 작업 내용:
- 홈 탭 섹션 순서를 PRD와 동일하게 조립한다.
- 순서: 헤더, 대화하기, 현재 라이브, 최신 오디오, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS.
- `CreatorChannelView`에서 공통 shell 순서를 PRD와 동일하게 조립한다.
- 공통 shell 배치: 크리에이터 이미지 header를 먼저 배치하고, title bar는 header 위에 overlay로 띄우며, 그 아래 tab-bar와 선택된 tab content를 배치한다.
- title bar 기본 배경은 투명이고 scroll progress에 따라 black으로 전환된다.
- `CreatorChannelHomeView`에서 홈 탭 콘텐츠 순서를 PRD와 동일하게 조립한다.
- 홈 탭 콘텐츠 순서: 대화하기, 현재 라이브, 최신 오디오, 채널 후원, 공지, 스케줄, 오디오, 시리즈, 커뮤니티, 팬Talk, 소개, 활동, SNS.
- 각 섹션은 자신의 데이터가 없으면 숨긴다.
- 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`
- 기대 결과: PRD의 홈 섹션 순서대로 조립되어 있다.
- 실행 명령: `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의 공통 shell과 홈 탭 콘텐츠 순서대로 조립되어 있다.
- [ ] **Task 18.2: 프로젝트 파일 포함 여부 확인**
- 대상 파일:
@@ -523,13 +584,14 @@
- [ ] **Task 18.3: PRD 성공 기준 회귀 점검**
- 대상 파일:
- 확인: `docs/20260701_크리에이터_채널_홈/prd.md`
- 확인: `SodaLive/Sources/V2/CreatorChannel/**`
- 확인: `SodaLive/Sources/V2/CreatorChannel/Home/**`
- 작업 내용:
- PRD `Success Criteria`를 한 줄씩 대조한다.
- `화보`, 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 구현에 없고, 필수 요구사항 키워드는 확인된다.
- [ ] **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: 기존 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: 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-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와 최종 검증 기준을 갱신했다.