# 메인 홈 추천 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` - 코드 스타일: `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/**` 아래에 형태별 폴더로 작성한다. - 외부 라이브러리는 추가하지 않는다. - 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`로 제공. - `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/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` - 체크리스트와 검증 기록 유지. ## TASK 체크리스트 ### Phase 1: 구현 기준과 데이터 계층 준비 - [x] **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`를 디코딩한다. - `success == true`이고 `data`가 있으면 화면 상태를 갱신한다. - 실패, 디코딩 실패, 네트워크 실패는 기존 앱 관례대로 `errorMessage`와 `isShowPopup`으로 처리한다. - `followAll(creatorIds:completionKey:)`에서 `ApiResponseWithoutData`를 디코딩한다. - 모두 팔로우 성공 시 해당 섹션 완료 상태만 true로 바꾼다. - 빈 `creatorIds`인 경우 API를 호출하지 않고 버튼을 숨기거나 비활성화하는 방식으로 처리한다. - 검증 기준: - 실행 명령: `rg "final class MainHomeViewModel|fetchRecommendations|followAll|ApiResponse|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` - 확인: `SodaLive/Sources/V2/Main/Home/**` - 확인: `SodaLive/Sources/V2/Component/**` - 작업 내용: - 신규 Swift 파일이 Xcode 빌드 대상에 포함되는지 확인한다. - 파일 시스템 동기화 방식으로 자동 포함되면 `SodaLive.xcodeproj/project.pbxproj`를 수정하지 않는다. - 프로젝트 파일 수정이 필요한 경우에만 `SodaLive.xcodeproj/project.pbxproj`를 갱신한다. - 검증 기준: - 실행 명령: `plutil -lint SodaLive.xcodeproj/project.pbxproj` - 기대 결과: 프로젝트 파일을 수정했거나 확인이 필요할 때 `OK`가 출력된다. - 수동 확인: 프로젝트 파일 수정이 불필요하면 변경하지 않아야 한다. - [ ] **Task 4.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` - 기대 결과: 검색 결과가 없어야 한다. - 실행 명령: `rg "api/v2/home/recommendations" SodaLive/Sources/Home` - 기대 결과: 검색 결과가 없어야 한다. - 실행 명령: `rg "localhost:3845|figma.com" SodaLive/Sources/V2/Main/Home SodaLive/Sources/V2/Component` - 기대 결과: 검색 결과가 없어야 한다. - 실행 명령: `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 6: 문서와 체크리스트 갱신 - [ ] **Task 6.1: 문서/체크리스트 갱신** - 대상 파일: - 수정: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md` - 수정: `docs/20260602_메인_홈_추천_UI_API_연동/plan-task.md` - 작업 내용: - 구현 중 확정된 배너 이동 규칙, 표시 개수, 타입 보정 사항이 있으면 PRD를 먼저 갱신한다. - 완료한 task 체크박스를 실제 상태에 맞게 `- [x]`로 갱신한다. - 실행한 검증 명령과 결과를 이 문서 하단 검증 기록에 누적한다. - 검증 기준: - 실행 명령: `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-02 계획 문서 생성 - 목적: `docs/20260602_메인_홈_추천_UI_API_연동/prd.md`를 기준으로 구현 전 계획/TASK 문서 작성 - 수행 내용: - MainHome 페이지 루트, 신규 API 계층, 공용 컴포넌트, MainHome 전용 컴포넌트, 연결/검증 작업을 TASK 단위로 분리 - 공용 컴포넌트는 `SodaLive/Sources/V2/Component/**` 형태별 폴더 기준으로 배치 - MainHome 전용 컴포넌트는 `SodaLive/Sources/V2/Main/Home/Components` 기준으로 배치 - 아직 수행하지 않은 작업: - Swift 코드 구현 - Xcode 프로젝트 포함 여부 확인 - 빌드/기능 검증 ### 2026-06-02 Phase 1 구현 완료 - 목적: Phase 1 범위인 구현 전 구조 확인, 추천 API 모델, I18n 문구, 신규 API/Repository 작성 - 수행 내용: - `SodaLive/Sources/V2/Main/MainView.swift`의 `.home` placeholder 유지 상태와 기존 `HomeApi`, `HomeTabRepository`, `I18n` 구조 확인 - `SodaLive/Sources/V2/Main/Home/Models`에 `HomeRecommendationResponse`, `FollowRecommendedCreatorsRequest`, `RecommendedActivityType` 추가 - `SodaLive/Sources/I18n/I18n.swift`에 `I18n.HomeRecommendation` 문구 추가 - `SodaLive/Sources/V2/Main/Home/Repository`에 `MainHomeApi`, `MainHomeRepository` 추가 - 신규 Swift 파일 5개를 `SodaLive.xcodeproj/project.pbxproj`의 `SodaLive`, `SodaLive-dev` Sources에 포함 - 검증: - `rg "case \.home|MainPlaceholderTabView|enum HomeApi|enum I18n" SodaLive/Sources/V2/Main SodaLive/Sources/Home SodaLive/Sources/I18n/I18n.swift` 실행, `.home` placeholder와 기존 `HomeApi`, `I18n` 구조 확인 - `rg "struct HomeRecommendationResponse|struct FollowRecommendedCreatorsRequest|enum RecommendedActivityType" SodaLive/Sources/V2/Main/Home/Models` 실행, 모델 정의 검색 확인 - `rg "enum HomeRecommendation|activityLive|followAllCompleted|collapse" SodaLive/Sources/I18n/I18n.swift` 실행, 신규 I18n 문구 검색 확인 - `rg "/api/v2/home/recommendations|followRecommendedCreators|getRecommendations" SodaLive/Sources/V2/Main/Home/Repository` 실행, 신규 API 경로와 repository 메서드 검색 확인 - `rg "/api/v2/home/recommendations" SodaLive/Sources/Home` 실행, 검색 결과 없음 확인 - `plutil -lint SodaLive.xcodeproj/project.pbxproj` 실행, `OK` 확인 - `git diff --check` 실행, 출력 없이 성공 확인 - `xcodebuild -workspace "SodaLive.xcworkspace" -list` 실행, 별도 테스트 타깃 없음 확인 - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 실행, `BUILD SUCCEEDED` 확인 - 아직 수행하지 않은 작업: - Phase 2 이후 ViewModel, UI 컴포넌트, 홈 탭 연결 - 테스트 타깃이 없어 Phase 1 전용 RED/GREEN 단위 테스트는 추가하지 않음 ### 2026-06-02 Phase 2 구현 완료 - 목적: Phase 2 범위인 `MainHomeViewModel`과 공용 UI 컴포넌트 작성 - 수행 내용: - `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`에 추천 API 로딩/오류/응답 상태와 모두 팔로우 완료/호출 중 상태 추가 - `SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift` 추가 - `SodaLive/Sources/V2/Component/Creator`에 `CreatorProfileItem`, `CreatorProfileGrid` 추가 - `SodaLive/Sources/V2/Component/Button`, `Banner`, `Card`에 `FollowAllButton`, `BannerCarousel`, `AiCharacterCard`, `CommunityPostCard` 추가 - `SodaLive/Resources/Assets.xcassets/v2`에 Phase 2 버튼/카드용 `ic_new_following`, `ic_new_follow`, `ic_new_community_lock` 이미지셋 포함 - 신규 Swift 파일 8개를 `SodaLive.xcodeproj/project.pbxproj`의 `SodaLive`, `SodaLive-dev` Sources에 포함 - 실제 파일이 없던 기존 `CustomView/ExpandableTextView.swift` stale 프로젝트 참조를 제거해 중복 빌드 산출물 오류 해결 - 검증: - `rg "final class MainHomeViewModel|fetchRecommendations|followAll|ApiResponse|ApiResponseWithoutData" SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift` 실행, ViewModel 상태/API 처리 메서드 검색 확인 - `rg "struct ExpandableTextView|lineLimit|moreTitle|collapseTitle|isExpanded" SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift` 실행, 확장/접기 상태와 3줄 제한 구현 키워드 검색 확인 - `rg "struct CreatorProfileItem|struct CreatorProfileGrid|creatorNickname|HomeCreatorItem|HomeRecommendationResponse|MainHome" SodaLive/Sources/V2/Component/Creator` 실행, 공용 Creator 컴포넌트 검색 및 API 응답 타입 의존 없음 확인 - `rg "struct FollowAllButton|ic_new_following|struct BannerCarousel|struct AiCharacterCard|struct CommunityPostCard|구매완료|HomeBannerItem|HomeAiCharacterItem|HomePopularCommunityPostItem|HomeRecommendationResponse" SodaLive/Sources/V2/Component` 실행, 공용 컴포넌트 검색 및 금지 문구/응답 타입 의존 없음 확인 - `plutil -lint SodaLive.xcodeproj/project.pbxproj` 실행, `OK` 확인 - `git diff --check` 실행, 출력 없이 성공 확인 - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 실행, `BUILD SUCCEEDED` 확인 - 아직 수행하지 않은 작업: - Phase 3 이후 MainHome 전용 섹션, 페이지 조립, 홈 탭 연결 - 테스트 타깃이 없어 Phase 2 전용 RED/GREEN 단위 테스트는 추가하지 않음 ### 2026-06-12 Phase 2 코드 리뷰 보완 - 목적: Phase 2 코드 리뷰에서 확인된 ViewModel Combine 캡처 안정성과 커뮤니티 카드 잠금 아이콘 asset 불일치 보완 - 수행 내용: - `SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift`의 추천 API/모두 팔로우 API Combine callback 캡처를 `[unowned self]`에서 `[weak self]`와 early return으로 변경 - `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`의 잠금 아이콘을 기존 `ic_lock_bb`에서 Phase 2 신규 asset인 `ic_new_community_lock`으로 변경 - 검증: - `rg "unowned self" SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift` 실행, 검색 결과 없음 확인 - `rg "weak self|ic_new_community_lock|ic_lock_bb" SodaLive/Sources/V2/Main/Home/MainHomeViewModel.swift SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift` 실행, `weak self` 캡처 4곳과 `ic_new_community_lock` 사용 확인 - `git diff --check` 실행, 출력 없이 성공 확인 ### 2026-06-12 Phase 1/2 리뷰 추가 보완 - 목적: Phase 1/2 리뷰에서 확인된 커뮤니티 카드 PRD 표시 누락과 사업자 정보 더보기 판정 안정성 보완 - 수행 내용: - `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`에 생성 시간 표시용 `createdAt` 입력과 footer 렌더링 추가 - 잠금 이미지의 가격 표시를 단순 텍스트에서 pay capsule 형태로 변경 - `SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift`의 측정 높이 변화 감지를 추가해 부모 폭/레이아웃 변경 시 더보기 표시 판정이 갱신되도록 보완 - 검증: - `rg "createdAt|Capsule\(\)|onChange\(of: proxy.size.height\)" SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift SodaLive/Sources/V2/Component/Text/ExpandableTextView.swift` 실행, 생성 시간 입력/표시, pay capsule, 높이 변화 감지 검색 확인 - `git diff --check` 실행, 출력 없이 성공 확인 - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 실행, `BUILD SUCCEEDED` 확인