feat(creator): 정렬 팝업을 적용한다

This commit is contained in:
Yu Sung
2026-07-04 17:11:19 +09:00
parent c4c18341a6
commit 0dd111f5d6
10 changed files with 306 additions and 161 deletions

View File

@@ -22,7 +22,7 @@
- 홈 탭의 `CreatorChannelCurrentLiveSection`과 기존 `showLiveDetail(_:)`, `showContentDetail(_:)`, `showCreateLive()` 흐름을 재사용한다.
- `CreatorChannelLiveResponse``CreatorChannelAudioContentResponse`는 기존 `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift` 타입을 우선 재사용한다. 타입 이동은 이번 구현에 필수일 때만 수행한다.
- `ContentSort`는 라이브 탭 외 콘텐츠 탭에서 재사용 가능하도록 `SodaLive/Sources/V2/CreatorChannel/Models/**` 아래에 둔다.
- sort-bar와 정렬 BottomSheet는 `SodaLive/Sources/V2/CreatorChannel/Components/**` 아래에 둔다.
- sort-bar와 정렬 context popup은 `SodaLive/Sources/V2/CreatorChannel/Components/**` 아래에 둔다.
- 라이브 탭 전용 API, Repository, ViewModel, View, item UI는 `SodaLive/Sources/V2/CreatorChannel/Live/**` 아래에 둔다.
- 최초 요청은 `page = 0`, `size = 20`, `sort = LATEST`로 고정한다.
- `hasNext == true`일 때만 `page + 1` 추가 요청을 수행하고, 추가 요청 중 중복 호출을 막는다.
@@ -34,7 +34,7 @@
### 생성
- `SodaLive/Sources/V2/CreatorChannel/Models/ContentSort.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- `SodaLive/Sources/V2/CreatorChannel/Live/Models/CreatorChannelLiveTabResponse.swift`
- `SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveApi.swift`
- `SodaLive/Sources/V2/CreatorChannel/Live/Repository/CreatorChannelLiveRepository.swift`
@@ -145,7 +145,7 @@
- 실행 명령: `rg "final class CreatorChannelLiveViewModel|isLoadingNextPage|liveReplayContents|selectedSort|private let pageSize|fetchFirstPage\\(creatorId: Int\\)|fetchNextPageIfNeeded|selectSort|ApiResponse<CreatorChannelLiveTabResponse>|page \\+ 1|hasNext" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveViewModel.swift`
- 기대 결과: 첫 페이지 로딩, 정렬 변경, 다음 페이지 로딩 상태와 성공 시에만 목록 교체/append하는 동작이 확인된다.
### Phase 3: sort-bar와 정렬 BottomSheet
### Phase 3: sort-bar와 정렬 context popup
- [x] **Task 3.1: sort-bar 공통 컴포넌트 생성**
- 대상 파일:
@@ -161,19 +161,19 @@
- 실행 명령: `rg "struct CreatorChannelSortBar|selectedSort: ContentSort|onTapSort|selectedSort.title|frame\\(height: 52" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift`
- 기대 결과: 현재 정렬과 터치 action이 있는 sort-bar가 확인된다.
- [x] **Task 3.2: 정렬 BottomSheet 컴포넌트 생성**
- [x] **Task 3.2: 정렬 context popup 컴포넌트 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift`
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- 작업 내용:
- `CreatorChannelSortBottomSheet``View`로 생성한다.
- `CreatorChannelSortContextPopup``View`로 생성한다.
- 입력값은 `selectedSort: ContentSort`, `sorts: [ContentSort] = ContentSort.allCases`, `onSelect: (ContentSort) -> Void`로 둔다.
- 항목은 `LATEST`, `POPULAR`, `PRICE_HIGH`, `PRICE_LOW` 순서로 표시한다.
- Figma context menu의 `추천순`은 표시하지 않는다.
- 선택된 항목은 배경색 또는 체크 아이콘 없이도 충분히 구분되는 foreground/background 상태를 적용한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelSortBottomSheet|selectedSort: ContentSort|ContentSort.allCases|onSelect|ForEach|sort.title" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift`
- 기대 결과: ContentSort 4개 항목 기반 BottomSheet가 확인된다.
- 실행 명령: `rg "추천순|recommend" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBottomSheet.swift`
- 실행 명령: `rg "struct CreatorChannelSortContextPopup|selectedSort: ContentSort|ContentSort.allCases|onSelect|ForEach|sort.title" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- 기대 결과: ContentSort 4개 항목 기반 context popup이 확인된다.
- 실행 명령: `rg "추천순|recommend" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- 기대 결과: 검색 결과가 없다.
### Phase 4: 라이브 탭 UI
@@ -223,13 +223,13 @@
- 입력값은 `creatorId: Int`, `isOwnCreatorChannel: Bool`, `onTapLive: (Int) -> Void`, `onTapContent: (Int) -> Void`로 둔다.
- 내부에서 `@StateObject private var viewModel = CreatorChannelLiveViewModel()`을 사용한다.
- `onAppear`에서 `hasLoaded == false`이면 `fetchFirstPage(creatorId:)`를 호출한다.
- sort-bar 터치 시 `.sheet` 또는 기존 앱 sheet 패턴으로 `CreatorChannelSortBottomSheet`를 표시한다.
- sort-bar 터치 시 `CreatorChannelSortContextPopup`를 표시한다.
- `currentLive != nil`이면 `CreatorChannelCurrentLiveSection(currentLive:onTapLive:)`를 표시한다.
- `liveReplayContents``LazyVStack`으로 표시하고 각 item의 `onAppear`에서 `fetchNextPageIfNeeded`를 호출한다.
- item tap은 `onTapContent(audioContent.audioContentId)`로 연결한다.
- CTA가 부모 화면의 고정 overlay와 겹치지 않도록 scroll content bottom padding을 추가한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelLiveTabView|CreatorChannelLiveViewModel|fetchFirstPage\\(creatorId: creatorId\\)|CreatorChannelSortBar|CreatorChannelSortBottomSheet|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|fetchNextPageIfNeeded|onTapContent\\(audioContent.audioContentId\\)|isOwnCreatorChannel" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift`
- 실행 명령: `rg "struct CreatorChannelLiveTabView|CreatorChannelLiveViewModel|fetchFirstPage\\(creatorId: creatorId\\)|CreatorChannelSortBar|CreatorChannelSortContextPopup|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|fetchNextPageIfNeeded|onTapContent\\(audioContent.audioContentId\\)|isOwnCreatorChannel" SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift`
- 기대 결과: 라이브 탭 화면, 정렬, 현재 라이브, 다시듣기 목록, pagination이 확인된다.
- [x] **Task 4.4: 라이브 탭 Empty 상태 추가**
@@ -321,9 +321,9 @@
- 확인: `SodaLive/Sources/V2/CreatorChannel`
- 확인: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- API endpoint, query parameter, 정렬 enum, `page=0`, `size=20`, sort-bar, BottomSheet, current live 재사용, 다시듣기 item, `ic_new_create_live`, 오디오 상세 이동 연결을 검색한다.
- API endpoint, query parameter, 정렬 enum, `page=0`, `size=20`, sort-bar, context popup, current live 재사용, 다시듣기 item, `ic_new_create_live`, 오디오 상세 이동 연결을 검색한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelLiveApi|/api/v2/creator-channels/|/live|URLEncoding.queryString|ContentSort|pageSize|20|page = 0|CreatorChannelSortBar|CreatorChannelSortBottomSheet|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|ic_new_create_live|contentDetail\\(contentId:" SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/App/AppStep.swift`
- 실행 명령: `rg "CreatorChannelLiveApi|/api/v2/creator-channels/|/live|URLEncoding.queryString|ContentSort|pageSize|20|page = 0|CreatorChannelSortBar|CreatorChannelSortContextPopup|CreatorChannelCurrentLiveSection|CreatorChannelAudioContentListItem|ic_new_create_live|contentDetail\\(contentId:" SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/App/AppStep.swift`
- 기대 결과: PRD 핵심 구현 지점이 모두 검색된다.
- [x] **Task 7.2: 빌드 검증**
@@ -342,7 +342,7 @@
- 작업 내용:
- 라이브 탭 진입 시 첫 요청이 `page=0`, `size=20`, `sort=LATEST`로 호출되는지 확인한다.
- sort-bar에 `최신순`이 표시되는지 확인한다.
- sort-bar 터치 시 BottomSheet가 표시되고 4개 정렬만 보이는지 확인한다.
- sort-bar 터치 시 context popup이 표시되고 4개 정렬만 보이는지 확인한다.
- 정렬 변경 시 첫 페이지부터 다시 조회되는지 확인한다.
- `currentLive != nil`인 경우 홈 탭 현재 라이브 UI와 동일한 카드가 표시되는지 확인한다.
- 마지막 item 노출 시 `hasNext == true`이면 `page + 1`이 호출되고 목록이 append되는지 확인한다.
@@ -355,6 +355,47 @@
- 기대 결과: PRD Success Criteria를 모두 만족한다.
- blocked 사유: 실제 로그인 세션, 타인/본인 크리에이터 채널 테스트 데이터, 라이브/다시듣기 응답 데이터, 네트워크 요청 관찰 수단이 필요해 현재 CLI 환경에서는 완료 검증하지 않는다.
### Phase 8: sort-bar 전체 개수와 정렬 context popup 변경
- [x] **Task 8.1: sort-bar 전체 개수 표시 추가**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 작업 내용:
- `CreatorChannelSortBar``totalCount: Int?` 입력을 추가한다.
- `totalCount >= 0`이면 좌측에 `전체 {totalCount}`를 표시한다.
- 라이브 탭은 `liveReplayContentCount`, 오디오 탭은 `audioContentCount`를 전달한다.
- 검증 기준:
- 실행 명령: `rg "totalCount|전체|liveReplayContentCount|audioContentCount" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortBar.swift SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 기대 결과: sort-bar 입력과 두 탭의 count 전달이 확인된다.
- [x] **Task 8.2: 정렬 UI를 context popup으로 변경**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift`
- 작업 내용:
- 기존 정렬 컴포넌트 내용을 anchored context popup 형태로 교체한다.
- popup은 `ContentSort.allCases` 4개만 표시한다.
- 선택 항목은 `Color.gray800`, 기본 항목은 `Color.gray900` 배경을 사용한다.
- sort-bar 터치 시 `.sheet`가 아니라 현재 화면 위 overlay로 popup을 표시한다.
- popup 밖 터치와 항목 선택 시 popup을 닫는다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelSortContextPopup|isSortPopupPresented|overlay|Color.gray700|cornerRadius: SodaSpacing.s14|\\.sheet\\(isPresented: \\$isSort" SodaLive/Sources/V2/CreatorChannel`
- 기대 결과: context popup 구현과 사용처가 확인되고 정렬 sheet 사용처는 검색되지 않는다.
- [x] **Task 8.3: 변경 검증**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/CreatorChannel`
- 확인: `docs/20260703_크리에이터_채널_라이브_탭`
- 확인: `docs/20260704_크리에이터_채널_오디오_탭`
- 검증 기준:
- 실행 명령: `git diff --check -- docs/20260703_크리에이터_채널_라이브_탭 docs/20260704_크리에이터_채널_오디오_탭 SodaLive/Sources/V2/CreatorChannel`
- 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: 빌드 성공.
## 검증 기록
- 2026-07-03: 문서 작성 단계. 구현/빌드 검증은 아직 수행하지 않음.
@@ -365,9 +406,13 @@
- 2026-07-04: Phase 6 리팩토링 완료. `CreatorChannelLiveReplayListItem`을 공용 `CreatorChannelAudioContentListItem`으로 이동/이름 변경하고, 홈 전용 `CreatorChannelAudioContentListItem``CreatorChannelHomeAudioContentListItem`으로 변경함. `rg` 정적 검색, `plutil -lint SodaLive.xcodeproj/project.pbxproj`, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 검증했으며 `** BUILD SUCCEEDED **`를 확인함.
- 2026-07-04: Phase 5 구현 완료. `CreatorChannelView``.live` 탭 분기에 `CreatorChannelLiveTabView`를 연결하고 `creatorId`, `isOwnCreatorChannel`, `showLiveDetail`, `showContentDetail`, `showCreateLive`를 전달함. 기존 `CreatorChannelFloatingActionMenu`는 본인 채널 `home` 탭 조건으로 유지함. `rg` 정적 검색, `plutil -lint SodaLive.xcodeproj/project.pbxproj`, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 검증했으며 `** BUILD SUCCEEDED **`를 확인함.
- 2026-07-04: `ContentSort`에서 소장 정렬 옵션을 제거하고 PRD/계획서의 정렬 옵션을 4개 기준으로 갱신함. `rg` 정적 검색, `plutil -lint SodaLive.xcodeproj/project.pbxproj`, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 검증했으며 `** BUILD SUCCEEDED **`를 확인함.
- 2026-07-04: Phase 7.1 정적 검색 검증 완료. API endpoint, query parameter, 정렬 enum 4개, pagination 기본값, sort-bar, BottomSheet, 현재 라이브 재사용, 공용 다시듣기 item, `ic_new_create_live`, 오디오 상세 이동 연결을 확인함. `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`를 확인함.
- 2026-07-04: Phase 7.1 정적 검색 검증 완료. API endpoint, query parameter, 정렬 enum 4개, pagination 기본값, sort-bar, context popup, 현재 라이브 재사용, 공용 다시듣기 item, `ic_new_create_live`, 오디오 상세 이동 연결을 확인함. `plutil -lint SodaLive.xcodeproj/project.pbxproj``OK`를 확인함.
- 2026-07-04: Phase 7.2 빌드 검증 완료. 최초 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`는 DerivedData `build.db` lock으로 실패했으나, 5초 후 동일 명령 재시도에서 `** BUILD SUCCEEDED **`를 확인함.
- 2026-07-04: Phase 7.3 수동 동작 검증은 blocked. iOS Simulator는 사용 가능하나 실제 로그인 세션, 타인/본인 크리에이터 채널 테스트 데이터, 라이브/다시듣기 응답 데이터, 네트워크 요청 관찰 수단이 없어 CLI 환경에서 PRD Success Criteria 전체를 완료 검증하지 않음.
- 2026-07-04: 리뷰 반영. sort-bar 카운트 표시 요구사항이 제거되어 PRD/계획서를 현재 정렬 표시 기준으로 갱신함.
- 2026-07-04: 리뷰 반영. 본인 채널 라이브 시작하기 CTA를 `CreatorChannelLiveTabView` 내부 scroll content overlay에서 `CreatorChannelView` 최상위 overlay로 이동해 화면 하단 고정 구조로 변경함.
- 2026-07-04: 요구사항 추가 반영. 진행 중인 라이브가 없고 라이브 다시듣기 콘텐츠가 0개인 경우 sort-bar/list를 숨기고 Empty 문구만 표시하도록 PRD/계획/구현을 갱신함.
- 2026-07-04: Figma `290:9034`, `290:9031`을 확인하고 Phase 8을 추가함. sort-bar 전체 개수 표시와 context popup 전환을 후속 변경 범위로 정의함.
- 2026-07-04: Phase 8 구현 완료. `CreatorChannelSortBar``totalCount`를 추가하고 라이브/오디오 탭에서 각각 `liveReplayContentCount`, `audioContentCount`를 전달함. 기존 정렬 타입을 `CreatorChannelSortContextPopup`으로 교체하고 `.sheet` 사용을 overlay popup으로 변경함.
- 2026-07-04: Phase 8 검증 완료. `rg "totalCount|전체|liveReplayContentCount|audioContentCount" ...`, `rg "CreatorChannelSortContextPopup|isSortPopupPresented|Color.gray700|cornerRadius: SodaSpacing.s14|\\.sheet\\(isPresented: \\$isSort" ...`, `git diff --check -- docs/20260703_크리에이터_채널_라이브_탭 docs/20260704_크리에이터_채널_오디오_탭 SodaLive/Sources/V2/CreatorChannel`, `plutil -lint SodaLive.xcodeproj/project.pbxproj`를 확인함. 샌드박스 안 빌드는 Xcode 캐시 권한 문제로 실패했으나, 샌드박스 밖 `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 **`를 확인함.