feat(home): 추천 배너 섹션을 연결한다

This commit is contained in:
Yu Sung
2026-06-29 15:39:23 +09:00
parent c441fb0b02
commit 0e31c69be8
10 changed files with 454 additions and 41 deletions

View File

@@ -242,18 +242,70 @@
### Phase 9: 배너 섹션 UI
- [ ] **Task 9.1: 추천 배너 섹션 작성/보완**
- [x] **Task 9.1: BannerCarousel 현재 구현과 Figma 요구사항 고정**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBannerSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- 확인/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- 작업 내용:
- `banners: List<RecommendationBannerResponse>`를 표시할 Swift 모델을 확정한다.
- 기존 `BannerCarousel`로 충분하면 재사용하고, 홈 전용 wrapper만 작성한다.
- Figma 기준 노드는 `24:5525`로 고정한다.
- `BannerCarousel`이 현재 `TabView(.page)` 기반 단일 페이지 노출 구조인지 확인한다.
- `RecommendationBannerResponse``imageUrl`, `eventItem`, `creatorId`, `seriesId`, `link`가 표시와 이동에 매핑 가능한지 확인한다.
- 배너 이동 규칙은 미확정이면 탭 액션을 임의 구현하지 않는다.
- 검증 기준:
- 실행 명령: `rg "RecommendationBannerResponse|HomeBanner|BannerCarousel|MainHomeBannerSection" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Component/Banner`
- 기대 결과: 배너 응답 모델과 화면 조합이 확인된다.
- 실행 명령: `rg "struct BannerCarousel|BannerCarouselItem|RecommendationBannerResponse|eventItem|creatorId|seriesId|link|imageUrl" SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- 기대 결과: 기존 캐러셀 구조와 배너 응답 모델 필드를 확인할 수 있다.
- [x] **Task 9.2: BannerCarousel을 Figma와 동일한 수평 캐러셀로 보완**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- 작업 내용:
- 배너 width는 `device width - 40`으로 계산한다.
- 배너 이미지 영역은 1:1 비율을 유지한다.
- 배너 사이 간격은 8로 둔다.
- 현재 배너 좌우에 이전/다음 배너가 조금씩 보이도록 수평 inset과 item 배치를 조정한다.
- 배너가 2개 이상이면 무한 스크롤이 가능하도록 첫/마지막 경계에서 자연스럽게 이어지게 처리한다.
- 배너 corner radius는 Figma 기준 `14`를 유지한다.
- Figma처럼 우측 상단에 현재/전체 페이지 인디케이터를 표시한다.
- 인디케이터는 `01 / 20` 형식으로 두 자리 current/total을 표시하고, 배경은 반투명 검정 pill 형태로 둔다.
- 로컬 Figma asset URL(`localhost:3845`)은 앱 코드에 넣지 않는다.
- 검증 기준:
- 실행 명령: `rg "SodaSpacing.s8|SodaSpacing.s14|BannerCarousel|GeometryReader|ScrollView|TabView|01|current|total|localhost:3845" SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- 기대 결과: 간격 8, radius 14, 페이지 인디케이터, 무한/수평 캐러셀 구현 근거가 확인되고 `localhost:3845`는 검색되지 않아야 한다.
- 수동 확인: iPhone 15 기준 현재 배너 폭이 화면 폭보다 40 작고, 좌우에 이전/다음 배너 일부가 보여야 한다.
- 수동 확인: 이미지 영역이 정사각형으로 표시되고 배너 간 간격이 8로 보여야 한다.
- [x] **Task 9.3: 추천 홈 배너 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBannerSection.swift`
- 수정: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 확인/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- 작업 내용:
- `HomeRecommendationResponse.banners``BannerCarouselItem` 배열로 변환한다.
- `eventItem`, `creatorId`, `seriesId`, `link`, `imageUrl` 순서로 안정적인 carousel id를 만든다.
- `imageUrl`이 비어 있는 배너는 이미지 placeholder 정책을 기존 `BannerCarousel` 동작과 맞춘다.
- 데이터가 비어 있으면 배너 섹션 자체를 표시하지 않는다.
- 기존 `MainHomeRecommendationView` 섹션 순서를 유지하면서 현재 라이브 섹션 다음 위치에 배너 섹션을 배치한다.
- 배너 이동 규칙은 미확정이면 탭 액션을 임의 구현하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeBannerSection|BannerCarouselItem|BannerCarousel|banners|RecommendationBannerResponse" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Component/Banner`
- 기대 결과: 추천 응답의 `banners`가 홈 배너 섹션을 통해 `BannerCarousel`에 연결되어야 한다.
- [x] **Task 9.4: 배너 섹션 빌드 및 시각 회귀 검증**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBannerSection.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 확인: `SodaLive.xcodeproj/project.pbxproj`
- 작업 내용:
- 새로 만든 `MainHomeBannerSection.swift`가 빌드 대상에 포함되는지 확인한다.
- 파일 시스템 동기화 방식으로 자동 포함되면 프로젝트 파일을 수정하지 않는다.
- 프로젝트 파일 수정이 필요한 경우에만 최소 변경한다.
- 검증 기준:
- 실행 명령: `plutil -lint SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: `OK`
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`
- 수동 확인: 추천 홈에서 배너가 Figma 노드 `24:5525`처럼 좌우 배너 일부 노출, 간격 8, 1:1 이미지, 우측 상단 인디케이터 형태로 표시되어야 한다.
### Phase 10: 최근 활동 크리에이터 섹션 UI
@@ -444,6 +496,57 @@
## 검증 기록
### 2026-06-29 Phase 9 배너 스펙 변경 반영
- 목적: 변경된 `RecommendationBannerResponse` 백엔드 응답과 배너 1:1 이미지 비율 요구사항을 반영
- 수행 내용:
- `RecommendationBannerResponse`에서 과거 `bannerId`, `type`, `title`, `eventId` 필드를 제거하고 `imageUrl`, `eventItem`, `creatorId`, `seriesId`, `link` 구조로 변경
- 배너 탭 시 `eventItem`, `creatorId`, `seriesId`, `link` 순서로 첫 non-optional 목적지를 찾아 이동하도록 연결
- `eventItem``eventDetail`, `creatorId``creatorDetail`, `seriesId``seriesDetail`, `link`는 외부 URL open 경로를 사용
- `BannerCarousel` 높이를 `itemWidth`와 동일하게 되돌려 이미지 영역 1:1 비율 유지
- `BannerCarousel_Previews`, `MainHomeBannerSection_Previews`, `MainHomeLiveSection_Previews`의 Preview 원격 이미지를 `placehold.co` 대신 JPEG를 반환하는 `picsum.photos/500/500`로 변경
- 검증:
- `rg -n "bannerId|eventId|RecommendationBannerResponse\(|\.type|\.title" SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Main/MainView.swift` 실행, V2 추천 배너 코드에서 과거 응답 필드 참조 제거 확인
- `curl -L -o /dev/null -w "%{http_code} %{content_type}\n" "https://placehold.co/500"` 실행, `200 image/svg+xml` 확인
- `curl -L -o /dev/null -w "%{http_code} %{content_type}\n" "https://picsum.photos/500/500"` 실행, `200 image/jpeg` 확인
- `rg -n "placehold\.co/500" SodaLive/Sources/V2/Main/Home/Recommendation/Components SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift` 실행, 검색 결과 없음 확인
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 실행, `BUILD SUCCEEDED` 확인
- `git diff --check` 실행, 출력 없이 성공 확인
### 2026-06-29 Phase 9 배너 섹션 구현 완료
- 목적: V2 Main Home 추천 탭에 Figma `24:5525` 기준 배너 캐러셀과 추천 배너 섹션을 연결
- 수행 내용:
- `BannerCarousel``device width - 40` 폭의 1:1 배너, 간격 8, radius 14, 좌우 인접 배너 일부 노출, 우측 상단 `01 / NN` pill 인디케이터 구조로 보완
- 배너가 2개 이상이면 첫/마지막에 복제 아이템을 두어 경계에서 이어지는 수평 캐러셀 동작을 구현
- `MainHomeBannerSection`을 추가해 `RecommendationBannerResponse.imageUrl``BannerCarouselItem`으로 매핑하고, 원본 배너를 상위 탭 콜백으로 전달하며, 데이터가 비어 있으면 섹션을 렌더링하지 않도록 구성
- `MainHomeRecommendationView`에서 현재 라이브 섹션 다음에 배너 섹션을 배치
- 배너 이동 규칙은 미확정 상태로 유지하고 탭 액션을 새로 구현하지 않음
- 리뷰어 지적에 따라 무한 캐러셀 경계 reset은 animation 비활성 transaction으로 처리하고, 인디케이터 trailing 위치를 현재 배너 내부 기준으로 보정
- 신규 `MainHomeBannerSection.swift``SodaLive.xcodeproj/project.pbxproj` 빌드 대상에 포함
- 검증:
- Figma `24:5525` 디자인 컨텍스트와 스크린샷 확인, 3개 수평 배너/간격 8/radius 14/우측 상단 `01 / 20` 인디케이터 확인
- `rg "SodaSpacing\.s8|SodaSpacing\.s14|BannerCarousel|GeometryReader|ScrollView|TabView|01|current|total|localhost:3845" SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift` 실행, 간격/radius/수평 캐러셀/페이지 인디케이터 근거 확인 및 `localhost:3845` 미포함 확인
- `rg "MainHomeBannerSection|BannerCarouselItem|BannerCarousel|banners|RecommendationBannerResponse" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Component/Banner` 실행, 추천 응답 `banners`가 배너 섹션과 `BannerCarousel`에 연결됨 확인
- `plutil -lint SodaLive.xcodeproj/project.pbxproj` 실행, `OK` 확인
- `git diff --check` 실행, 출력 없이 성공 확인
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 실행, `BUILD SUCCEEDED` 확인
- 전용 테스트 타깃이 검색되지 않아 새 테스트 인프라를 추가하지 않고 계획서의 정적/빌드/수동 시각 기준으로 검증
### 2026-06-29 Phase 9 배너 섹션 Task 세분화
- 목적: `BannerCarousel`을 Figma `24:5525` 기준으로 구현할 수 있도록 Phase 9 작업을 실행 가능한 단위로 세분화
- 수행 내용:
- 기존 단일 `Task 9.1``Task 9.1`~`Task 9.4`로 분리
- `BannerCarousel` 현재 구조 확인, Figma 레이아웃 보완, 추천 홈 배너 섹션 연결, 빌드/시각 검증 단계로 분리
- 무한 스크롤, 좌우 배너 일부 노출, 배너 간격 8, 이미지 1:1 비율, 배너 width `device width - 40`, 우측 상단 페이지 인디케이터 요구사항 반영
- Figma 컨텍스트에서 확인한 radius 14와 `01 / 20` 형태 인디케이터를 구현 요구사항에 반영
- 검증:
- Figma `24:5525` 디자인 컨텍스트와 스크린샷 확인, 수평 배너 3개 구성과 우측 상단 `01 / 20` 인디케이터 확인
- `rg "Task 9\\.[1-4]|device width - 40|1:1|간격은 8|무한 스크롤|24:5525|01 / 20" docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, Phase 9 세부 Task와 핵심 배너 요구사항 반영 확인
- `rg "### Phase [0-9]+:|- \\[[ x]\\] \\*\\*Task [0-9]+\\.[0-9]+:" docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, Phase/Task 형식 확인
- `git diff --check -- docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 출력 없이 성공 확인
### 2026-06-29 Phase 8.1 현재 라이브 섹션 구현 완료
- 목적: V2 Main Home 추천 탭에 현재 라이브 섹션을 API 응답 데이터 기반으로 표시
@@ -469,7 +572,7 @@
- 수행 내용:
- PRD 응답 스펙에서 `?`가 없는 필드를 Swift non-optional로 변경
- `targetId`, `coverImage`, `profileImage`, `originalWorkTitle`, `creatorProfileImage`, `imageUrl`, `audioUrl` 등 PRD nullable 필드는 optional 유지
- `RecommendationBannerResponse`상세 PRD 스펙이 별도로 확정되지 않아 기존 optional 선언 유지
- `RecommendationBannerResponse`백엔드 변경 스펙에 맞춰 `imageUrl`, `eventItem`, `creatorId`, `seriesId`, `link` 기준으로 정렬
- `HomeLiveItem.creatorNickname`, `creatorProfileImage`가 non-optional이 되면서 라이브 섹션의 불필요한 nil fallback 제거
- 검증:
- `rg -n "let (roomId|activityType|activityAt|contentId|isPointAvailable|characterId|totalChatCount|genreName|creators|postId|createdAt|likeCount|commentCount|existOrdered): .*\\?" SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift` 실행, 검색 결과 없음 확인 예정
@@ -512,7 +615,7 @@
- 목적: Phase 2.2 리뷰에서 지적된 과거 추정 응답 필드 혼재를 제거하고, 추천 전용 request 모델 위치를 `Recommendation/Models` 구조로 일치
- 수행 내용:
- `MainHomeRecommendationResponse.swift`에서 PRD 응답 스펙에 없는 legacy/추정 필드(`liveRoomId`, `profileImageUrl`, `thumbnailUrl`, `coverImageUrl`, `releaseDate`, `originalTitle` 등) 제거
- 배너는 상세 PRD 스펙이 별도로 없으므로 기존 표시/이동 판단에 필요한 `RecommendationBannerResponse` 최소 필드 유지
- 배너는 변경된 백엔드 스펙에 맞춰 `eventItem`, `creatorId`, `seriesId`, `link` 중 첫 non-optional 값으로 이동 판단
- `FollowRecommendedCreatorsRequest.swift``SodaLive/Sources/V2/Main/Home/Recommendation/Models` 아래로 이동
- `SodaLive.xcodeproj/project.pbxproj`의 request/model/API/Repository 참조 경로 갱신
- 검증:

View File

@@ -120,6 +120,25 @@ data class HomeGenreCreatorGroupItem(
val creators: List<HomeCreatorItem>
)
#### RecommendationBannerResponse
```kotlin
data class RecommendationBannerResponse(
val imageUrl: String,
val eventItem: EventItem?,
val creatorId: Long?,
val seriesId: Long?,
val link: String?
)
```
배너 탭 이동은 `eventItem`, `creatorId`, `seriesId`, `link` 순서로 첫 non-null 값을 사용한다.
- `eventItem` -> 이벤트 상세
- `creatorId` -> 크리에이터 상세
- `seriesId` -> 시리즈 상세
- `link` -> 외부 URL 열기
data class HomePopularCommunityPostItem(
val postId: Long,
val creatorId: Long,
@@ -312,7 +331,7 @@ MainHomeView
- 빌드가 성공하고, 가능하면 ViewModel 디코딩/상태 전환 검증을 수행한다.
## 11. Open Questions
- `RecommendationBannerResponse`의 필드 목록과 배너 `type`별 이동 규칙 확인이 필요하다.
- `RecommendationBannerResponse`는 변경된 백엔드 스펙 기준으로 `eventItem`, `creatorId`, `seriesId`, `link` 순서의 이동 규칙을 사용한다.
- 각 섹션별 최대 표시 개수와 가로/세로 스크롤 정책이 Figma 기준 그대로인지, 서버 응답 전체를 모두 표시해야 하는지 확인이 필요하다.
- `HomeFirstAudioContentItem`에서 오리지널 작품 여부와 크리에이터의 첫 번째 콘텐츠 여부를 판단할 서버 필드가 추가되는지 확인이 필요하다.
- `HomeLiveItem`에 민감/성인 라이브 여부를 판단할 `isAdult` 또는 동등한 필드가 추가되는지, 아니면 서버에서 추천 라이브를 사전 필터링하는지 확인이 필요하다.