chore(home): 추천 홈 기준을 정리한다

This commit is contained in:
Yu Sung
2026-06-26 19:23:53 +09:00
parent 8d6e32f5fb
commit 6b5176eba7
4 changed files with 626 additions and 520 deletions

View File

@@ -1,319 +1,388 @@
# 메인 홈 추천 UI와 API 연동 구현 계획
# 메인 홈 추천 UI와 API 연동 구현 계획
## 기준 문서
- PRD: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md`
- Figma 추천 화면: `24:5514`
- Figma 모두 팔로우 완료 버튼: `24:9092`
- Figma 커뮤니티 카드:
- Text Only: `446:9688`
- Text + Img, 유료 + 구매하지 않음: `446:9690`
- Text + Img, 유료/무료 + 구매함: `446:9691`
- Figma FeedCommunityView 유료 미구매: `309:19774`
- Figma FeedCommunityView 유료 구매함 또는 무료: `309:19775`
- 코드 스타일: `docs/agent-guides/code-style.md`
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
## 작업 원칙
## 재분류 원칙
- PRD의 성공 기준과 제외 범위를 벗어나지 않는다.
- `추천 필모그래피`, `또 다른 모습` 섹션은 만들지 않는다.
- 기존 `SodaLive/Sources/Home/HomeApi.swift`에는 추천 API를 추가하지 않는다.
- 신규 페이지 루트, ViewModel, Repository, API, Models, MainHome 전용 컴포넌트는 `SodaLive/Sources/V2/Main/Home/**` 아래에 작성한다.
- 여러 페이지에서 재사용 가능한 UI widget은 `SodaLive/Sources/V2/Component/**` 아래에 형태별 폴더로 작성한다.
- 2026-06-26 기준으로 기존 Phase와 완료 체크 상태는 구현 순서 판단에 사용하지 않는다.
- 이전 검증 기록은 하단에 보존하되, 새 체크리스트는 신규 PRD 요구사항 기준으로 다시 작성한다.
- 화면 UI는 섹션당 하나의 Phase로 나누어 작게 작업한다.
- `MainHomeView`는 홈 상단 공통 shell과 추천/랭킹/팔로잉 탭 전환만 담당한다.
- 추천/랭킹/팔로잉 탭 아래 콘텐츠는 각각 `Recommendation`, `Ranking`, `Following` 하위 폴더에 별도 View로 배치한다.
- 기존에 이미 생성된 widget과 파일이 있으면 새로 만들지 않고 확인/보완한다.
- `추천 필모그래피`, `또 다른 모습`, 사업자 정보 외 추가 하단 정보 섹션은 만들지 않는다.
- 기존 `SodaLive/Sources/Home/HomeApi.swift`에는 `/api/v2/home/recommendations`를 추가하지 않는다.
- 외부 라이브러리는 추가하지 않는다.
- Figma localhost asset URL은 앱 코드에 사용하지 않는다.
- 서버/기획 확인이 필요한 이동 규칙과 최대 표시 개수는 임의 확장하지 않고, 현재 화면 렌더링과 API 연동에 필요한 최소 동작만 구현한다.
- 수정 후 검증 기록은 이 문서 하단에 누적한다.
## 구현 대상 파일
## 주요 대상 파일
### 생성
### 확인/보완
- `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- `MainView` 홈 탭에서 표시할 실제 홈 추천 페이지 루트.
- `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 추천 API 로딩/에러/섹션 데이터/모두 팔로우 완료 상태 관리.
- `SodaLive/Sources/V2/Main/Home/Repository/MainHomeApi.swift`
- `/api/v2/home/recommendations`, `/api/v2/home/recommendations/creators/follow` 전용 Moya `TargetType`.
- `SodaLive/Sources/V2/Main/Home/Repository/MainHomeRepository.swift`
- `MainHomeApi` 호출을 `AnyPublisher<Response, MoyaError>`로 제공.
- `SodaLive/Sources/V2/Main/Home/Models/MainHomeRecommendationResponse.swift`
- `HomeRecommendationResponse`와 하위 응답 모델.
- `SodaLive/Sources/V2/Main/Home/Models/FollowRecommendedCreatorsRequest.swift`
- 모두 팔로우 요청 모델.
- `SodaLive/Sources/V2/Main/Home/Models/RecommendedActivityType.swift`
- 서버 활동 타입 enum 변환과 I18n 표시.
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeLiveSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeLiveItem.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeActiveCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeActiveCreatorItem.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeRecentDebutCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeFirstAudioContentSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeAiCharacterSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeGenreCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeCheerCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeCreatorGroupSection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeCreatorGrid.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomePopularCommunitySection.swift`
- `SodaLive/Sources/V2/Main/Home/Components/MainHomeBusinessInfoSection.swift`
- `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- `SodaLive/Sources/V2/Component/Card/AiCharacterCard.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Models/RecommendedActivityType.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationRepository.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift`
- `SodaLive/Sources/V2/Component/AudioContentCard.swift`
- `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`
- `SodaLive/Sources/V2/Component/Creator/CreatorProfileGrid.swift`
- `SodaLive/Sources/V2/Component/Creator/CreatorProfileItem.swift`
- `SodaLive/Sources/V2/Component/Button/FollowAllButton.swift`
- `SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift`
### 수정
- `SodaLive/Sources/V2/Main/MainView.swift`
- `.home` 탭에서 `MainPlaceholderTabView` 대신 `MainHomeView` 표시.
- `SodaLive/Sources/I18n/I18n.swift`
- 홈 추천 섹션 제목, 활동 타입, 모두 팔로우 문구, 더보기/접기 문구 추가.
- `SodaLive.xcodeproj/project.pbxproj`
- 신규 Swift 파일이 Xcode 빌드 대상에 포함되지 않는 경우에만 수정.
- `docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md`
- 체크리스트와 검증 기록 유지.
### 생성 후보
없는 경우에만 생성한다.
- `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeLiveSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBannerSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeActiveCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeRecentDebutCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeFirstAudioContentSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeAiCharacterSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeGenreCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeCheerCreatorSection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomePopularCommunitySection.swift`
- `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBusinessInfoSection.swift`
- `SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift`
- `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
## TASK 체크리스트
### Phase 1: 구현 기준과 데이터 계층 준비
### Phase 1: 기준 스펙과 기존 구현 점검
- [x] **Task 1.1: 구현 전 구조 확인**
- [ ] **Task 1.1: 문서/코드 기준점 확인**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/Main/MainView.swift`
- 확인: `SodaLive/Sources/V2/Component/SectionTitle.swift`
- 확인: `SodaLive/Sources/V2/Component/AudioContentCard.swift`
- 확인: `SodaLive/Sources/V2/Component/HomeTitleBar.swift`
- 확인: `SodaLive/Sources/V2/Main/MainTabBarView.swift`
- 확인: `SodaLive/Sources/Home/HomeApi.swift`
- 확인: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- `.home` 탭이 현재 `MainPlaceholderTabView`를 표시하는지 확인한다.
- 기존 V2 공용 컴포넌트 재사용 범위를 확인한다.
- 기존 `HomeApi.swift`를 수정하지 않는 것을 기준으로 신규 `MainHomeApi` 위치를 확정한다.
- `I18n.swift`의 기존 enum 배치 방식과 `pick(ko:en:ja:)` 사용 방식을 확인한다.
- 검증 기준:
- 실행 명령: `rg "case \\.home|MainPlaceholderTabView|enum HomeApi|enum I18n" SodaLive/Sources/V2/Main SodaLive/Sources/Home SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: `.home` placeholder 위치, 기존 `HomeApi`, `I18n` 구조를 확인할 수 있다.
- 수동 확인: 신규 추천 API를 기존 `HomeApi.swift`에 추가하지 않아야 한다.
- [x] **Task 1.2: 추천 API 모델 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Main/Home/Models/MainHomeRecommendationResponse.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Models/FollowRecommendedCreatorsRequest.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Models/RecommendedActivityType.swift`
- 작업 내용:
- `MainHomeRecommendationResponse.swift``HomeRecommendationResponse`와 하위 응답 모델을 `Decodable`로 작성한다.
- 서버 nullable 필드는 Swift optional로 선언한다.
- 서버 Long 값은 우선 기존 앱 라우팅 관례와 맞추기 쉬운 `Int`로 작성하고, 빌드 또는 호출부에서 `Int64`가 필요하면 보정한다.
- `FollowRecommendedCreatorsRequest``creatorIds: [Int]?`를 갖는 `Encodable`로 작성한다.
- `RecommendedActivityType``LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY`, unknown 처리를 제공한다.
- 검증 기준:
- 실행 명령: `rg "struct HomeRecommendationResponse|struct FollowRecommendedCreatorsRequest|enum RecommendedActivityType" SodaLive/Sources/V2/Main/Home/Models`
- 기대 결과: 세 모델/enum 정의가 검색된다.
- 수동 확인: PRD의 응답 필드명이 Swift 모델에 누락 없이 반영되어야 한다.
- [x] **Task 1.3: I18n 문구 추가**
- 대상 파일:
- 수정: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- `I18n.HomeRecommendation` enum을 추가한다.
- 활동 타입 문구를 추가한다.
- `activityLive`: ko `라이브`, en `Live`, ja `ライブ`
- `activityAudio`: ko `오디오`, en `Audio`, ja `オーディオ`
- `activityCommunity`: ko `커뮤니티`, en `Community`, ja `コミュニティ`
- 섹션 제목 문구를 추가한다.
- 현재 라이브
- 방금 활동한 크리에이터
- 최근 데뷔한 크리에이터
- 처음 만나는 오디오
- AI 캐릭터
- 장르의 크리에이터
- 최근 응원이 많은 크리에이터
- 인기 커뮤니티
- 버튼/텍스트 문구를 추가한다.
- `followAll`: `모두 팔로우하기`
- `followAllCompleted`: `모두 팔로우 완료`
- `more`: `더보기`
- `collapse`: `접기`
- 검증 기준:
- 실행 명령: `rg "enum HomeRecommendation|activityLive|followAllCompleted|collapse" SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: 홈 추천 I18n enum과 핵심 문구가 검색된다.
- 수동 확인: 신규 사용자 노출 문자열이 하드코딩 View 텍스트로 흩어지지 않아야 한다.
- [x] **Task 1.4: 신규 API와 Repository 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Main/Home/Repository/MainHomeApi.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Repository/MainHomeRepository.swift`
- 확인: `SodaLive/Sources/Home/HomeApi.swift`
- 확인: `SodaLive/Sources/Home/HomeTabRepository.swift`
- 작업 내용:
- `MainHomeApi``getRecommendations`, `followRecommendedCreators(request:)` 케이스를 작성한다.
- `getRecommendations``GET /api/v2/home/recommendations`로 작성한다.
- `followRecommendedCreators(request:)``POST /api/v2/home/recommendations/creators/follow`로 작성한다.
- 인증 헤더는 기존 패턴대로 `Authorization: Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))`를 사용한다.
- `MainHomeRepository`에서 위 두 API를 `requestPublisher`로 감싼다.
- 기존 `HomeApi.swift`, `HomeTabRepository.swift`는 수정하지 않는다.
- 검증 기준:
- 실행 명령: `rg "/api/v2/home/recommendations|followRecommendedCreators|getRecommendations" SodaLive/Sources/V2/Main/Home/Repository`
- 기대 결과: 신규 API 경로와 repository 메서드가 신규 폴더에서만 검색된다.
- 실행 명령: `rg "/api/v2/home/recommendations" SodaLive/Sources/Home`
- 기대 결과: 검색 결과가 없어야 한다.
### Phase 2: ViewModel 상태와 공용 UI 컴포넌트 작성
- [x] **Task 2.1: MainHomeViewModel 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/Repository/MainHomeRepository.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/Models/MainHomeRecommendationResponse.swift`
- 작업 내용:
- `@Published` 상태를 작성한다.
- `isLoading`
- `isShowPopup`
- `errorMessage`
- `recommendations`
- 장르/응원 그룹별 모두 팔로우 완료 상태
- 모두 팔로우 호출 중 상태
- `fetchRecommendations()`에서 `ApiResponse<HomeRecommendationResponse>`를 디코딩한다.
- `success == true`이고 `data`가 있으면 화면 상태를 갱신한다.
- 실패, 디코딩 실패, 네트워크 실패는 기존 앱 관례대로 `errorMessage``isShowPopup`으로 처리한다.
- `followAll(creatorIds:completionKey:)`에서 `ApiResponseWithoutData`를 디코딩한다.
- 모두 팔로우 성공 시 해당 섹션 완료 상태만 true로 바꾼다.
-`creatorIds`인 경우 API를 호출하지 않고 버튼을 숨기거나 비활성화하는 방식으로 처리한다.
- 검증 기준:
- 실행 명령: `rg "final class MainHomeViewModel|fetchRecommendations|followAll|ApiResponse<HomeRecommendationResponse>|ApiResponseWithoutData" SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 기대 결과: ViewModel 상태와 API 처리 메서드가 검색된다.
- 수동 확인: 실패 처리에서 빈 `catch`를 사용하지 않아야 한다.
- [x] **Task 2.2: 공용 `ExpandableTextView` 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift`
- 작업 내용:
- `text`, `lineLimit`, `moreTitle`, `collapseTitle`, `font`, `foregroundColor`를 입력으로 받는다.
- 기본 상태는 `lineLimit(3)`과 말줄임표를 적용한다.
- `더보기` 터치 시 전체 표시로 전환한다.
- `접기` 터치 시 다시 3줄 제한 상태로 전환한다.
- 외부 라이브러리를 사용하지 않는다.
- 텍스트가 3줄 이하이면 더보기/접기 버튼을 숨긴다.
- 검증 기준:
- 실행 명령: `rg "struct ExpandableTextView|lineLimit|moreTitle|collapseTitle|isExpanded" SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift`
- 기대 결과: 확장/접기 상태와 3줄 제한 구현 키워드가 검색된다.
- 수동 확인: 외부 라이브러리 import 없이 `SwiftUI` 기반으로 구현되어야 한다.
- [x] **Task 2.3: 공용 Creator 컴포넌트 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Component/Creator/CreatorProfileItem.swift`
- 생성: `SodaLive/Sources/V2/Component/Creator/CreatorProfileGrid.swift`
- 작업 내용:
- `CreatorProfileItem`은 프로필 이미지, 이름, 선택적 보조 텍스트, 아이템 탭 액션을 입력으로 받는다.
- `CreatorProfileItem`은 API 응답 모델에 직접 의존하지 않는다.
- `CreatorProfileGrid`는 표시 모델 배열, 열/간격, 탭 액션을 입력으로 받는다.
- 최근 데뷔 크리에이터와 장르/응원 그룹의 개별 아이템에 재사용할 수 있게 작성한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorProfileItem|struct CreatorProfileGrid|creatorNickname|HomeCreatorItem|HomeRecommendationResponse" SodaLive/Sources/V2/Component/Creator`
- 기대 결과: `CreatorProfileItem`, `CreatorProfileGrid`는 검색되고 API 응답 타입 의존은 없어야 한다.
- 수동 확인: 공용 컴포넌트 타입명에 `MainHome` 또는 `HomeRecommendation` 접두사를 붙이지 않아야 한다.
- [x] **Task 2.4: 공용 Button/Banner/Card 컴포넌트 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Component/Button/FollowAllButton.swift`
- 생성: `SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift`
- 생성: `SodaLive/Sources/V2/Component/Card/AiCharacterCard.swift`
- 생성: `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`
- 작업 내용:
- `FollowAllButton`은 기본 상태 `모두 팔로우하기`, 완료 상태 `모두 팔로우 완료`, 완료 아이콘 `ic_new_following`, 호출 중 중복 터치 방지를 지원한다.
- `BannerCarousel`은 배너 이미지 URL 목록과 탭 액션을 입력으로 받는다.
- `AiCharacterCard`는 캐릭터명, 설명, 프로필 이미지, 채팅 수, 원작 제목을 입력으로 받는다.
- `CommunityPostCard`는 Text Only, Text + Img 잠금, Text + Img 노출 variant를 `imageUrl`, `price`, `existOrdered` 기준으로 분기한다.
- `CommunityPostCard``구매완료` 캡슐을 구현하지 않는다.
- 검증 기준:
- 실행 명령: `rg "struct FollowAllButton|ic_new_following|struct BannerCarousel|struct AiCharacterCard|struct CommunityPostCard|구매완료" SodaLive/Sources/V2/Component`
- 기대 결과: 네 공용 컴포넌트와 `ic_new_following`은 검색되고, `구매완료` 캡슐 구현 텍스트는 production UI로 남지 않아야 한다.
- 수동 확인: 공용 컴포넌트가 특정 API 응답 모델을 직접 받지 않아야 한다.
### Phase 3: MainHome 전용 섹션과 페이지 조립
- [ ] **Task 3.1: MainHome 전용 섹션 컴포넌트 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeLiveSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeLiveItem.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeActiveCreatorSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeActiveCreatorItem.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeRecentDebutCreatorSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeFirstAudioContentSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeAiCharacterSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeGenreCreatorSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeCheerCreatorSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeCreatorGroupSection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeCreatorGrid.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomePopularCommunitySection.swift`
- 생성: `SodaLive/Sources/V2/Main/Home/Components/MainHomeBusinessInfoSection.swift`
- 작업 내용:
- 라이브/최근 활동/최근 데뷔/첫 오디오/AI 캐릭터/장르/응원/커뮤니티/사업자 정보 섹션을 작성한다.
- `MainHomeRecentDebutCreatorSection`은 공용 `CreatorProfileGrid`를 사용한다.
- `MainHomeFirstAudioContentSection`은 기존 `AudioContentCard`를 사용한다.
- `MainHomeAiCharacterSection`은 공용 `AiCharacterCard`를 사용한다.
- `MainHomeGenreCreatorSection`, `MainHomeCheerCreatorSection``MainHomeCreatorGroupSection`을 조합한다.
- `MainHomePopularCommunitySection`은 공용 `CommunityPostCard`를 사용한다.
- `MainHomeBusinessInfoSection`은 공용 `ExpandableTextView`를 사용한다.
- 각 섹션은 데이터 배열이 비어 있으면 렌더링하지 않는다.
- 검증 기준:
- 실행 명령: `rg "struct MainHome.*Section|struct MainHome.*Item|CreatorProfileGrid|AudioContentCard|AiCharacterCard|CommunityPostCard|ExpandableTextView" SodaLive/Sources/V2/Main/Home/Components`
- 기대 결과: MainHome 전용 섹션과 공용 컴포넌트 조합이 검색된다.
- 수동 확인: `추천 필모그래피`, `또 다른 모습` 섹션 파일이나 View를 만들지 않아야 한다.
- [ ] **Task 3.2: MainHomeView 작성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 확인: `SodaLive/Sources/V2/Component/HomeTitleBar.swift`
- 작업 내용:
- `HomeTitleBar`, 상단 탭 UI, 세로 `ScrollView`, 각 섹션 컴포넌트를 조합한다.
- `.onAppear`에서 최초 `fetchRecommendations()`를 호출한다.
- 로딩/에러 표시 방식은 기존 앱 관례를 따른다.
- 배너/카드 탭 액션은 PRD의 미결정 항목이므로 구현 범위에서는 비워 두거나 기존 라우팅이 명확한 경우에만 연결한다.
- 사업자 정보 텍스트는 Figma의 사업자 정보 문구를 사용한다.
- 검증 기준:
- 실행 명령: `rg "struct MainHomeView|HomeTitleBar|ScrollView|fetchRecommendations|MainHomeBusinessInfoSection" SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- 기대 결과: 페이지 루트와 주요 조합 요소가 검색된다.
- 수동 확인: `MainTabBarView``MainView`의 safe area inset에서 유지하고, `MainHomeView` 내부에서 중복 생성하지 않아야 한다.
- [ ] **Task 3.3: MainView 홈 탭 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
- 작업 내용:
- `.home` 분기에서 `MainPlaceholderTabView(title: MainTab.home.title)``MainHomeView()`로 교체한다.
- 다른 탭의 placeholder 동작은 변경하지 않는다.
- 기존 `legacyHomeViewModel`의 이벤트 팝업/회원 정보 초기화 흐름은 변경하지 않는다.
- 검증 기준:
- 실행 명령: `rg "case \\.home|MainHomeView|MainPlaceholderTabView" SodaLive/Sources/V2/Main/MainView.swift`
- 기대 결과: `.home` 분기는 `MainHomeView()`를 표시하고, 다른 탭에는 기존 placeholder가 남아 있다.
- 수동 확인: 홈 탭 외 content/chat/my 분기를 불필요하게 수정하지 않아야 한다.
### Phase 4: 프로젝트 포함과 정적 점검
- [ ] **Task 4.1: Xcode 프로젝트 포함 여부 확인**
- 대상 파일:
- 확인: `SodaLive.xcodeproj/project.pbxproj`
- 확인: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md`
- 확인: `SodaLive/Sources/V2/Main/Home/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 작업 내용:
- 신규 Swift 파일이 Xcode 빌드 대상에 포함되는지 확인한다.
- 파일 시스템 동기화 방식으로 자동 포함되면 `SodaLive.xcodeproj/project.pbxproj`를 수정하지 않는다.
- 프로젝트 파일 수정이 필요한 경우에만 `SodaLive.xcodeproj/project.pbxproj`를 갱신한다.
- PRD의 신규 API 응답 필드와 현재 Swift 모델 필드 차이를 확인한다.
- 현재 `SodaLive/Sources/V2/Main/Home` 바로 아래에 있는 추천 전용 파일을 `Recommendation` 하위 폴더로 이동/이름 변경할 대상을 확인한다.
- Figma 기준으로 재사용 가능한 기존 widget을 확인한다.
- 이미 생성된 파일은 유지하고 필요한 보완만 계획한다.
- 검증 기준:
- 실행 명령: `find SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component -maxdepth 4 -type f | sort`
- 기대 결과: 현재 생성된 V2 홈/공용 컴포넌트 목록을 확인할 수 있다.
- [ ] **Task 1.2: 제외 범위 점검**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/Main/Home/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 작업 내용:
- `추천 필모그래피`, `또 다른 모습` 섹션 파일이나 View를 만들지 않는다.
- Figma URL이나 localhost asset URL이 앱 코드에 직접 들어가지 않도록 확인한다.
- 검증 기준:
- 실행 명령: `rg "추천 필모그래피|또 다른 모습|figma.com|localhost:3845" SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component`
- 기대 결과: 검색 결과가 없어야 한다.
### Phase 2: 신규 API와 응답 모델
- [ ] **Task 2.1: 추천 API endpoint 확인/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationRepository.swift`
- 이동/정리: `SodaLive/Sources/V2/Main/Home/Repository/MainHomeApi.swift`
- 이동/정리: `SodaLive/Sources/V2/Main/Home/Repository/MainHomeRepository.swift`
- 확인: `SodaLive/Sources/Home/HomeApi.swift`
- 작업 내용:
- `GET /api/v2/home/recommendations`를 신규 API 타입에만 둔다.
- 추천 API 타입/Repository는 `Recommendation/Repository` 아래에 둔다.
- 기존 `HomeApi`에는 endpoint를 추가하지 않는다.
- 인증 헤더는 기존 token 패턴을 따른다.
- 검증 기준:
- 실행 명령: `rg "/api/v2/home/recommendations|getRecommendations" SodaLive/Sources/V2/Main/Home/Recommendation/Repository SodaLive/Sources/Home`
- 기대 결과: 신규 경로는 `SodaLive/Sources/V2/Main/Home/Recommendation/Repository`에서만 검색된다.
- [ ] **Task 2.2: 신규 Response 모델 보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- 이동/정리: `SodaLive/Sources/V2/Main/Home/Models/MainHomeRecommendationResponse.swift`
- 작업 내용:
- `HomeRecommendationResponse` 필드를 PRD와 맞춘다.
- 추천 응답 모델은 `Recommendation/Models` 아래에 둔다.
- `banners` 타입은 `RecommendationBannerResponse` 또는 Swift 대응 타입으로 명확히 한다.
- `HomeLiveItem.roomId`, `creatorProfileImage`, `HomeActiveCreatorItem.targetId`, `HomeFirstAudioContentItem.price`, `coverImage`, `isPointAvailable`, `HomeAiCharacterItem.totalChatCount`, `originalWorkTitle`, `HomePopularCommunityPostItem.audioUrl` 누락 여부를 확인한다.
- 과거 추정 필드는 실제 사용 경로가 없으면 제거하거나 호환 필요 시 명확히 분리한다.
- 검증 기준:
- 실행 명령: `rg "roomId|creatorProfileImage|targetId|price|coverImage|isPointAvailable|totalChatCount|originalWorkTitle|audioUrl" SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift`
- 기대 결과: PRD 핵심 필드가 모델에 반영되어야 한다.
### Phase 3: 활동 타입과 I18n
- [ ] **Task 3.1: RecommendedActivityType 변환 보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Models/RecommendedActivityType.swift`
- 이동/정리: `SodaLive/Sources/V2/Main/Home/Models/RecommendedActivityType.swift`
- 확인/보완: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- `LIVE`, `LIVE_REPLAY``라이브` 계열 문구로 표시한다.
- `AUDIO``오디오`, `COMMUNITY``커뮤니티` 계열 문구로 표시한다.
- 알 수 없는 값은 서버 코드를 그대로 표시하지 않는다.
- ko/en/ja 문구를 I18n에 둔다.
- 검증 기준:
- 실행 명령: `rg "LIVE_REPLAY|activityLive|activityAudio|activityCommunity|ライブ|オーディオ|コミュニティ" SodaLive/Sources/V2/Main/Home/Recommendation/Models/RecommendedActivityType.swift SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: enum 변환과 I18n 문구가 확인된다.
### Phase 4: MainHomeView 탭 shell과 탭별 루트 View
- [ ] **Task 4.1: MainHomeView 탭 shell 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- 확인: `SodaLive/Sources/V2/Component/HomeTitleBar.swift`
- 작업 내용:
- `MainHomeView``HomeTitleBar`, 추천/랭킹/팔로잉 상단 탭, 선택된 탭 콘텐츠 조합만 담당한다.
- 추천 탭 섹션 조립과 추천 API 호출을 `MainHomeView`에 직접 넣지 않는다.
- 하단 `MainTabBarView`는 기존 `MainView` 구조에서 유지하고 `MainHomeView` 내부에 중복 생성하지 않는다.
- 검증 기준:
- 실행 명령: `rg "struct MainHomeView|HomeTitleBar|MainHomeRecommendationView|MainHomeRankingView|MainHomeFollowingView|MainTabBarView|fetchRecommendations" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`
- 기대 결과: `MainHomeView`는 탭별 루트 View를 조합하고, `fetchRecommendations`를 직접 호출하지 않으며, 탭바 중복 생성은 없어야 한다.
- [ ] **Task 4.2: 추천/랭킹/팔로잉 하위 폴더와 루트 View 준비**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
- 이동/정리: `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 작업 내용:
- 추천 탭 콘텐츠 조립은 `MainHomeRecommendationView`에서 담당한다.
- 추천 API 로딩/에러/섹션 상태는 `MainHomeRecommendationViewModel`에서 담당한다.
- 랭킹/팔로잉 탭은 이번 추천 API 범위 밖이므로 placeholder 또는 최소 루트 View만 준비하고 추천 섹션 코드를 넣지 않는다.
- 기존 `MainHomeViewModel`이 추천 전용 상태만 가진다면 `MainHomeRecommendationViewModel`로 이동/이름 변경한다.
- 검증 기준:
- 실행 명령: `find SodaLive/Sources/V2/Main/Home -maxdepth 3 -type f | sort`
- 기대 결과: `Recommendation`, `Ranking`, `Following` 하위 폴더의 루트 View 파일이 확인된다.
- [ ] **Task 4.3: MainView 홈 탭 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
- 작업 내용:
- `.home` 분기에서 placeholder 대신 `MainHomeView()`를 표시한다.
- 다른 탭 분기와 기존 이벤트 팝업/회원 정보 초기화 흐름은 변경하지 않는다.
- 검증 기준:
- 실행 명령: `rg "case \\.home|MainHomeView|MainPlaceholderTabView" SodaLive/Sources/V2/Main/MainView.swift`
- 기대 결과: `.home``MainHomeView()`를 표시하고, 다른 탭은 기존 동작을 유지한다.
- [ ] **Task 4.4: 추천 탭 상세 진입 guard 적용**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 확인: `SodaLive/Sources/Main/Home/HomeView.swift`
- 확인: `SodaLive/Sources/Home/HomeTabView.swift`
- 작업 내용:
- 신규 추천 탭의 API 호출과 섹션 노출 자체는 막지 않고, 라이브/크리에이터/오디오 콘텐츠/AI 캐릭터/커뮤니티 등 상세 진입 탭 액션 앞에서 guard를 적용한다.
- `MainHomeView``MainHomeRecommendationView`는 직접 로그인/본인인증/민감 콘텐츠 설정 로직을 복사하지 않고, `MainView`에서 전달받은 callback으로 탭 이벤트만 올린다.
- `MainView`에 이미 있는 `token`, `auth`, `isShowAuthView`, `isShowAuthConfirmView`, `pendingAction`, `authConfirmDialog`, `authView` 인프라를 재사용한다.
- 토큰이 비어 있으면 `AppState.shared.setAppStep(step: .login)`으로 이동한다.
- 민감/성인 콘텐츠 진입이 필요한 항목은 한국 사용자이고 `auth == false`이면 `pendingAction`에 원래 이동 액션을 저장하고 `isShowAuthConfirmView = true`로 본인인증 dialog를 띄운다.
- 민감 콘텐츠 보기 설정이 꺼져 있으면 `AppState.shared.setPendingContentSettingsGuideMessage(I18n.Settings.adultContentEnableGuide)``AppState.shared.setAppStep(step: .contentViewSettings)`로 이동한다.
- guard 통과 후에만 `liveDetail`, `creatorDetail`, `contentDetail`, `characterDetail` 등 실제 상세 이동을 실행한다.
- 라이브 항목에 성인 여부 guard를 적용하려면 `HomeLiveItem` 응답에 `isAdult` 또는 동등한 필드가 필요하므로, 필드가 없으면 서버 필터링 전제인지 확인하고 임의 판단하지 않는다.
- 검증 기준:
- 실행 명령: `rg "pendingAction|isShowAuthConfirmView|setPendingContentSettingsGuideMessage|adultContentEnableGuide|contentViewSettings|MainHomeView\\(|MainHomeRecommendationView\\(" SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/V2/Main/Home`
- 기대 결과: 신규 추천 탭 상세 진입 callback이 기존 인증/민감 콘텐츠 guard 인프라를 재사용한다.
- 수동 확인: `MainHomeView`, `MainHomeRecommendationView` 내부에 Bootpay 인증 UI를 중복 구현하지 않아야 한다.
### Phase 5: 현재 라이브 섹션
- [ ] **Task 5.1: 현재 라이브 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeLiveSection.swift`
- 작업 내용:
- `HomeLiveItem.roomId`, `creatorNickname`, `creatorProfileImage`로 라이브 아이템을 표시한다.
- 데이터가 비어 있으면 섹션 제목과 컨테이너를 표시하지 않는다.
- 아이템 터치 이동은 라우팅 규칙이 명확한 경우에만 연결한다.
- 검증 기준:
- 실행 명령: `rg "MainHomeLiveSection|roomId|creatorNickname|creatorProfileImage" SodaLive/Sources/V2/Main/Home/Recommendation`
- 기대 결과: 라이브 섹션이 신규 응답 필드를 기준으로 구성된다.
### Phase 6: 배너 섹션
- [ ] **Task 6.1: 추천 배너 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `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만 작성한다.
- 배너 이동 규칙은 미확정이면 탭 액션을 임의 구현하지 않는다.
- 검증 기준:
- 실행 명령: `rg "RecommendationBannerResponse|HomeBanner|BannerCarousel|MainHomeBannerSection" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Component/Banner`
- 기대 결과: 배너 응답 모델과 화면 조합이 확인된다.
### Phase 7: 최근 활동 크리에이터 섹션
- [ ] **Task 7.1: 최근 활동 크리에이터 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeActiveCreatorSection.swift`
- 작업 내용:
- `creatorNickname`, `creatorProfileImage`, `activityType`, `activityAt`, `targetId`를 사용한다.
- `activityType``RecommendedActivityType` 변환 결과를 표시한다.
- 데이터가 비어 있으면 섹션을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeActiveCreatorSection|activityType|activityAt|targetId|RecommendedActivityType" SodaLive/Sources/V2/Main/Home/Recommendation`
- 기대 결과: 최근 활동 섹션이 신규 응답 필드와 활동 타입 변환을 사용한다.
### Phase 8: 최근 데뷔한 크리에이터 섹션
- [ ] **Task 8.1: 최근 데뷔 크리에이터 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeRecentDebutCreatorSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Creator/CreatorProfileGrid.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Creator/CreatorProfileItem.swift`
- 작업 내용:
- `HomeCreatorItem.creatorId`, `creatorNickname`, `creatorProfileImage`를 사용한다.
- 기존 `CreatorProfileGrid``CreatorProfileItem`을 우선 재사용한다.
- 데이터가 비어 있으면 섹션을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeRecentDebutCreatorSection|CreatorProfileGrid|CreatorProfileItem|creatorProfileImage" SodaLive/Sources/V2/Main/Home/Recommendation SodaLive/Sources/V2/Component/Creator`
- 기대 결과: 최근 데뷔 섹션이 공용 Creator widget을 재사용한다.
### Phase 9: 처음 만나는 오디오 섹션
- [ ] **Task 9.1: AudioContentCardView 조건부 태그 보완**
- 대상 파일:
- 확인/보완: `SodaLive/Sources/V2/Component/AudioContentCard.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeFirstAudioContentSection.swift`
- 작업 내용:
- `HomeFirstAudioContentItem.isPointAvailable == true`이면 `ic_content_tag_point` 시각요소를 표시한다.
- 오리지널 작품이면 `ic_content_tag_original` 시각요소를 표시한다.
- `price == 0`이면 `무료` 텍스트를 표시한다.
- 크리에이터의 첫 번째 콘텐츠이면 `FIRST` 표시를 보여준다.
- 조건에 해당하지 않는 요소는 렌더링하지 않는다.
- 오리지널/첫 콘텐츠 판단 필드가 응답에 없으면 임의 추정하지 않고 Open Question으로 유지한다.
- 검증 기준:
- 실행 명령: `rg "ic_content_tag_point|ic_content_tag_original|isPointAvailable|무료|FIRST|MainHomeFirstAudioContentSection" SodaLive/Sources/V2`
- 기대 결과: 오디오 카드 조건부 시각요소와 섹션 조합이 확인된다.
### Phase 10: AI 캐릭터 섹션
- [ ] **Task 10.1: AI 캐릭터 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeAiCharacterSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Card/AiCharacterCard.swift`
- 작업 내용:
- `characterId`, `creatorId`, `name`, `description`, `profileImage`, `totalChatCount`, `originalWorkTitle`를 사용한다.
- 기존 `AiCharacterCard`로 충분하면 재사용한다.
- 데이터가 비어 있으면 섹션을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeAiCharacterSection|AiCharacterCard|totalChatCount|originalWorkTitle|profileImage" SodaLive/Sources/V2`
- 기대 결과: AI 캐릭터 섹션이 신규 응답 필드를 기준으로 구성된다.
### Phase 11: 장르의 크리에이터 섹션
- [ ] **Task 11.1: 장르 크리에이터 그룹 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeGenreCreatorSection.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeCreatorGroupSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Creator/CreatorProfileGrid.swift`
- 작업 내용:
- `HomeGenreCreatorGroupItem.genreName``creators`를 사용한다.
- 그룹별 크리에이터 목록은 기존 Creator widget을 재사용한다.
- 모두 팔로우 API가 이번 범위에 남아 있다면 현재 섹션의 `creatorId` 목록으로만 호출한다.
- 데이터가 비어 있으면 섹션을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeGenreCreatorSection|MainHomeCreatorGroupSection|genreName|creators|followAll" SodaLive/Sources/V2/Main/Home/Recommendation`
- 기대 결과: 장르 그룹 섹션과 그룹 재사용 구조가 확인된다.
### Phase 12: 최근 응원이 많은 크리에이터 섹션
- [ ] **Task 12.1: 응원 크리에이터 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeCheerCreatorSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeCreatorGroupSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Creator/CreatorProfileGrid.swift`
- 작업 내용:
- `cheerCreators``creatorId`, `creatorNickname`, `creatorProfileImage`를 사용한다.
- 장르 섹션과 같은 그룹 UI를 재사용한다.
- 모두 팔로우 API가 이번 범위에 남아 있다면 응원 섹션의 `creatorId` 목록으로만 호출한다.
- 데이터가 비어 있으면 섹션을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeCheerCreatorSection|cheerCreators|MainHomeCreatorGroupSection|CreatorProfileGrid" SodaLive/Sources/V2/Main/Home/Recommendation`
- 기대 결과: 응원 섹션이 장르 그룹 UI를 재사용한다.
### Phase 13: 인기 커뮤니티 섹션과 FeedCommunityView
- [ ] **Task 13.1: FeedCommunityView/CommunityPostCard 요구 보완**
- 대상 파일:
- 확인/보완: `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomePopularCommunitySection.swift`
- 작업 내용:
- 키워드 영역을 제거한다.
- `imageUrl`이 있으면 이미지를 표시한다.
- `price > 0 && existOrdered == false`이면 유료 미구매 UI를 표시한다.
- `price <= 0 || existOrdered == true`이면 일반 이미지로 표시한다.
- `구매완료` 버튼은 표시하지 않는다.
- `creatorProfileImage`, `createdAt`, `likeCount`, `commentCount`를 표시한다.
- `audioUrl` 재생 UI는 확정 전까지 추가하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomePopularCommunitySection|CommunityPostCard|imageUrl|audioUrl|existOrdered|구매완료" SodaLive/Sources/V2`
- 기대 결과: 커뮤니티 섹션과 카드 조건 분기가 확인되고 `구매완료` 표시 구현은 없어야 한다.
### Phase 14: 사업자 정보 섹션
- [ ] **Task 14.1: 사업자 정보 더보기/접기 섹션 작성/보완**
- 대상 파일:
- 생성/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/Components/MainHomeBusinessInfoSection.swift`
- 확인/보완: `SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift`
- 작업 내용:
- 추천 탭 UI 마지막에 사업자 정보 섹션을 배치한다.
- 섹션 width는 화면에 채운다.
- 기본 3줄 말줄임표와 `더보기`를 표시한다.
- 더보기 터치 시 전체 표시와 `접기`를 제공한다.
- 외부 라이브러리를 사용하지 않는다.
- 검증 기준:
- 실행 명령: `rg "MainHomeBusinessInfoSection|ExpandableTextView|lineLimit|더보기|접기" SodaLive/Sources/V2`
- 기대 결과: 사업자 정보 섹션과 확장 텍스트 구현이 확인된다.
### Phase 15: 섹션 조립 순서와 빈 섹션 처리
- [ ] **Task 15.1: MainHomeRecommendationView에 섹션 순서 반영**
- 대상 파일:
- 확인/보완: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 작업 내용:
- 섹션 순서를 PRD와 맞춘다.
- 현재 라이브, 배너, 최근 활동, 최근 데뷔, 처음 만나는 오디오, AI 캐릭터, 장르, 응원, 인기 커뮤니티, 사업자 정보 순서로 배치한다.
- 각 데이터 배열이 비어 있으면 섹션 제목과 컨테이너까지 숨긴다.
- 검증 기준:
- 실행 명령: `rg "MainHomeLiveSection|MainHomeBannerSection|MainHomeActiveCreatorSection|MainHomeRecentDebutCreatorSection|MainHomeFirstAudioContentSection|MainHomeAiCharacterSection|MainHomeGenreCreatorSection|MainHomeCheerCreatorSection|MainHomePopularCommunitySection|MainHomeBusinessInfoSection" SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift`
- 기대 결과: 모든 구현 대상 섹션이 올바른 순서로 조립된다.
### Phase 16: 프로젝트 포함과 빌드 검증
- [ ] **Task 16.1: Xcode 프로젝트 포함 여부 확인**
- 대상 파일:
- 확인/보완: `SodaLive.xcodeproj/project.pbxproj`
- 확인: `SodaLive/Sources/V2/Main/Home/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 작업 내용:
- 신규 Swift 파일이 빌드 대상에 포함되는지 확인한다.
- 파일 시스템 동기화 방식으로 자동 포함되면 프로젝트 파일을 수정하지 않는다.
- 프로젝트 파일 수정이 필요한 경우에만 최소 변경한다.
- 검증 기준:
- 실행 명령: `plutil -lint SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: 프로젝트 파일을 수정했거나 확인이 필요할 때 `OK`가 출력된다.
- 수동 확인: 프로젝트 파일 수정이 불필요하면 변경하지 않아야 한다.
- 기대 결과: `OK`
- [ ] **Task 4.2: 정적 점검**
- [ ] **Task 16.2: 정적 점검과 빌드**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/Main/Home/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 확인: `SodaLive/Sources/Home/**`
- 작업 내용:
- 제외 섹션, 기존 홈 API 오염, Figma URL, 공백 오류를 확인한다.
- 가능한 빌드 명령을 실행한다.
- 검증 기준:
- 실행 명령: `rg "추천 필모그래피|또 다른 모습" SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component`
- 기대 결과: 검색 결과가 없어야 한다.
@@ -323,61 +392,64 @@
- 기대 결과: 검색 결과가 없어야 한다.
- 실행 명령: `git diff --check`
- 기대 결과: 출력 없이 성공한다.
### Phase 5: 빌드와 기능 검증
- [ ] **Task 5.1: 빌드 검증**
- 대상 파일:
- 확인: `SodaLive.xcworkspace`
- 확인: `SodaLive.xcodeproj`
- 확인: `SodaLive/Sources/V2/Main/Home/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 작업 내용:
- 기본 빌드를 실행한다.
- 필요 시 운영 스킴 빌드를 실행한다.
- 테스트 액션 확인이 필요하면 테스트 명령을 실행하고, 스킴 미구성 시 결과를 검증 기록에 남긴다.
- 검증 기준:
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
- 기대 결과: 테스트가 실행되거나, 스킴 미구성 메시지를 검증 기록에 남긴다.
- [ ] **Task 5.2: 기능 검증**
- 대상 파일:
- 확인: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`
- 확인: `SodaLive/Sources/V2/Main/Home/Components/**`
- 확인: `SodaLive/Sources/V2/Component/**`
- 작업 내용:
- 추천 API 성공/실패, 빈 섹션, 모두 팔로우 성공/실패, 사업자 정보 확장/접기, 활동 타입 I18n, 커뮤니티 카드 variant를 확인한다.
- 검증 기준:
- 수동 확인: 추천 API 성공 시 각 섹션이 응답 데이터로 표시된다.
- 수동 확인: 빈 배열 섹션은 제목과 컨테이너까지 숨겨진다.
- 수동 확인: 모두 팔로우 버튼 터치 후 API 성공 시 해당 버튼만 `모두 팔로우 완료`로 바뀐다.
- 수동 확인: 모두 팔로우 실패 시 버튼 상태가 변경되지 않고 오류가 표시된다.
- 수동 확인: 사업자 정보 기본 3줄 말줄임표, 더보기, 전체 표시, 접기가 동작한다.
- 수동 확인: 활동 타입 `LIVE`, `LIVE_REPLAY`, `AUDIO`, `COMMUNITY`가 I18n 문구로 표시된다.
- 수동 확인: 커뮤니티 카드 3개 variant가 조건에 맞게 표시되고 `구매완료` 캡슐이 표시되지 않는다.
### Phase 17: 문서와 검증 기록 갱신
### Phase 6: 문서와 체크리스트 갱신
- [ ] **Task 6.1: 문서/체크리스트 갱신**
- [ ] **Task 17.1: PRD/계획 문서 갱신**
- 대상 파일:
- 수정: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md`
- 수정: `docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md`
- 작업 내용:
- 구현 중 확정된 배너 이동 규칙, 표시 개수, 타입 보정 사항이 있으면 PRD를 먼저 갱신한다.
- 완료한 task 체크박스를 실제 상태에 맞게 `- [x]` 갱신한다.
- 구현 중 확정된 배너 이동 규칙, 표시 개수, 오디오 태그 판단 필드, 커뮤니티 `audioUrl` 표시 여부가 있으면 PRD를 먼저 갱신한다.
- 완료한 task 체크박스를 실제 상태에 맞게 갱신한다.
- 실행한 검증 명령과 결과를 이 문서 하단 검증 기록에 누적한다.
- 검증 기준:
- 실행 명령: `rg "### Phase [0-9]+:|- \\[[ x]\\] \\*\\*Task [0-9]+\\.[0-9]+:" docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md`
- 기대 결과: phase heading과 `Task N.N` 형식 체크박스가 검색된다.
- 수동 확인: 새 문서를 만들지 않고 기존 PRD/계획 문서에 이어서 기록해야 한다.
## 검증 기록
### 2026-06-26 MainHome 탭별 폴더 구조 반영
- 목적: `MainHomeView`에 추천/랭킹/팔로잉 콘텐츠 조립이 누적되지 않도록 탭별 하위 폴더와 루트 View 구조를 PRD/계획 문서에 반영
- 수행 내용:
- `prd.md``MainHomeView`는 홈 상단 shell, 추천/랭킹/팔로잉 탭 콘텐츠는 별도 View로 분리한다는 기준 추가
- 추천 탭 루트/모델/API/ViewModel/섹션 경로를 `SodaLive/Sources/V2/Main/Home/Recommendation/**` 기준으로 변경
- 랭킹/팔로잉 탭 루트 파일 후보를 `SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift`, `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`로 추가
- Phase 4를 `MainHomeView 탭 shell과 탭별 루트 View`로 재정의하고, 추천 섹션 조립은 `MainHomeRecommendationView`에서 처리하도록 변경
- 검증:
- `rg "MainHomeRecommendationView|MainHomeRankingView|MainHomeFollowingView|Recommendation/Components|Recommendation/Repository|Recommendation/Models" docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 탭별 루트 View와 추천 탭 하위 경로 반영 확인
- `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_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 출력 없이 성공 확인
### 2026-06-26 상세 진입 guard 요구사항 추가
- 목적: 신규 추천 탭의 상세 진입 시 기존 홈과 같은 로그인/본인인증/민감 콘텐츠 보기 설정 guard를 적용하도록 PRD와 계획 문서 보완
- 수행 내용:
- `prd.md``추천 탭 상세 진입 guard` 요구사항 추가
- `plan-task.md`의 Phase 4에 `Task 4.3: 추천 탭 상세 진입 guard 적용` 추가
- `MainHomeView`는 탭 이벤트만 올리고, V2 `MainView`의 기존 인증 인프라를 재사용하는 방향으로 명시
- `HomeLiveItem``isAdult` 또는 동등한 필드가 필요하다는 Open Question 반영
- 검증:
- `rg "추천 탭 상세 진입 guard|pendingAction|isShowAuthConfirmView|setPendingContentSettingsGuideMessage|HomeLiveItem.*isAdult|Task 4.3" docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, guard 요구사항과 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_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 출력 없이 성공 확인
### 2026-06-26 PRD/계획 문서 재분류
- 목적: 신규 API 응답 스펙, FeedCommunityView, AudioContentCardView, 사업자 정보 요구사항을 PRD와 계획 문서에 반영하고, 기존 Phase를 섹션 단위 Phase로 재분류
- 수행 내용:
- `prd.md`를 신규 응답 스펙 기준으로 갱신
- `plan-task.md`의 기존 Phase/완료 상태를 새 작업 분류 기준으로 재작성
- UI 작업은 현재 라이브, 배너, 최근 활동, 최근 데뷔, 처음 만나는 오디오, AI 캐릭터, 장르, 응원, 인기 커뮤니티, 사업자 정보 섹션별 Phase로 분리
- 기존 검증 기록은 아래에 보존
- 검증:
- `rg "GET /api/v2/home/recommendations|RecommendationBannerResponse|FeedCommunityView|AudioContentCardView|추천 필모그래피|또 다른 모습|node-id=309-19774|node-id=309-19775" docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 신규 API/응답 타입/FeedCommunityView/AudioContentCardView/제외 섹션/Figma 노드 반영 확인
- `rg "### Phase [0-9]+:|- \\[[ x]\\] \\*\\*Task [0-9]+\\.[0-9]+:" docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, Phase heading과 Task 체크박스 형식 확인
- `git diff --check -- docs/20260602_메인_홈_추천_UI_API_연동/prd.md docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` 실행, 출력 없이 성공 확인
### 2026-06-02 계획 문서 생성
- 목적: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md`를 기준으로 구현 전 계획/TASK 문서 작성