feat(main): 콘텐츠 탭 타이틀바를 정리한다

This commit is contained in:
Yu Sung
2026-07-08 03:45:32 +09:00
parent c0369e6f58
commit 403eee262f
8 changed files with 58 additions and 7 deletions

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "ic_bar_storage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

View File

@@ -3,7 +3,7 @@ import SwiftUI
struct MainContentView: View { struct MainContentView: View {
let onTapCanCharge: () -> Void let onTapCanCharge: () -> Void
let onTapSearch: () -> Void let onTapSearch: () -> Void
let onTapNotificationList: () -> Void let onTapStorage: () -> Void
let onTapContent: (Int) -> Void let onTapContent: (Int) -> Void
let onTapBanner: (AudioBannerResponse) -> Void let onTapBanner: (AudioBannerResponse) -> Void
let onTapSeries: (Int) -> Void let onTapSeries: (Int) -> Void
@@ -14,7 +14,7 @@ struct MainContentView: View {
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
HomeTitleBar { DefaultTitleBar(title: MainTab.content.title) {
HStack(spacing: SodaSpacing.s14) { HStack(spacing: SodaSpacing.s14) {
Image("ic_bar_cash") Image("ic_bar_cash")
.onTapGesture { onTapCanCharge() } .onTapGesture { onTapCanCharge() }
@@ -22,8 +22,8 @@ struct MainContentView: View {
Image("ic_bar_search") Image("ic_bar_search")
.onTapGesture { onTapSearch() } .onTapGesture { onTapSearch() }
Image("ic_bar_bell") Image("ic_bar_storage")
.onTapGesture { onTapNotificationList() } .onTapGesture { onTapStorage() }
} }
} }
@@ -75,7 +75,7 @@ struct MainContentView_Previews: PreviewProvider {
MainContentView( MainContentView(
onTapCanCharge: {}, onTapCanCharge: {},
onTapSearch: {}, onTapSearch: {},
onTapNotificationList: {}, onTapStorage: {},
onTapContent: { _ in }, onTapContent: { _ in },
onTapBanner: { _ in }, onTapBanner: { _ in },
onTapSeries: { _ in }, onTapSeries: { _ in },

View File

@@ -175,7 +175,7 @@ struct MainView: View {
MainContentView( MainContentView(
onTapCanCharge: handleHomeCanChargeTap, onTapCanCharge: handleHomeCanChargeTap,
onTapSearch: handleHomeSearchTap, onTapSearch: handleHomeSearchTap,
onTapNotificationList: handleHomeNotificationListTap, onTapStorage: handleContentStorageTap,
onTapContent: handleRecommendationContentTap, onTapContent: handleRecommendationContentTap,
onTapBanner: handleContentAudioBannerTap, onTapBanner: handleContentAudioBannerTap,
onTapSeries: handleContentOriginalSeriesTap, onTapSeries: handleContentOriginalSeriesTap,
@@ -583,6 +583,10 @@ struct MainView: View {
} }
} }
private func handleContentStorageTap() {
AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))
}
private func handleContentOriginalSeriesTap(seriesId: Int) { private func handleContentOriginalSeriesTap(seriesId: Int) {
guard seriesId > 0 else { return } guard seriesId > 0 else { return }
performRecommendationDetailAction { performRecommendationDetailAction {

View File

@@ -8,6 +8,7 @@
- 문서 정책: `docs/agent-guides/documentation-policy.md` - 문서 정책: `docs/agent-guides/documentation-policy.md`
- Figma 전체 화면: `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=24-6737&m=dev` - Figma 전체 화면: `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=24-6737&m=dev`
- Figma 전체 화면 node: `24:6737` - Figma 전체 화면 node: `24:6737`
- Figma title bar: `24:6738`
- Figma 배너: `24:6741` - Figma 배너: `24:6741`
- Figma 새로 올라온 오디오: `24:6751` - Figma 새로 올라온 오디오: `24:6751`
- Figma New&Hot: `24:6758` - Figma New&Hot: `24:6758`
@@ -24,6 +25,7 @@
- 메인 하단 `콘텐츠` 탭 안에 내부 `추천` 탭 화면을 구현한다. - 메인 하단 `콘텐츠` 탭 안에 내부 `추천` 탭 화면을 구현한다.
- API endpoint는 `GET /api/v2/audio/recommendations`를 사용한다. - API endpoint는 `GET /api/v2/audio/recommendations`를 사용한다.
- title bar와 내부 tab bar는 고정하고, 배너부터 하위 콘텐츠만 세로 스크롤한다. - title bar와 내부 tab bar는 고정하고, 배너부터 하위 콘텐츠만 세로 스크롤한다.
- title bar는 Figma `24:6738` 기준으로 좌측에 메인 탭명 `콘텐츠`, 우측에 `cash`/`search`/`storage` 액션을 표시한다.
- Figma에 보이는 섹션별로 Phase를 분리한다. - Figma에 보이는 섹션별로 Phase를 분리한다.
- 이번 범위에서 `추천 시리즈`, `키워드의 오디오`는 구현하지 않는다. - 이번 범위에서 `추천 시리즈`, `키워드의 오디오`는 구현하지 않는다.
- `V2` 패키지 하위에 구현된 재사용 가능한 위젯 후보를 먼저 확인하고 필요한 경우 최소 보완한다. - `V2` 패키지 하위에 구현된 재사용 가능한 위젯 후보를 먼저 확인하고 필요한 경우 최소 보완한다.
@@ -62,6 +64,7 @@
- 여러 화면에서 재사용 근거가 있는 UI만 `SodaLive/Sources/V2/Component/**`로 승격한다. - 여러 화면에서 재사용 근거가 있는 UI만 `SodaLive/Sources/V2/Component/**`로 승격한다.
- `MainView``.content` 탭에서 신규 콘텐츠 탭 root를 보여주고, 상세 진입 guard callback만 전달한다. - `MainView``.content` 탭에서 신규 콘텐츠 탭 root를 보여주고, 상세 진입 guard callback만 전달한다.
- 콘텐츠 탭 root는 title bar와 내부 tab bar를 고정 영역으로 두고, 추천 탭 body 내부에서만 `ScrollView`를 사용한다. - 콘텐츠 탭 root는 title bar와 내부 tab bar를 고정 영역으로 두고, 추천 탭 body 내부에서만 `ScrollView`를 사용한다.
- 콘텐츠 탭 root의 title bar는 `DefaultTitleBar(title: MainTab.content.title)`를 사용하고, 홈 탭은 기존 `HomeTitleBar`를 유지한다.
- 섹션 배열이 비어 있으면 해당 섹션을 숨긴다. - 섹션 배열이 비어 있으면 해당 섹션을 숨긴다.
- 전체 응답이 비어 있거나 API 실패/디코딩 실패 시 최소 empty 또는 error 상태를 표시한다. - 전체 응답이 비어 있거나 API 실패/디코딩 실패 시 최소 empty 또는 error 상태를 표시한다.
- 오디오 상세 진입은 기존 `handleRecommendationContentTap(contentId:)`의 로그인/성인 콘텐츠 guard 흐름을 재사용한다. - 오디오 상세 진입은 기존 `handleRecommendationContentTap(contentId:)`의 로그인/성인 콘텐츠 guard 흐름을 재사용한다.
@@ -93,8 +96,10 @@
## 재사용 가능한 V2 위젯 후보 ## 재사용 가능한 V2 위젯 후보
- `SodaLive/Sources/V2/Component/DefaultTitleBar.swift`
- 콘텐츠 탭 title bar의 좌측 고정 title과 우측 액션 영역 기준 후보.
- `SodaLive/Sources/V2/Component/HomeTitleBar.swift` - `SodaLive/Sources/V2/Component/HomeTitleBar.swift`
- 콘텐츠 탭 title bar와 우측 액션 영역의 기준 후보. - 탭 title bar 기준 후보이며, 콘텐츠 탭에는 사용하지 않는다.
- `SodaLive/Sources/V2/Component/TextTabBar.swift` - `SodaLive/Sources/V2/Component/TextTabBar.swift`
- 내부 `추천` 탭 bar 구성 후보. - 내부 `추천` 탭 bar 구성 후보.
- `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift` - `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
@@ -635,3 +640,14 @@
- 새로 올라온 오디오, 무료 오디오, 포인트 오디오, 추천 오디오 섹션이 `AudioContentThumbnailCard`를 사용하도록 정리했다. - 새로 올라온 오디오, 무료 오디오, 포인트 오디오, 추천 오디오 섹션이 `AudioContentThumbnailCard`를 사용하도록 정리했다.
- 기존 `MainContentAudioTagOverlay.swift`를 삭제하고 신규 공용 card 파일을 Xcode project에 추가했다. - 기존 `MainContentAudioTagOverlay.swift`를 삭제하고 신규 공용 card 파일을 Xcode project에 추가했다.
- `plutil``OK`, `xcodebuild``BUILD SUCCEEDED`로 통과했다. - `plutil``OK`, `xcodebuild``BUILD SUCCEEDED`로 통과했다.
- 2026-07-08 콘텐츠 탭 title bar Figma 반영:
- 실행/확인:
- Figma node `24:6738`
- `SodaLive/Sources/V2/Main/Content/MainContentView.swift`
- `SodaLive/Sources/V2/Main/MainView.swift`
- `SodaLive/Sources/V2/Component/DefaultTitleBar.swift`
- `SodaLive/Sources/V2/Component/HomeTitleBar.swift`
- 결과:
- 콘텐츠 탭 title bar를 `DefaultTitleBar(title: MainTab.content.title)`로 변경해 좌측에 메인 탭명 `콘텐츠`를 고정 표시하도록 반영했다.
- 우측 액션은 `ic_bar_cash`, `ic_bar_search`, `ic_bar_storage` 순서로 정리하고, `ic_bar_storage``MainContentView.onTapStorage``MainView.handleContentStorageTap`을 통해 `.myBox(currentTab: .orderlist)`로 이동하도록 연결했다.
- 홈 탭은 기존 `HomeTitleBar``ic_bar_bell` 동작을 유지한다.

View File

@@ -7,6 +7,7 @@
Figma 참조: Figma 참조:
- 전체 화면: `24:6737`, `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=24-6737&m=dev` - 전체 화면: `24:6737`, `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=24-6737&m=dev`
- title bar: `24:6738`
- 배너: `24:6741` - 배너: `24:6741`
- 새로 올라온 오디오: `24:6751` - 새로 올라온 오디오: `24:6751`
- New&Hot: `24:6758` - New&Hot: `24:6758`
@@ -85,6 +86,8 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다
### 7.3 Fixed shell and scrolling ### 7.3 Fixed shell and scrolling
- 콘텐츠 탭 title bar는 화면 상단 고정 영역이다. - 콘텐츠 탭 title bar는 화면 상단 고정 영역이다.
- 콘텐츠 탭 title bar 좌측은 홈 로고나 내부 탭명이 아니라 메인 탭명 `콘텐츠`를 고정 표시한다.
- 콘텐츠 탭 title bar 우측은 Figma `24:6738` 기준 `cash`, `search`, `storage` 액션 아이콘을 표시하고 알림 bell 아이콘은 사용하지 않는다.
- 내부 tab bar도 title bar 아래 고정 영역이다. - 내부 tab bar도 title bar 아래 고정 영역이다.
- Figma `24:6741` 배너부터 하위 콘텐츠만 세로 `ScrollView` 안에 둔다. - Figma `24:6741` 배너부터 하위 콘텐츠만 세로 `ScrollView` 안에 둔다.
- 하단 main tab bar와 mini player 영역은 기존 `MainView` 구조를 유지한다. - 하단 main tab bar와 mini player 영역은 기존 `MainView` 구조를 유지한다.
@@ -119,6 +122,7 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다
- `오직 보이스온에서만!``ONLY` 표시는 `ic_series_original` + `img_new_only` asset 조합을 사용한다. - `오직 보이스온에서만!``ONLY` 표시는 `ic_series_original` + `img_new_only` asset 조합을 사용한다.
### 7.8 Navigation ### 7.8 Navigation
- title bar의 storage 아이콘 탭은 기존 보관함 진입 경로인 `AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))`를 사용한다.
- 오디오 item 탭은 `audioContentId`로 기존 content detail 진입 흐름을 사용한다. - 오디오 item 탭은 `audioContentId`로 기존 content detail 진입 흐름을 사용한다.
- 배너 탭은 `eventItem`, `creatorId`, `seriesId`, `link`를 명시적으로 분기한다. - 배너 탭은 `eventItem`, `creatorId`, `seriesId`, `link`를 명시적으로 분기한다.
- `eventItem`은 기존 event detail 흐름이 있으면 재사용한다. - `eventItem`은 기존 event detail 흐름이 있으면 재사용한다.
@@ -152,6 +156,7 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다
## 10. Success Criteria ## 10. Success Criteria
- `.content` 탭에서 placeholder 대신 콘텐츠 탭 root가 표시된다. - `.content` 탭에서 placeholder 대신 콘텐츠 탭 root가 표시된다.
- 콘텐츠 탭 내부 `추천` 탭 진입 시 `GET /api/v2/audio/recommendations` 호출이 발생한다. - 콘텐츠 탭 내부 `추천` 탭 진입 시 `GET /api/v2/audio/recommendations` 호출이 발생한다.
- 콘텐츠 탭 title bar는 좌측 고정 title `콘텐츠`와 우측 `cash`/`search`/`storage` 액션을 표시하고, storage 탭 시 보관함으로 이동한다.
- title bar와 내부 tab bar는 고정되고, 배너부터 하위 섹션만 세로 스크롤된다. - title bar와 내부 tab bar는 고정되고, 배너부터 하위 섹션만 세로 스크롤된다.
- API 응답의 구현 대상 배열이 각 Figma 섹션에 매핑된다. - API 응답의 구현 대상 배열이 각 Figma 섹션에 매핑된다.
- `추천 시리즈`, `#키워드의 오디오` 섹션은 구현되지 않는다. - `추천 시리즈`, `#키워드의 오디오` 섹션은 구현되지 않는다.
@@ -170,3 +175,4 @@ Swift 모델은 기존 V2 관례에 맞춰 `Decodable` 구조체로 선언한다
- 2026-07-05: `MainView`, `MainHomeView`, `AudioContentCard`, `BannerCarousel`, `CreatorChannelAudioContentListItem`, `CreatorChannelSeriesListItem`을 확인해 재사용 후보를 정리했다. - 2026-07-05: `MainView`, `MainHomeView`, `AudioContentCard`, `BannerCarousel`, `CreatorChannelAudioContentListItem`, `CreatorChannelSeriesListItem`을 확인해 재사용 후보를 정리했다.
- 2026-07-05: 최초 문서 작성 시 `plan-task.md`만 작성되어 신규 기능 문서 정책과 맞지 않았다. 본 PRD를 추가해 정책 누락을 보완한다. - 2026-07-05: 최초 문서 작성 시 `plan-task.md`만 작성되어 신규 기능 문서 정책과 맞지 않았다. 본 PRD를 추가해 정책 누락을 보완한다.
- 2026-07-05: 사용자 확인으로 댓글 preview bubble에는 nickname을 표시하지 않고, 배너 `link`는 deep link 우선 후 외부 브라우저로 열며, `seriesId``seriesDetail` route로 이동하도록 확정했다. - 2026-07-05: 사용자 확인으로 댓글 preview bubble에는 nickname을 표시하지 않고, 배너 `link`는 deep link 우선 후 외부 브라우저로 열며, `seriesId``seriesDetail` route로 이동하도록 확정했다.
- 2026-07-08: Figma `24:6738` 기준으로 콘텐츠 탭 title bar가 홈 탭 로고/bell 구성이 아니라 좌측 고정 title `콘텐츠`와 우측 `ic_bar_cash`/`ic_bar_search`/`ic_bar_storage` 구성을 사용하도록 요구사항을 보완했다.

View File

@@ -648,6 +648,9 @@ Expected: 현재 저장소 상태상 테스트 번들 타깃이 없으면 `Schem
- 무엇/왜/어떻게: 구현 후 코드 리뷰를 요청해 요구사항 누락과 품질 이슈를 점검했다. - 무엇/왜/어떻게: 구현 후 코드 리뷰를 요청해 요구사항 누락과 품질 이슈를 점검했다.
- 실행 명령/도구: `task(category="deep")` 코드 리뷰 및 follow-up 검토 - 실행 명령/도구: `task(category="deep")` 코드 리뷰 및 follow-up 검토
- 결과: 현재 범위는 `홈/콘텐츠/채팅/마이``홈/콘텐츠/채팅`이 placeholder이므로 Bootpay 인증 트리거가 없는 것은 blocking issue가 아니며, 향후 실제 라이브/성인 콘텐츠 진입 UI 연결 시 `isShowAuthConfirmView`/`pendingAction` 경로를 연결해야 한다는 non-blocking future integration note로 정리했다. - 결과: 현재 범위는 `홈/콘텐츠/채팅/마이``홈/콘텐츠/채팅`이 placeholder이므로 Bootpay 인증 트리거가 없는 것은 blocking issue가 아니며, 향후 실제 라이브/성인 콘텐츠 진입 UI 연결 시 `isShowAuthConfirmView`/`pendingAction` 경로를 연결해야 한다는 non-blocking future integration note로 정리했다.
- 2026-07-08 메인 콘텐츠 탭 title bar 요구사항 반영: Figma node `24:6738` 기준으로 콘텐츠 탭 상단은 홈 탭과 같은 좌측 로고/우측 알림 버튼 구성이 아니라, 좌측에 메인 탭명 `콘텐츠`를 고정 표시하고 우측에는 `ic_bar_cash`, `ic_bar_search`, `ic_bar_storage`를 표시하도록 수정했다.
- 2026-07-08 메인 콘텐츠 탭 보관함 이동 반영: `ic_bar_storage` 터치 시 기존 보관함 진입 경로인 `AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist))`로 이동하도록 `MainContentView``onTapStorage` 콜백을 추가하고 `MainView`에서 연결했다. 홈 탭의 `HomeTitleBar`/`ic_bar_bell` 동작은 유지한다.
- 2026-07-08 검증: Figma `24:6738` design context와 screenshot을 확인했고, `rg`/`ast-grep`로 콘텐츠 탭은 `DefaultTitleBar(title: MainTab.content.title)``ic_bar_storage`, 홈 탭은 기존 `HomeTitleBar``ic_bar_bell`을 사용하는 것을 확인했다. 구현 직후 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -destination 'generic/platform=iOS Simulator' -configuration Debug build` 실행 결과 빌드 성공을 확인했다.
## 커밋 정책 ## 커밋 정책

View File

@@ -131,3 +131,4 @@
- 2026-05-19 추가 구현: Figma node `20:3590` 기준 캡슐형 탭 바를 `SodaLive/Sources/V2/Component/CapsuleTabBar.swift`로 추가했다. 항목 수 제한 없이 `ScrollView(.horizontal, showsIndicators: false)``LazyHStack`으로 가로 스크롤되도록 구현했고, 선택 상태는 외부 `Binding`으로 관리한다. - 2026-05-19 추가 구현: Figma node `20:3590` 기준 캡슐형 탭 바를 `SodaLive/Sources/V2/Component/CapsuleTabBar.swift`로 추가했다. 항목 수 제한 없이 `ScrollView(.horizontal, showsIndicators: false)``LazyHStack`으로 가로 스크롤되도록 구현했고, 선택 상태는 외부 `Binding`으로 관리한다.
- 2026-05-19 추가 구현: `SodaLive.xcodeproj/project.pbxproj``CapsuleTabBar.swift``SodaLive`, `SodaLive-dev` 두 앱 타깃 Sources에 등록했다. - 2026-05-19 추가 구현: `SodaLive.xcodeproj/project.pbxproj``CapsuleTabBar.swift``SodaLive`, `SodaLive-dev` 두 앱 타깃 Sources에 등록했다.
- 2026-05-19 빌드 검증: `xcodebuild -workspace 'SodaLive.xcworkspace' -scheme 'SodaLive-dev' -configuration Debug build` 실행 결과 `** BUILD SUCCEEDED **`. - 2026-05-19 빌드 검증: `xcodebuild -workspace 'SodaLive.xcworkspace' -scheme 'SodaLive-dev' -configuration Debug build` 실행 결과 `** BUILD SUCCEEDED **`.
- 2026-07-08 메인 콘텐츠 탭 title bar 반영: Figma node `24:6738` 기준으로 `DefaultTitleBar(title:)`가 좌측 화면명 텍스트와 우측 메뉴 슬롯을 재사용할 수 있음을 확인했고, `SodaLive/Sources/V2/Main/Content/MainContentView.swift`에서 `HomeTitleBar` 대신 `DefaultTitleBar(title: MainTab.content.title)`를 사용하도록 반영했다. `HomeTitleBar`는 홈 탭 로고형 title bar 책임을 유지한다.