635 lines
71 KiB
Markdown
635 lines
71 KiB
Markdown
# 메인 홈 팔로잉 탭 구현 계획
|
|
|
|
## 기준 문서
|
|
|
|
- PRD: `docs/20260630_메인_홈_팔로잉_탭/prd.md`
|
|
- 코드 스타일: `docs/agent-guides/code-style.md`
|
|
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
|
|
- Figma 전체 화면: `24:5682`
|
|
- Figma 최근 소식 랭킹 카드: `24:5717`
|
|
- Figma 최근 소식 오디오 카드: `1229:27212`
|
|
- Figma 최근 소식 화보 카드: `1229:27213`
|
|
|
|
## 구현 원칙
|
|
|
|
- `MainHomeView`는 홈 상단 title bar와 `추천`/`랭킹`/`팔로잉` Text tab shell만 담당한다.
|
|
- 팔로잉 탭의 API, 모델, ViewModel, 세부 UI는 `SodaLive/Sources/V2/Main/Home/Following/**` 아래에 둔다.
|
|
- 여러 화면에서 재사용할 근거가 생기기 전까지 팔로잉 전용 UI는 `SodaLive/Sources/V2/Main/Home/Following/Components/**`에 둔다.
|
|
- 기존 추천/랭킹 탭의 `TargetType`, Repository, ViewModel, `ApiResponse<T>` 디코딩 패턴을 따른다.
|
|
- 홈 상단 `팔로잉` 탭 터치 시 로그인 guard를 먼저 적용한다. 비로그인 상태에서는 탭 전환과 API 호출을 하지 않고 로그인 화면으로 이동한다.
|
|
- 팔로잉 탭 전체 empty 문구는 `팔로잉 소식이 아직 없어요.\n관심 있는 크리에이터를 팔로우해 보세요.`를 사용하고 `I18n.HomeFollowing.emptyStateMessage`로 다국어 처리한다.
|
|
- API 실패 문구는 `팔로잉 정보를 불러오지 못했습니다.\n잠시 후 다시 시도해 주세요.`를 사용한다.
|
|
- 섹션별 배열이 비어 있으면 해당 섹션은 숨긴다. 팔로잉 크리에이터 섹션도 예외 없이 `followingCreators`가 비어 있으면 `전체` 버튼까지 함께 숨긴다.
|
|
- 모든 뷰의 루트 container와 섹션 container에는 불필요한 고정 숫자 width/height를 사용하지 않는다.
|
|
- 프로필 이미지, 썸네일, 아이콘, tag, 최소 터치 영역처럼 UI 의미상 크기가 정해진 요소에만 고정 크기를 사용한다.
|
|
- 외부 라이브러리는 추가하지 않는다.
|
|
- Figma localhost asset URL은 앱 코드에 사용하지 않는다.
|
|
|
|
## 구현 제약
|
|
|
|
- `FollowingCommunityPostNewsResponse`에는 `creatorId`가 없다.
|
|
- 현재 코드에는 `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`와 `CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)`가 있어 `postId` 단독 상세 진입이 가능하다.
|
|
- `COMMUNITY_POST` tap 액션은 `communityPost.postId`를 이 route로 전달한다. 임의로 수정 화면(`creatorCommunityModify`)이나 잘못된 creator 화면으로 이동하지 않는다.
|
|
|
|
## 주요 대상 파일
|
|
|
|
### 생성
|
|
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Models/HomeFollowingTabResponse.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingApi.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingRepository.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingChatSection.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingScheduleSection.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingContentNewsCard.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingEmptyStateView.swift`
|
|
|
|
### 수정
|
|
|
|
- `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- `SodaLive/Sources/I18n/I18n.swift`
|
|
- 필요 시 `SodaLive.xcodeproj/project.pbxproj`
|
|
|
|
### 확인
|
|
|
|
- `SodaLive/Sources/V2/Component/TextTabBar.swift`
|
|
- `SodaLive/Sources/V2/Component/SectionTitle.swift`
|
|
- `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift`
|
|
- `SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingViewModel.swift`
|
|
- `SodaLive/Sources/App/AppStep.swift`
|
|
- `SodaLive/Sources/ContentView.swift`
|
|
- `SodaLive/Sources/Follow/FollowCreatorView.swift`
|
|
- `SodaLive/Sources/Chat/Talk/TalkView.swift`
|
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`
|
|
|
|
## TASK 체크리스트
|
|
|
|
### Phase 1: 기준점 점검과 탭 guard 설계
|
|
|
|
- [x] **Task 1.1: PRD와 기존 팔로잉 placeholder 확인**
|
|
- 대상 파일:
|
|
- 확인: `docs/20260630_메인_홈_팔로잉_탭/prd.md`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 작업 내용:
|
|
- PRD 성공 기준과 제외 범위를 다시 읽고 구현 범위를 확정한다.
|
|
- `MainHomeFollowingView`가 placeholder인지 확인한다.
|
|
- `MainHomeView`가 `TextTabBar` binding으로 탭을 전환하는 구조인지 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `sed -n '1,120p' SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift && sed -n '1,120p' SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 기대 결과: 팔로잉 탭은 placeholder이고, `MainHomeView`는 탭 shell 역할만 담당한다.
|
|
|
|
- [x] **Task 1.2: 팔로잉 탭 선택 로그인 guard 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Component/TextTabBar.swift`
|
|
- 작업 내용:
|
|
- `TextTabBar` 자체는 수정하지 않는다.
|
|
- `MainHomeView`에 `onSelectFollowingTab: () -> Bool` callback을 추가한다.
|
|
- `TextTabBar`의 `selectedItem`에는 custom `Binding`을 전달한다.
|
|
- binding setter에서 `.following` 선택 시 `onSelectFollowingTab()`이 `true`일 때만 `selectedTab = .following`을 실행한다.
|
|
- `MainView`에서 `onSelectFollowingTab`을 구현한다.
|
|
- 토큰이 비어 있으면 `AppState.shared.setAppStep(step: .login)`을 호출하고 `false`를 반환한다.
|
|
- 토큰이 있으면 `true`를 반환한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onSelectFollowingTab|Binding\\(|selectedTab = \\.following|setAppStep\\(step: \\.login\\)|TextTabBar" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 기대 결과: 팔로잉 탭 선택 guard가 `MainHomeView`와 `MainView`에 존재하고, `TextTabBar.swift`는 변경되지 않는다.
|
|
|
|
### Phase 2: API, Repository, Response model, ViewModel
|
|
|
|
- [x] **Task 2.1: 팔로잉 응답 모델 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Models/HomeFollowingTabResponse.swift`
|
|
- 작업 내용:
|
|
- PRD의 Swift 모델 기준대로 `HomeFollowingTabResponse`, `FollowingCreatorResponse`, `FollowingLiveResponse`, `ChatRoomListItemResponse`, `FollowingScheduleResponse`, `FollowingNewsResponse`를 만든다.
|
|
- nested 최근 소식 모델 `FollowingCreatorRankingNewsResponse`, `FollowingContentNewsResponse`, `FollowingContentRankingNewsResponse`, `FollowingCommunityPostNewsResponse`를 만든다.
|
|
- `CreatorActivityType`, `FollowingNewsType`은 unknown case를 포함한 `Decodable, Hashable` enum으로 만든다.
|
|
- id가 있는 모델은 `Identifiable`을 채택한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct HomeFollowingTabResponse|struct ChatRoomListItemResponse|enum CreatorActivityType|struct FollowingNewsResponse|struct FollowingCreatorRankingNewsResponse|struct FollowingContentNewsResponse|struct FollowingContentRankingNewsResponse|struct FollowingCommunityPostNewsResponse|enum FollowingNewsType|unknown\\(" SodaLive/Sources/V2/Main/Home/Following/Models/HomeFollowingTabResponse.swift`
|
|
- 기대 결과: PRD의 응답 모델과 unknown enum 처리가 확인된다.
|
|
|
|
- [x] **Task 2.2: 팔로잉 API endpoint 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingApi.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift`
|
|
- 작업 내용:
|
|
- `MainHomeFollowingApi`를 `TargetType`으로 만든다.
|
|
- case는 `getFollowing` 하나만 둔다.
|
|
- path는 `/api/v2/home/following`, method는 `.get`, task는 `.requestPlain`로 둔다.
|
|
- headers는 기존 V2 홈 API와 동일하게 `Authorization: Bearer {token}` 패턴을 사용한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "enum MainHomeFollowingApi|getFollowing|/api/v2/home/following|Authorization|requestPlain|TargetType" SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingApi.swift`
|
|
- 기대 결과: 팔로잉 전용 API endpoint와 인증 헤더가 확인된다.
|
|
|
|
- [x] **Task 2.3: 팔로잉 Repository 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingRepository.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Ranking/Repository/MainHomeRankingRepository.swift`
|
|
- 작업 내용:
|
|
- `MainHomeFollowingRepository`를 만들고 `MoyaProvider<MainHomeFollowingApi>`를 사용한다.
|
|
- `getFollowing() -> AnyPublisher<Response, MoyaError>` 메서드를 제공한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "final class MainHomeFollowingRepository|MoyaProvider<MainHomeFollowingApi>|getFollowing\\(\\)|AnyPublisher<Response, MoyaError>" SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingRepository.swift`
|
|
- 기대 결과: 기존 V2 홈 Repository와 같은 패턴이 확인된다.
|
|
|
|
- [x] **Task 2.4: 팔로잉 ViewModel 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingViewModel.swift`
|
|
- 작업 내용:
|
|
- `MainHomeFollowingViewModel`을 `ObservableObject`로 만든다.
|
|
- `@Published var isLoading`, `@Published var response`, `@Published var isLoginRequired`, `@Published var message`, `@Published var hasLoaded`를 둔다.
|
|
- `fetchFollowing()`에서 `ApiResponse<HomeFollowingTabResponse>`를 디코딩한다.
|
|
- 성공 시 `response`와 `isLoginRequired`를 갱신한다.
|
|
- `isLoginRequired == true`이면 `response`는 nil로 두고 로그인 필요 상태를 표시할 수 있게 한다.
|
|
- 모든 섹션 배열이 비어 있으면 `message = I18n.HomeFollowing.emptyStateMessage`를 설정한다.
|
|
- API 실패/디코딩 실패 시 `message = I18n.HomeFollowing.loadFailedMessage`를 설정한다.
|
|
- 실패 시 toast 대신 안내 상태를 표시한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "final class MainHomeFollowingViewModel|fetchFollowing|ApiResponse<HomeFollowingTabResponse>|isLoginRequired|emptyStateMessage|loadFailedMessage|hasLoaded|ERROR_LOG" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- 기대 결과: API 디코딩, 로그인 필요 상태, empty/API 실패 문구 처리가 확인된다.
|
|
|
|
- [x] **Task 2.5: 팔로잉 I18n 문구 추가**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/I18n/I18n.swift`
|
|
- 작업 내용:
|
|
- `I18n.HomeFollowing` enum을 추가한다.
|
|
- `emptyStateMessage` ko는 `팔로잉 소식이 아직 없어요.\n관심 있는 크리에이터를 팔로우해 보세요.`로 둔다.
|
|
- `loadFailedMessage` ko는 `팔로잉 정보를 불러오지 못했습니다.\n잠시 후 다시 시도해 주세요.`로 둔다.
|
|
- `loginRequiredMessage`, `loginButtonTitle`, `allButtonTitle`, `onAirTitle`, `recentChatTitle`, `monthlyScheduleTitle`, `recentNewsTitle`, `audioTag`, `photoTag`를 추가한다.
|
|
- en/ja는 기능 검증을 막지 않는 자연스러운 대응 문구로 추가한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "enum HomeFollowing|emptyStateMessage|loadFailedMessage|팔로잉 소식이 아직 없어요|팔로잉 정보를 불러오지 못했습니다|allButtonTitle|recentNewsTitle|audioTag|photoTag" SodaLive/Sources/I18n/I18n.swift`
|
|
- 기대 결과: 팔로잉 탭 사용자 노출 문구가 I18n에 존재한다.
|
|
|
|
### Phase 3: 팔로잉 크리에이터 섹션과 전체 버튼
|
|
|
|
- [x] **Task 3.1: 팔로잉 크리에이터 섹션 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`
|
|
- 확인: `SodaLive/Sources/V2/Component/Creator/CreatorProfileItem.swift`
|
|
- 확인: `SodaLive/Sources/Follow/FollowCreatorView.swift`
|
|
- 작업 내용:
|
|
- `followingCreators`를 가로 스크롤 목록으로 표시한다.
|
|
- 각 creator item은 프로필 이미지와 닉네임을 표시한다.
|
|
- creator item 탭 시 `onTapCreator(creatorId)`를 호출한다.
|
|
- `followingCreators`가 비어 있으면 섹션을 렌더링하지 않는다.
|
|
- 목록 마지막에는 `followingCreators`가 있을 때만 `전체` 버튼을 표시한다.
|
|
- `전체` 버튼 text는 `I18n.HomeFollowing.allButtonTitle`, 색상은 `Color.soda400`을 사용한다.
|
|
- `전체` 버튼 horizontal padding은 16으로 둔다.
|
|
- `전체` 버튼 높이는 creator item 높이와 동일하게 맞춘다.
|
|
- `전체` 버튼 탭 시 `onTapAll()`을 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingCreatorSection|ScrollView\\(\\.horizontal|followingCreators|onTapCreator|onTapAll|allButtonTitle|Color.soda400|padding\\(\\.horizontal, 16\\)" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`
|
|
- 기대 결과: 가로 creator 목록, 비어 있을 때 섹션 숨김, 마지막 `전체` 버튼 요구사항이 확인된다.
|
|
|
|
- [x] **Task 3.2: 전체 팔로잉 목록 진입 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 확인: `SodaLive/Sources/App/AppStep.swift`
|
|
- 확인: `SodaLive/Sources/ContentView.swift`
|
|
- 작업 내용:
|
|
- `MainHomeView`에 `onTapFollowingAll: () -> Void` callback을 추가한다.
|
|
- `MainHomeFollowingView`까지 callback을 전달한다.
|
|
- `MainView`에서 `onTapFollowingAll`을 `AppState.shared.setAppStep(step: .followingList)`로 구현한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapFollowingAll|followingList|FollowCreatorView" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`
|
|
- 기대 결과: `전체` 버튼이 기존 `FollowCreatorView` 라우팅으로 연결된다.
|
|
|
|
### Phase 4: 팔로잉 탭 API 호출 연결과 데이터 상태 세팅
|
|
|
|
- [x] **Task 4.1: 팔로잉 탭 진입 시 API 호출 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- 작업 내용:
|
|
- `MainHomeFollowingView`에 `@StateObject private var viewModel = MainHomeFollowingViewModel()`를 추가한다.
|
|
- 최초 `onAppear`에서 `viewModel.hasLoaded == false`일 때만 `viewModel.fetchFollowing()`을 호출한다.
|
|
- 이 단계에서는 새 섹션 UI를 추가하지 않고, 이미 존재하는 팔로잉 크리에이터 섹션에 `viewModel.response?.followingCreators ?? []`를 연결한다.
|
|
- API 성공 시 `MainHomeFollowingViewModel.response`에 `HomeFollowingTabResponse`가 세팅되는 흐름을 먼저 확보한다.
|
|
- `isLoginRequired`, `message`, `hasLoaded`, `isLoading` 상태는 ViewModel의 기존 세팅 흐름을 사용한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "StateObject private var viewModel = MainHomeFollowingViewModel|onAppear|hasLoaded|fetchFollowing|response\\?\\.followingCreators|isLoading|message|MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- 기대 결과: 팔로잉 탭 진입 시 ViewModel이 생성되고, 최초 진입에서 API 호출 후 응답 데이터가 기존 크리에이터 섹션에 전달되는 흐름이 확인된다.
|
|
|
|
- [x] **Task 4.2: 팔로잉 API 호출 빌드 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`
|
|
- 작업 내용:
|
|
- 추가 섹션 UI 구현 전에 API 호출 연결만으로 컴파일이 깨지지 않는지 확인한다.
|
|
- 로그인 guard가 유지되어 비로그인 상태에서는 팔로잉 탭 전환과 API 호출이 발생하지 않는지 코드 흐름을 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
|
|
- 기대 결과: 빌드가 성공하고, `MainHomeView`의 팔로잉 탭 로그인 guard가 유지된다.
|
|
|
|
### Phase 5: On Air 섹션
|
|
|
|
- [x] **Task 5.1: On Air 섹션 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift`
|
|
- 작업 내용:
|
|
- `onAirLives`가 비어 있으면 parent에서 섹션을 렌더링하지 않는다.
|
|
- 섹션 타이틀은 `I18n.HomeFollowing.onAirTitle`을 사용한다.
|
|
- 각 item은 프로필 이미지, 닉네임, 라이브 제목, `startedAtUtc` 기준 경과 시간을 표시한다.
|
|
- 시간 표시는 기존 `DateParser.relativeTimeText(fromUTC:fallback:now:)` 패턴을 우선 사용한다.
|
|
- item 탭 시 `onTapLive(liveId)`를 호출한다.
|
|
- 카드 루트에는 고정 width/height를 강제하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingLiveSection|onAirTitle|FollowingLiveResponse|startedAtUtc|DateParser|onTapLive|DownsampledKFImage|ScrollView\\(\\.horizontal" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift`
|
|
- 기대 결과: On Air 섹션의 데이터 표시와 live tap callback이 확인된다.
|
|
|
|
### Phase 6: 최근 대화 섹션
|
|
|
|
- [x] **Task 6.1: 최근 대화 섹션 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingChatSection.swift`
|
|
- 확인: `SodaLive/Sources/Chat/Talk/TalkView.swift`
|
|
- 확인: `SodaLive/Sources/Chat/Talk/TalkItemView.swift`
|
|
- 작업 내용:
|
|
- `recentChats`가 비어 있으면 parent에서 섹션을 렌더링하지 않는다.
|
|
- 섹션 타이틀은 `I18n.HomeFollowing.recentChatTitle`을 사용한다.
|
|
- 각 item은 `targetImageUrl`, `targetName`, `lastMessage`, `lastMessageAt`을 표시한다.
|
|
- `chatType == "DM"`이면 `Direct` 태그를 표시한다.
|
|
- `lastMessageAt`은 UTC 기준 문자열로 받아 상대 시간 또는 기존 Talk item과 맞는 시간 label로 표시한다.
|
|
- item 탭 시 `onTapChatRoom(roomId, chatType)`를 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingChatSection|recentChatTitle|ChatRoomListItemResponse|targetImageUrl|targetName|lastMessage|lastMessageAt|chatType == \"DM\"|Direct|onTapChatRoom" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingChatSection.swift`
|
|
- 기대 결과: 최근 대화 item 필드, `Direct` 태그 조건, 채팅방 tap callback이 확인된다.
|
|
|
|
- [x] **Task 6.2: 채팅방 진입 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 확인: `SodaLive/Sources/App/AppStep.swift`
|
|
- 확인: `SodaLive/Sources/ContentView.swift`
|
|
- 작업 내용:
|
|
- `MainHomeView`에 `onTapChatRoom: (Int, String) -> Void` callback을 추가한다.
|
|
- `MainHomeFollowingView`까지 callback을 전달한다.
|
|
- `MainView`에서 `chatType == "DM"`이면 `AppState.shared.setAppStep(step: .userCreatorChatRoom(roomId: roomId))`로 구현한다.
|
|
- `MainView`에서 Direct가 아니면 기존처럼 `AppState.shared.setAppStep(step: .chatRoom(id: roomId))`로 구현한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapChatRoom|chatRoom\\(id:|ChatRoomView" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`
|
|
- 기대 결과: 최근 대화 Direct item tap은 `UserCreatorChatRoomView`, Direct가 아닌 item tap은 기존 채팅방 화면으로 연결된다.
|
|
|
|
### Phase 7: 이달의 스케줄 섹션
|
|
|
|
- [x] **Task 7.1: 이달의 스케줄 섹션 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingScheduleSection.swift`
|
|
- 작업 내용:
|
|
- `monthlySchedules`가 비어 있으면 parent에서 섹션을 렌더링하지 않는다.
|
|
- 섹션 타이틀은 `I18n.HomeFollowing.monthlyScheduleTitle`을 사용한다.
|
|
- 각 item은 날짜 영역, 크리에이터 프로필/닉네임, 제목, 활동 타입 tag, 시간 또는 `On Air` 상태를 표시한다.
|
|
- `scheduledAtUtc`가 오늘이면 날짜 영역에 `오늘`을 표시한다.
|
|
- `isOnAir == true`이면 우측 상태를 `On Air`로 표시하고 `Color.soda400`을 사용한다.
|
|
- `isOnAir == true`이면 좌측 accent line을 표시한다.
|
|
- `CreatorActivityType.live`, `.liveReplay`, `.audio`, `.community`를 tag 문구로 매핑한다.
|
|
- item 탭 시 `onTapSchedule(type, targetId)`를 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingScheduleSection|monthlyScheduleTitle|FollowingScheduleResponse|scheduledAtUtc|isOnAir|Color.soda400|CreatorActivityType|onTapSchedule|오늘" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingScheduleSection.swift`
|
|
- 기대 결과: 스케줄 카드 표시, On Air 강조, schedule tap callback이 확인된다.
|
|
|
|
- [x] **Task 7.2: 스케줄 상세 진입 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 작업 내용:
|
|
- `MainHomeView`에 `onTapFollowingSchedule: (CreatorActivityType, Int) -> Void` callback을 추가한다.
|
|
- `.live`, `.liveReplay`은 기존 live 입장 callback으로 연결한다.
|
|
- `.audio`는 `AppState.shared.setAppStep(step: .contentDetail(contentId: targetId))` 흐름으로 연결한다.
|
|
- `.community`는 기존 커뮤니티 진입 정책이 `creatorId` 기반인지 `postId` 기반인지 구현 전 확인한다. `targetId`가 creator id로 내려오는 경우에만 `.creatorCommunityAll(creatorId: targetId)`로 연결한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapFollowingSchedule|CreatorActivityType|contentDetail|creatorCommunityAll|enterLiveRoom" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 기대 결과: schedule type별 routing 분기가 확인된다.
|
|
|
|
### Phase 8: 최근 소식 랭킹 카드
|
|
|
|
- [x] **Task 8.1: 최근 소식 랭킹 카드 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift`
|
|
- 작업 내용:
|
|
- Figma `24:5717` 기준 카드 형태로 만든다.
|
|
- `CREATOR_RANKING`은 `FollowingCreatorRankingNewsResponse`를 사용한다.
|
|
- `CONTENT_RANKING`은 `FollowingContentRankingNewsResponse`를 사용한다.
|
|
- `rank` 값을 `N위`로 표시하고 `Color.soda400`로 강조한다.
|
|
- 타입과 payload가 맞지 않으면 parent에서 렌더링하지 않는다.
|
|
- card tap 시 creator ranking은 `onTapCreator(creatorId)`, content ranking은 `onTapContent(contentId)`를 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingRankingNewsCard|FollowingCreatorRankingNewsResponse|FollowingContentRankingNewsResponse|rank|Color.soda400|onTapCreator|onTapContent|24:5717" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift`
|
|
- 기대 결과: 랭킹 최근 소식 카드와 타입별 tap callback이 확인된다.
|
|
|
|
### Phase 9: 최근 소식 커뮤니티 카드
|
|
|
|
- [x] **Task 9.1: 최근 소식 커뮤니티 카드 조립**
|
|
- 대상 파일:
|
|
- 생성 또는 수정: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- 확인: `SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift`
|
|
- 작업 내용:
|
|
- `COMMUNITY_POST`는 `communityPost` payload가 있을 때만 렌더링한다.
|
|
- 표시 값은 `postId`, `creatorProfileImage`, `creatorNickname`, `imageUrl`, `content`, `createdAt`, `likeCount`, `commentCount`를 사용한다.
|
|
- 기존 `CommunityPostCard`를 재사용할 경우 `audioUrl = nil`, `price = 0`, `existOrdered = true`, `isLike = false`로 매핑한다.
|
|
- 반응 정보는 댓글 아이콘/댓글 수를 먼저, 좋아요 아이콘/좋아요 수를 뒤에 표시한다.
|
|
- `createdAt`은 기존 `CommunityPostCard`의 `createdAt` 표시 형식에 맞춰 전달한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "COMMUNITY_POST|communityPost|CommunityPostCard|postId|creatorProfileImage|likeCount|commentCount|audioUrl: nil|price: 0|existOrdered: true" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- 기대 결과: community post payload가 기존 community card 표시값으로 매핑된다.
|
|
|
|
- [x] **Task 9.2: 커뮤니티 게시글 tap 라우팅 제약 처리**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/App/AppStep.swift`
|
|
- 확인: `SodaLive/Sources/ContentView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 확인: `SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift`
|
|
- 작업 내용:
|
|
- `postId` 단독으로 게시글 상세/댓글 화면에 진입 가능한 기존 route가 있는지 확인한다.
|
|
- `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`를 확인한다.
|
|
- `COMMUNITY_POST` tap은 `postId` 기준 커뮤니티 게시글 상세 진입으로 연결한다.
|
|
- 잘못된 creator 화면이나 수정 화면으로 이동하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "creatorChannelCommunityPostDetail|CreatorChannelCommunityPostDetailView|handleRecommendationCommunityPostTap" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 기대 결과: `postId` 단독 커뮤니티 게시글 상세 route와 기존 홈 추천 탭 커뮤니티 게시글 handler가 확인된다.
|
|
|
|
### Phase 10: 최근 소식 오디오/화보 콘텐츠 카드
|
|
|
|
- [x] **Task 10.1: 최근 소식 콘텐츠 카드 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingContentNewsCard.swift`
|
|
- 작업 내용:
|
|
- Figma `1229:27212` 오디오 카드와 `1229:27213` 화보 카드 형태를 하나의 카드 컴포넌트에서 variant로 처리한다.
|
|
- `AUDIO_CONTENT`는 정사각형 썸네일 비율과 `I18n.HomeFollowing.audioTag`를 사용한다.
|
|
- `PHOTO_CONTENT`는 세로형 썸네일 비율과 `I18n.HomeFollowing.photoTag`를 사용한다.
|
|
- `contentImageUrl`, `creatorProfileImageUrl`, `creatorNickname`, `title`, `visibleFromAtUtc` 표시값을 받는다.
|
|
- card tap 시 `onTapContent(contentId)`를 호출한다.
|
|
- 루트 container에는 고정 width를 사용하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingContentNewsCard|audioTag|photoTag|contentImageUrl|creatorProfileImageUrl|creatorNickname|visibleFromAtUtc|onTapContent|aspectRatio" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingContentNewsCard.swift`
|
|
- 기대 결과: 오디오/화보 variant와 콘텐츠 상세 callback이 확인된다.
|
|
|
|
### Phase 11: 화면 조립, empty/error 상태, 상세 진입 callback 연결
|
|
|
|
- [x] **Task 11.1: 팔로잉 탭 화면 조립**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 작업 내용:
|
|
- placeholder를 제거한다.
|
|
- Phase 4에서 연결한 `MainHomeFollowingViewModel`과 최초 `fetchFollowing()` 호출 흐름은 유지한다.
|
|
- loading, loginRequired, message, content 상태를 분기한다.
|
|
- content 상태에서는 PRD 순서대로 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식을 조립한다.
|
|
- 각 섹션 배열이 비어 있으면 해당 섹션을 렌더링하지 않는다.
|
|
- 모든 섹션 배열이 비어 있으면 `MainHomeFollowingEmptyStateView(message: I18n.HomeFollowing.emptyStateMessage)`를 표시한다.
|
|
- API 실패 시 `MainHomeFollowingEmptyStateView(message: I18n.HomeFollowing.loadFailedMessage)`를 표시한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "MainHomeFollowingViewModel|fetchFollowing|MainHomeFollowingCreatorSection|MainHomeFollowingLiveSection|MainHomeFollowingChatSection|MainHomeFollowingScheduleSection|MainHomeFollowingNewsSection|MainHomeFollowingEmptyStateView|emptyStateMessage|loadFailedMessage|MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 기대 결과: `MainPlaceholderTabView`는 검색되지 않고, 팔로잉 화면 상태 분기와 섹션 조립이 확인된다.
|
|
|
|
- [x] **Task 11.2: empty/login required view 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingEmptyStateView.swift`
|
|
- 작업 내용:
|
|
- message를 받아 검정 배경 위에 표시한다.
|
|
- 로그인 필요 상태에서는 로그인 버튼을 표시할 수 있도록 optional action을 받는다.
|
|
- 전체 루트에는 고정 width/height를 사용하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingEmptyStateView|message|loginButtonTitle|Button|Color.black|frame\\(maxWidth: \\.infinity" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingEmptyStateView.swift`
|
|
- 기대 결과: empty/API 실패/로그인 필요 상태 공용 안내 view가 확인된다.
|
|
|
|
- [x] **Task 11.3: 최근 소식 섹션 조립**
|
|
- 대상 파일:
|
|
- 생성 또는 수정: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- 작업 내용:
|
|
- `recentNews`를 순서대로 렌더링한다.
|
|
- `visibleFromAtUtc`를 모든 item의 표시 시간 기준으로 사용한다.
|
|
- `.creatorRanking`은 `creatorRanking` payload가 있을 때만 `MainHomeFollowingRankingNewsCard`를 표시한다.
|
|
- `.contentRanking`은 `contentRanking` payload가 있을 때만 `MainHomeFollowingRankingNewsCard`를 표시한다.
|
|
- `.communityPost`는 `communityPost` payload가 있을 때만 community card를 표시한다.
|
|
- `.audioContent`는 `audioContent` payload가 있을 때만 `MainHomeFollowingContentNewsCard`를 표시한다.
|
|
- `.photoContent`는 `photoContent` payload가 있을 때만 `MainHomeFollowingContentNewsCard`를 표시한다.
|
|
- `.unknown`은 렌더링하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct MainHomeFollowingNewsSection|recentNewsTitle|visibleFromAtUtc|creatorRanking|contentRanking|communityPost|audioContent|photoContent|unknown|MainHomeFollowingRankingNewsCard|MainHomeFollowingContentNewsCard|CommunityPostCard" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- 기대 결과: 최근 소식 타입별 payload 분기와 unknown 숨김 정책이 확인된다.
|
|
|
|
- [x] **Task 11.4: 상세 진입 callback 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 작업 내용:
|
|
- `MainHomeView`에 팔로잉 탭용 callback을 추가한다.
|
|
- live는 기존 `handleRecommendationLiveTap` 또는 같은 login/external navigation guard를 재사용한다.
|
|
- creator는 기존 `handleRecommendationCreatorTap`을 재사용한다.
|
|
- content는 기존 `handleRecommendationContentTap`을 재사용한다.
|
|
- chat room은 `.chatRoom(id:)`로 이동한다.
|
|
- following all은 `.followingList`로 이동한다.
|
|
- community post는 Task 9.2 확인 결과에 따라 `postId` 단독 route인 `.creatorChannelCommunityPostDetail(postId:)`로 이동한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapFollowingAll|onTapChatRoom|onTapFollowingSchedule|handleRecommendationLiveTap|handleRecommendationCreatorTap|handleRecommendationContentTap|followingList|chatRoom\\(id:" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 기대 결과: 팔로잉 탭의 상세 진입 callback이 기존 MainView guard/route에 연결된다.
|
|
|
|
### Phase 12: 프로젝트 등록, 빌드, UI 검증
|
|
|
|
- [x] **Task 12.1: Xcode 프로젝트 포함 여부 확인**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive.xcodeproj/project.pbxproj`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/**`
|
|
- 작업 내용:
|
|
- 신규 Swift 파일들이 Xcode target에 포함됐는지 확인한다.
|
|
- 파일이 target에 자동 포함되지 않는 프로젝트 구조이면 `project.pbxproj`에 필요한 file reference/build file을 추가한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "HomeFollowingTabResponse|MainHomeFollowingApi|MainHomeFollowingRepository|MainHomeFollowingViewModel|MainHomeFollowingCreatorSection|MainHomeFollowingNewsSection|MainHomeFollowingEmptyStateView" SodaLive.xcodeproj/project.pbxproj SodaLive/Sources/V2/Main/Home/Following`
|
|
- 기대 결과: 신규 파일 참조와 소스 파일이 확인된다.
|
|
|
|
- [x] **Task 12.2: 정적 검색 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/**`
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 확인: `SodaLive/Sources/I18n/I18n.swift`
|
|
- 작업 내용:
|
|
- PRD 핵심 키워드가 코드에 반영됐는지 확인한다.
|
|
- `MainPlaceholderTabView(title: "팔로잉")`이 남아 있지 않은지 확인한다.
|
|
- Figma localhost asset URL이 들어가지 않았는지 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "/api/v2/home/following|HomeFollowingTabResponse|ChatRoomListItemResponse|CreatorActivityType|FollowingNewsType|visibleFromAtUtc|팔로잉 소식이 아직 없어요|팔로잉 정보를 불러오지 못했습니다|Color.soda400|followingList|chatRoom\\(id:" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/I18n/I18n.swift`
|
|
- 기대 결과: API, 모델, 문구, 라우팅, 색상 요구사항이 확인된다.
|
|
|
|
- [x] **Task 12.3: 빌드 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive.xcworkspace`
|
|
- 작업 내용:
|
|
- 앱 빌드를 실행해 신규 Swift 파일과 라우팅 변경의 컴파일 오류를 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
|
|
- 기대 결과: 빌드가 성공한다.
|
|
|
|
- [ ] **Task 12.4: 수동 UI 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/V2/Main/Home/Following/**`
|
|
- 확인: Figma `24:5682`
|
|
- 작업 내용:
|
|
- 비로그인 상태에서 홈 `팔로잉` 탭을 탭하면 로그인 화면으로 이동하는지 확인한다.
|
|
- 로그인 상태에서 홈 `팔로잉` 탭 진입 시 API 호출과 섹션 표시가 동작하는지 확인한다.
|
|
- 팔로잉 크리에이터가 있으면 섹션 마지막에 `전체` 버튼이 표시되고, 팔로잉 크리에이터가 없으면 섹션과 `전체` 버튼이 표시되지 않는지 확인한다.
|
|
- `전체` 버튼 text color가 `Color.soda400`로 보이는지 확인한다.
|
|
- `CREATOR_RANKING`, `CONTENT_RANKING`, `COMMUNITY_POST`, `AUDIO_CONTENT`, `PHOTO_CONTENT`가 타입별 UI로 표시되는지 확인한다.
|
|
- 루트/섹션 container가 고정 width/height 때문에 작은 화면에서 깨지지 않는지 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: 수동 확인
|
|
- 기대 결과: PRD 성공 기준과 Figma 섹션 순서가 충족된다.
|
|
|
|
### Phase 13: 최근 소식 커뮤니티 게시글 상세 진입 보강
|
|
|
|
- [x] **Task 13.1: COMMUNITY_POST 상세 진입 문서 갱신**
|
|
- 대상 파일:
|
|
- 수정: `docs/20260630_메인_홈_팔로잉_탭/prd.md`
|
|
- 수정: `docs/20260630_메인_홈_팔로잉_탭/plan-task.md`
|
|
- 작업 내용:
|
|
- 현재 코드에 `postId` 단독 커뮤니티 게시글 상세 route가 있음을 기록한다.
|
|
- 기존 `creatorId` 요구 제약 기록을 최신 route 기준으로 보정한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "creatorChannelCommunityPostDetail|COMMUNITY_POST|postId 단독" docs/20260630_메인_홈_팔로잉_탭/prd.md docs/20260630_메인_홈_팔로잉_탭/plan-task.md`
|
|
- 기대 결과: 문서가 `COMMUNITY_POST` 상세 진입 범위와 route를 명시한다.
|
|
|
|
- [x] **Task 13.2: COMMUNITY_POST tap callback 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`
|
|
- 확인: `SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 작업 내용:
|
|
- `MainHomeFollowingNewsSection`에 `onTapCommunityPost` callback을 추가한다.
|
|
- `CommunityPostCard`의 post/comment tap에서 `communityPost.postId`를 callback으로 전달한다.
|
|
- `MainHomeFollowingView`와 `MainHomeView`를 통해 기존 `MainView.handleRecommendationCommunityPostTap(postId:)`로 연결한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapCommunityPost|handleRecommendationCommunityPostTap|creatorChannelCommunityPostDetail|CommunityPostCard" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`
|
|
- 기대 결과: 최근 소식 `COMMUNITY_POST` card tap이 기존 커뮤니티 게시글 상세 route로 연결된다.
|
|
|
|
### Phase 14: 팔로잉 크리에이터 빈 섹션 후속 정리
|
|
|
|
- [x] **Task 14.1: 팔로잉 크리에이터 빈 섹션과 전체 버튼 숨김**
|
|
- 대상 파일:
|
|
- 수정: `docs/20260630_메인_홈_팔로잉_탭/prd.md`
|
|
- 수정: `docs/20260630_메인_홈_팔로잉_탭/plan-task.md`
|
|
- 수정: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 확인: `SodaLive/Sources/I18n/I18n.swift`
|
|
- 작업 내용:
|
|
- 전체 empty 문구가 `I18n.HomeFollowing.emptyStateMessage`로 다국어 처리되어 있음을 확인한다.
|
|
- 기존 문서의 “크리에이터가 없어도 `전체` 버튼 표시” 요구를 “크리에이터가 있을 때만 섹션과 `전체` 버튼 표시”로 갱신한다.
|
|
- `MainHomeFollowingView`에서 `response.followingCreators`가 비어 있으면 `MainHomeFollowingCreatorSection`을 렌더링하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "emptyStateMessage|팔로잉 소식이 아직 없어요|No following updates yet|フォロー中の新着情報" SodaLive/Sources/I18n/I18n.swift`
|
|
- 실행 명령: `rg "if !response\\.followingCreators\\.isEmpty|MainHomeFollowingCreatorSection" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`
|
|
- 기대 결과: empty 문구의 ko/en/ja 다국어 처리와 팔로잉 크리에이터 빈 배열 시 섹션 렌더링 차단이 확인된다.
|
|
|
|
## 검증 기록
|
|
|
|
- 2026-06-30: `docs/agent-guides/documentation-policy.md`, `build-test-verification.md`, `code-style.md`를 확인해 계획 문서 경로, Phase/checklist 형식, 검증 명령 기준을 반영했다.
|
|
- 2026-06-30: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`가 placeholder 상태임을 확인했다.
|
|
- 2026-06-30: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift`의 탭 전환이 `TextTabBar` binding 기반임을 확인해 custom binding setter 방식으로 로그인 guard 계획을 세웠다.
|
|
- 2026-06-30: `SodaLive/Sources/ContentView.swift`에서 `.followingList -> FollowCreatorView()`, `.chatRoom(id:) -> ChatRoomView(roomId:)` 라우팅을 확인했다.
|
|
- 2026-06-30: `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`와 `AppState`의 pending community post 구조가 `creatorId`를 요구함을 확인해 `COMMUNITY_POST` tap 라우팅 제약을 계획에 명시했다.
|
|
- 2026-06-30: Phase 1-2 구현을 진행해 `MainHomeView`의 팔로잉 탭 custom `Binding` guard, `MainView.handleFollowingTabSelection()`, 팔로잉 응답 모델/API/Repository/ViewModel, `I18n.HomeFollowing` 문구를 추가했다.
|
|
- 2026-06-30: `rg "onSelectFollowingTab|Binding\(|selectedTab = \.following|setAppStep\(step: \.login\)|TextTabBar" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 팔로잉 탭 guard 연결을 확인했다.
|
|
- 2026-06-30: `rg "struct HomeFollowingTabResponse|struct ChatRoomListItemResponse|enum CreatorActivityType|struct FollowingNewsResponse|struct FollowingCreatorRankingNewsResponse|struct FollowingContentNewsResponse|struct FollowingContentRankingNewsResponse|struct FollowingCommunityPostNewsResponse|enum FollowingNewsType|unknown\(" SodaLive/Sources/V2/Main/Home/Following/Models/HomeFollowingTabResponse.swift`로 Phase 2 응답 모델과 unknown enum 처리를 확인했다.
|
|
- 2026-06-30: `rg "enum MainHomeFollowingApi|getFollowing|/api/v2/home/following|Authorization|requestPlain|TargetType|final class MainHomeFollowingRepository|MoyaProvider<MainHomeFollowingApi>|AnyPublisher<Response, MoyaError>" SodaLive/Sources/V2/Main/Home/Following/Repository`로 API/Repository 패턴을 확인했다.
|
|
- 2026-06-30: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 빌드 성공을 확인했다. 기존 프로젝트의 duplicate build file/run script warning은 남아 있으나 Phase 1-2 컴파일 오류는 없었다.
|
|
- 2026-06-30: Phase 3 구현을 진행해 `MainHomeFollowingCreatorSection`을 추가하고, `MainHomeView -> MainHomeFollowingView -> MainView.handleFollowingAllTap()` 경로로 `전체` 버튼을 `.followingList` 라우팅에 연결했다.
|
|
- 2026-06-30: `rg "struct MainHomeFollowingCreatorSection|ScrollView\(\.horizontal|followingCreators|onTapCreator|onTapAll|allButtonTitle|Color\.soda400|padding\(\.horizontal, 16\)" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingCreatorSection.swift`로 팔로잉 크리에이터 섹션 요구사항을 확인했다.
|
|
- 2026-06-30: `rg "onTapFollowingAll|followingList|FollowCreatorView" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`로 `전체` 버튼 라우팅 연결을 확인했다.
|
|
- 2026-06-30: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 재실행해 Phase 3 신규 Swift 파일 target 등록과 컴파일 성공을 확인했다.
|
|
- 2026-07-01: 완료된 Phase 3은 유지하고, Phase 4를 팔로잉 탭 API 호출 연결과 데이터 상태 세팅 단계로 변경했다. 이후 섹션 UI 작업은 Phase 5부터 순차 진행하도록 Phase/Task 번호를 재정렬했다.
|
|
- 2026-07-01: Phase 4 구현을 진행해 `MainHomeFollowingView`에 `@StateObject private var viewModel = MainHomeFollowingViewModel()`를 추가하고, 최초 `onAppear`에서 `hasLoaded == false`일 때 `fetchFollowing()`을 호출하도록 연결했다.
|
|
- 2026-07-01: `MainHomeFollowingCreatorSection`의 `followingCreators`를 `viewModel.response?.followingCreators ?? []`로 연결해 API 성공 응답 데이터가 기존 크리에이터 섹션으로 전달되는 흐름을 확보했다.
|
|
- 2026-07-01: `rg "StateObject private var viewModel = MainHomeFollowingViewModel|onAppear|hasLoaded|fetchFollowing|response\?\.followingCreators|isLoading|message|MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift`로 Phase 4 정적 요구사항을 확인했다.
|
|
- 2026-07-01: `rg "onSelectFollowingTab|Binding\(|selectedTab = \.following|setAppStep\(step: \.login\)|TextTabBar" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 비로그인 팔로잉 탭 guard 흐름이 유지됨을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 Phase 4 API 호출 연결 후 빌드 성공을 확인했다. 기존 AppIntents metadata extraction warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 5 구현을 진행해 `MainHomeFollowingLiveSection`을 추가하고, `FollowingLiveResponse`의 프로필 이미지/닉네임/라이브 제목/`startedAtUtc` 상대 시간 표시와 `onTapLive(liveId)` callback을 연결했다.
|
|
- 2026-07-01: `rg "struct MainHomeFollowingLiveSection|onAirTitle|FollowingLiveResponse|startedAtUtc|DateParser|onTapLive|DownsampledKFImage|ScrollView\(\.horizontal|!onAirLives\.isEmpty" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift`로 Phase 5 정적 요구사항을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 Phase 5 신규 Swift 파일 컴파일과 빌드 성공을 확인했다. 기존 Crashlytics dSYM/AppIntents metadata extraction warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: 사용자 요청에 따라 Phase 11까지 기다리지 않고 `MainHomeFollowingView`에 `MainHomeFollowingLiveSection`을 조기 조립했다. `viewModel.response?.onAirLives ?? []`를 전달하고, `MainHomeView`의 기존 `onTapLive` callback을 팔로잉 On Air 섹션까지 연결했다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingLiveSection.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 바로 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "MainHomeFollowingLiveSection.swift|MainHomeFollowingLiveSection.swift in Sources|MainHomeFollowingLiveSection\(" SodaLive.xcodeproj/project.pbxproj SodaLive/Sources/V2/Main/Home`로 Xcode 프로젝트 등록과 화면 조립을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 재실행해 프로젝트 등록 후 빌드 성공을 확인했다. 기존 duplicate build file/run script/Crashlytics dSYM/AppIntents warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Figma `24:5693`, `24:5696`을 확인해 On Air 섹션을 캡슐형 라이브 아이템으로 수정했다. `75x75` 프로필, 검정 `LIVE` pill, `#62CFFF` 2pt 캡슐 테두리, `14/18/14pt` 텍스트 계층, leading 14/item gap 8 간격을 반영했다.
|
|
- 2026-07-01: `rg "CGSize\(width: 75, height: 75\)|Color\(hex: \"62CFFF\"\)|Text\(\"LIVE\"\)|frame\(height: 18\)|padding\(\.trailing, 22\)|padding\(\.leading, SodaSpacing\.s14\)|spacing: SodaSpacing\.s8|appFont\(size: 18, weight: \.bold\)|appFont\(size: 14, weight: \.regular\)" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift`로 Figma 핵심 수치 반영을 확인했다.
|
|
- 2026-07-01: `rg "localhost:3845|img_live|frame\(width: 50, height: 18\)|CGSize\(width: 62, height: 62\)|RoundedRectangle" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingLiveSection.swift` 결과가 없어 Figma localhost asset 미사용과 기존 배지/카드 형태 제거를 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 Figma 반영 후 빌드 성공을 확인했다. 기존 Crashlytics dSYM/AppIntents warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 6 구현을 진행해 `MainHomeFollowingChatSection`을 추가하고, `recentChats`를 Figma `24:5698`, `24:5701` 기준의 가로 카드 섹션으로 표시하도록 조립했다. `chatType == "DM"`일 때만 `Direct` 태그를 표시하고, `lastMessageAt`은 `DateParser.relativeTimeText(fromUTC:fallback:now:)`로 표시한다.
|
|
- 2026-07-01: `MainHomeFollowingView -> MainHomeView -> MainView.handleFollowingChatRoomTap(roomId:)` 경로로 최근 대화 item tap을 `.chatRoom(id:)` 라우팅에 연결했다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingChatSection.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 바로 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "struct MainHomeFollowingChatSection|recentChatTitle|ChatRoomListItemResponse|targetImageUrl|targetName|lastMessage|lastMessageAt|DateParser|onTapChatRoom|ScrollView\(\.horizontal|Color\.gray900|frame\(width: 288|CGSize\(width: 64, height: 64\)|chatType == \"DM\"|Direct" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingChatSection.swift`로 최근 대화 섹션 핵심 요구사항과 Direct 태그 표시 조건을 확인했다.
|
|
- 2026-07-01: `rg "onTapChatRoom|MainHomeFollowingChatSection|response\?\.recentChats|chatRoom\(id:|ChatRoomView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`로 최근 대화 섹션 조립과 채팅방 라우팅 연결을 확인했다.
|
|
- 2026-07-01: `rg "MainHomeFollowingChatSection.swift|MainHomeFollowingChatSection.swift in Sources" SodaLive.xcodeproj/project.pbxproj`로 신규 Swift 파일의 Xcode 프로젝트 등록을 확인했다.
|
|
- 2026-07-01: `if rg "localhost:3845|unread" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingChatSection.swift; then exit 1; else true; fi`를 실행해 Figma localhost asset과 별도 unread 표시가 들어가지 않았음을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`를 실행해 Phase 6 구현 후 빌드 성공을 확인했다. 기존 duplicate build file/Crashlytics dSYM/AppIntents warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Figma `24:5703`, `24:5706`, `24:5707`을 확인해 Phase 7 이달의 스케줄 섹션을 구현했다. 카드 `Color.gray900`, 14pt radius/padding, 53x53 날짜 영역, 20pt 프로필, 16pt 제목/상태, gray700 활동 tag를 반영했고, 오늘/On Air dot 및 On Air 상태/좌측 accent는 `Color.soda400`, 일반 schedule dot/line/status는 gray 계열로 분리했다.
|
|
- 2026-07-01: `MainHomeFollowingView -> MainHomeView -> MainView.handleFollowingScheduleTap(type:targetId:)` 경로로 스케줄 tap callback을 연결했다. `.live`/`.liveReplay`은 기존 live 입장, `.audio`는 콘텐츠 상세, `.community`는 기존 정책에 따라 `creatorCommunityAll(creatorId:)`, `.unknown`은 no-op 처리했다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingScheduleSection.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "MainHomeFollowingScheduleSection.swift|MainHomeFollowingScheduleSection.swift in Sources|onTapFollowingSchedule|onTapSchedule|monthlySchedules|CreatorActivityType|contentDetail|creatorCommunityAll|enterLiveRoom|On Air|a hh:mm|오늘|Color.soda400|Color.gray500|localhost:3845" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift SodaLive.xcodeproj/project.pbxproj`로 Phase 7 UI, 라우팅, 프로젝트 등록, Figma localhost asset 미사용을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`는 기존 DerivedData `build.db` lock으로 실패했으나, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase7" build`로 재실행해 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: 사용자 피드백에 따라 Figma `24:5703`의 왼쪽 스케줄 timeline을 재확인했다. 기존 item별 `TimelineGuideView`는 카드 사이 gap에서 line이 끊겨 보이고 dot 위치가 Figma보다 카드에 가까웠으므로, list 전체 overlay의 `ScheduleTimelineLine`과 item별 `TimelineDotView` 구조로 변경하고 item gap을 4pt로 맞췄다.
|
|
- 2026-07-01: `rg "VStack\(alignment: \.leading, spacing: SodaSpacing\.s4\)|ScheduleTimelineLine|TimelineDotView|padding\(\.vertical, 52\)|opacity\(monthlySchedules\.count > 1|HStack\(alignment: \.center, spacing: SodaSpacing\.s14\)|localhost:3845" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingScheduleSection.swift`로 연속 line/dot 구조와 Figma localhost asset 미사용을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase7" build`를 실행해 timeline 수정 후 빌드 성공을 확인했다. 기존 warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: `MainHomeFollowingView` 및 팔로잉 하위 컴포넌트의 런타임 노출 하드코딩 문구(`팔로잉`, `LIVE`, `Direct`, `오늘`, `On Air`, 스케줄 활동 태그)를 `I18n.HomeFollowing` 참조로 전환했다. Preview 샘플 문자열과 API 응답 기반 동적 문자열은 런타임 고정 문구가 아니므로 유지했다.
|
|
- 2026-07-01: Phase 8 구현을 진행해 Figma `24:5717` 기준 `MainHomeFollowingRankingNewsCard`를 추가했다. `CREATOR_RANKING`은 `FollowingCreatorRankingNewsResponse`, `CONTENT_RANKING`은 `FollowingContentRankingNewsResponse` initializer로 분리하고, `rank`를 `N위`로 표시하며 `Color.soda400`로 강조했다.
|
|
- 2026-07-01: Phase 9 구현을 진행해 `MainHomeFollowingNewsSection`을 추가했다. `COMMUNITY_POST`는 `communityPost` payload가 있을 때만 `CommunityPostCard`로 렌더링하고, `postId`, `creatorProfileImage`, `creatorNickname`, `imageUrl`, `content`, `createdAt`, `likeCount`, `commentCount`를 매핑했다. `audioUrl = nil`, `price = 0`, `existOrdered = true`, `isLike = false`로 설정했다.
|
|
- 2026-07-01: `AppState.setPendingCommunityCommentDeepLink(creatorId:postId:)`, `AppState.consumePendingCommunityCommentPostId(creatorId:)`, `AppStep.creatorCommunityAll(creatorId:)`, `ContentView`의 `CreatorCommunityAllView(creatorId:)`, `CreatorCommunityAllView`의 pending post 처리 흐름을 확인했다. 기존 직접 진입 경로는 `creatorId`를 요구하고 `FollowingCommunityPostNewsResponse`에는 `creatorId`가 없으므로 Phase 9에서는 `COMMUNITY_POST` 상세/댓글 진입을 연결하지 않았다.
|
|
- 2026-07-01: `rg "COMMUNITY_POST|communityPost|CommunityPostCard|postId|creatorProfileImage|likeCount|commentCount|audioUrl: nil|price: 0|existOrdered: true|isLike: false|relativeCreatedAtText|DateParser.relativeTimeText" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`로 Phase 9.1 매핑을 확인했다.
|
|
- 2026-07-01: `rg "setPendingCommunityCommentDeepLink|consumePendingCommunityCommentPostId|creatorCommunityAll|case chatRoom|case creatorCommunity" SodaLive/Sources/App/AppState.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift`로 Phase 9.2 라우팅 제약을 확인했다.
|
|
- 2026-07-01: `rg "localhost:3845|setAppStep\(step: \.creatorCommunityAll|setPendingCommunityCommentDeepLink|onTapComment:" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift` 결과가 없어 Figma localhost asset 미사용과 `COMMUNITY_POST` 상세/댓글 진입 미연결을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase9Test"`는 `Scheme SodaLive is not currently configured for the test action.`으로 실패해 테스트 액션 미구성 상태를 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase9" build`를 실행해 Phase 9 신규 Swift 파일 컴파일과 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingRankingNewsCard.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "struct MainHomeFollowingRankingNewsCard|FollowingCreatorRankingNewsResponse|FollowingContentRankingNewsResponse|rank|Color.soda400|onTapCreator|onTapContent|24:5717" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift docs/20260630_메인_홈_팔로잉_탭/plan-task.md`로 Phase 8 정적 요구사항을 확인했다.
|
|
- 2026-07-01: `rg "localhost:3845|// Figma|/\*|english\(" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift` 결과가 없어 Figma localhost asset, 불필요한 주석, 잘못된 font family 호출이 없음을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase8" build`를 실행해 Phase 8 신규 Swift 파일 컴파일과 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Figma `1229:27212`, `1229:27213`을 확인해 Phase 10 최근 소식 오디오/화보 콘텐츠 카드 `MainHomeFollowingContentNewsCard`를 추가했다. 오디오는 88x88 정사각형 썸네일과 `I18n.HomeFollowing.audioTag`, 화보는 88x118 세로형 썸네일과 `I18n.HomeFollowing.photoTag`를 variant로 처리하고, `contentImageUrl`, `creatorProfileImageUrl`, `creatorNickname`, `title`, `visibleFromAtUtc` 표시 및 `onTapContent(contentId)` callback을 반영했다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingContentNewsCard.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "struct MainHomeFollowingContentNewsCard|audioTag|photoTag|contentImageUrl|creatorProfileImageUrl|creatorNickname|visibleFromAtUtc|onTapContent|aspectRatio" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingContentNewsCard.swift`로 Phase 10 정적 요구사항을 확인했다.
|
|
- 2026-07-01: `rg "localhost:3845|imgRectangle|http://localhost" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingContentNewsCard.swift` 결과가 없어 Figma localhost asset 미사용을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase10Test"`는 `Scheme SodaLive is not currently configured for the test action.`으로 실패해 테스트 액션 미구성 상태를 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase10" build`를 실행해 Phase 10 신규 Swift 파일 컴파일과 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 11 구현 전 `rg "struct MainHomeFollowingEmptyStateView" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingEmptyStateView.swift`는 파일 없음으로 실패했고, `rg "MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`는 placeholder 잔존을 확인했으며, `rg "MainHomeFollowingNewsSection\(" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift`는 결과가 없어 최근 소식 미조립 상태를 확인했다.
|
|
- 2026-07-01: Phase 11 구현을 진행해 `MainHomeFollowingEmptyStateView`를 추가하고, `MainHomeFollowingView`에서 `isLoading`, `isLoginRequired`, `message`, `response` 상태를 분기하도록 조립했다. content 상태에서는 팔로잉 크리에이터, On Air, 최근 대화, 이달의 스케줄, 최근 소식 순서로 렌더링하고, `MainPlaceholderTabView`를 제거했다.
|
|
- 2026-07-01: `MainHomeFollowingView`에 `onTapContent` callback을 추가하고 `MainHomeView`에서 기존 `onTapContent`를 전달해 최근 소식의 콘텐츠 랭킹/오디오/화보 tap이 기존 `MainView.handleRecommendationContentTap(contentId:)` 경로를 재사용하도록 연결했다. `COMMUNITY_POST`는 기존 Task 9.2 제약에 따라 상세 진입을 연결하지 않았다.
|
|
- 2026-07-01: `SodaLive.xcodeproj/project.pbxproj`에 `MainHomeFollowingEmptyStateView.swift` 파일 참조와 Sources build file을 추가해 Xcode navigator와 target compile sources에서 확인 가능하도록 등록했다.
|
|
- 2026-07-01: `rg "struct MainHomeFollowingEmptyStateView|message|loginButtonTitle|Button|Color.black|frame\(maxWidth: \.infinity|SodaRadius\.r14" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingEmptyStateView.swift`로 empty/login required view 요구사항을 확인했다.
|
|
- 2026-07-01: `rg "MainHomeFollowingViewModel|fetchFollowing|ProgressView|MainHomeFollowingCreatorSection|MainHomeFollowingLiveSection|MainHomeFollowingChatSection|MainHomeFollowingScheduleSection|MainHomeFollowingNewsSection|MainHomeFollowingEmptyStateView|emptyStateMessage|loadFailedMessage|MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift` 및 `if rg "MainPlaceholderTabView" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift; then exit 1; else true; fi`로 상태 분기/섹션 조립과 placeholder 제거를 확인했다.
|
|
- 2026-07-01: `rg "onTapContent|MainHomeFollowingView\(|handleRecommendationContentTap|onTapFollowingAll|onTapChatRoom|onTapFollowingSchedule" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 팔로잉 탭 상세 진입 callback이 기존 route에 연결됨을 확인했다.
|
|
- 2026-07-01: 최초 Phase 11 빌드는 `MainHomeFollowingEmptyStateView.swift`의 존재하지 않는 `SodaRadius.r20` 사용으로 실패했다. `SodaLive/Sources/UI/Theme/Radius.swift`에서 `r4`, `r8`, `r14`만 존재함을 확인하고 `SodaRadius.r14`로 수정했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase11" build`를 재실행해 Phase 11 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 11 리뷰에서 `isLoginRequired` 상태의 로그인 버튼 액션 미연결과 최근 소식 payload mismatch 시 제목만 남을 수 있는 문제를 지적받았다. `MainHomeFollowingView -> MainHomeView -> MainView.handleFollowingLoginTap()` callback을 추가해 로그인 필요 상태의 `MainHomeFollowingEmptyStateView`에 `loginAction`을 전달하고 `.login` route로 연결했다.
|
|
- 2026-07-01: `MainHomeFollowingNewsSection`에 `renderableNews`를 추가해 `.unknown` 및 payload가 없는 news item을 섹션 렌더링 전에 제외했다. `if !renderableNews.isEmpty`와 `ForEach(renderableNews)`를 사용해 실제 카드가 없는 경우 최근 소식 제목만 표시되지 않도록 수정했다.
|
|
- 2026-07-01: `rg "onTapLogin|onTapFollowingLogin|handleFollowingLoginTap|loginAction: onTapLogin|setAppStep\(step: \.login\)" SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 로그인 필요 상태의 버튼 action과 `.login` route 연결을 확인했다.
|
|
- 2026-07-01: `rg "renderableNews|if !renderableNews\.isEmpty|ForEach\(renderableNews\)|case \.unknown|return false" SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift`로 최근 소식 섹션의 렌더 가능 item 필터링과 unknown 숨김을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase11" build`를 재실행해 Phase 11 리뷰 수정 후 빌드 성공을 확인했다. 기존 duplicate build file/run script/Crashlytics dSYM/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 12.1 확인으로 `rg --files "SodaLive/Sources/V2/Main/Home/Following"`를 실행해 팔로잉 탭 신규 Swift 파일 13개가 소스 경로에 존재함을 확인했다.
|
|
- 2026-07-01: `rg "MainHomeFollowing(LiveSection|ChatSection|ScheduleSection|RankingNewsCard|ContentNewsCard|NewsSection|EmptyStateView|CreatorSection)\.swift|HomeFollowingTabResponse\.swift|MainHomeFollowing(Api|Repository|ViewModel)\.swift" SodaLive.xcodeproj/project.pbxproj`로 팔로잉 모델/API/Repository/ViewModel/섹션/카드/empty view 파일 참조와 Sources build file 등록을 확인했다.
|
|
- 2026-07-01: Phase 12.2 정적 검색으로 `/api/v2/home/following`, `HomeFollowingTabResponse`, `ChatRoomListItemResponse`, `CreatorActivityType`, `FollowingNewsType`, `visibleFromAtUtc`, 팔로잉 empty/API 실패 문구, `Color.soda400`, `followingList`, `chatRoom(id:)` 요구사항이 코드에 반영되어 있음을 확인했다.
|
|
- 2026-07-01: `if rg "MainPlaceholderTabView\(title: \"팔로잉\"\)|localhost:3845|http://localhost" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift; then exit 1; else true; fi`로 팔로잉 placeholder와 Figma localhost asset URL이 남아 있지 않음을 확인했다.
|
|
- 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase12" build`를 실행해 Phase 12 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다.
|
|
- 2026-07-01: Phase 12.4 수동 UI 검증 항목 중 실제 비로그인/로그인 탭 이동, `/api/v2/home/following` 실응답 섹션 표시, 타입별 UI 표시 확인은 실행 가능한 시뮬레이터 세션과 로그인 계정/API 데이터가 필요해 이번 정적/빌드 검증 범위에서는 완료하지 않았다. 대신 코드 흐름상 비로그인 guard, API endpoint, 섹션 조립, `전체` 버튼 `Color.soda400`, 타입별 recent news 분기, root/section 고정 width/height 미사용 원칙을 정적 검색과 빌드로 확인했다.
|
|
- 2026-07-01: 독립 explore 점검에서도 `project.pbxproj` 등록, placeholder 제거, Figma localhost asset 미사용, 팔로잉 API/I18n/라우팅 반영을 확인했다. 잔여 리스크로 `MainHomeTab.title`의 기존 탭 타이틀 하드코딩과 계획된 제약사항인 `COMMUNITY_POST` 상세/댓글 진입 미연결을 기록했다.
|
|
- 2026-07-12: `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`, `ContentView`의 `CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)`, `MainView.handleRecommendationCommunityPostTap(postId:)`를 확인해 `postId` 단독 커뮤니티 게시글 상세 진입 route가 있음을 확인했다.
|
|
- 2026-07-12: Phase 13을 추가해 `MainHomeFollowingNewsSection -> MainHomeFollowingView -> MainHomeView -> MainView.handleRecommendationCommunityPostTap(postId:)` 경로로 최근 소식 `COMMUNITY_POST` card/comment tap을 `.creatorChannelCommunityPostDetail(postId:)` 라우팅에 연결했다.
|
|
- 2026-07-12: `rg "onTapCommunityPost|handleRecommendationCommunityPostTap|creatorChannelCommunityPostDetail|onTapPost:|onTapComment:" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 팔로잉 최근 소식 `COMMUNITY_POST` tap callback과 기존 상세 route 연결을 확인했다.
|
|
- 2026-07-12: `git diff --check -- SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/MainHomeView.swift docs/20260630_메인_홈_팔로잉_탭/prd.md docs/20260630_메인_홈_팔로잉_탭/plan-task.md` 실행 결과 출력 없이 종료되어 변경 파일 공백 오류가 없음을 확인했다.
|
|
- 2026-07-12: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataFollowingCommunityPost" build`는 로컬에 team `3A8QGPMRXM`용 iOS Development signing certificate이 없어 코드 서명 단계에서 실패했다.
|
|
- 2026-07-12: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -destination "generic/platform=iOS Simulator" -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataFollowingCommunityPostSimulator" CODE_SIGNING_ALLOWED=NO build`는 의존성 컴파일 중 600초 제한으로 `BUILD INTERRUPTED`되어 완료 여부를 확인하지 못했다.
|
|
- 2026-07-13: 사용자 요청에 따라 전체 empty 문구가 `I18n.HomeFollowing.emptyStateMessage`로 ko/en/ja 다국어 처리되어 있음을 `rg "emptyStateMessage|팔로잉 소식이 아직 없어요|No following updates yet|フォロー中の新着情報" SodaLive/Sources/I18n/I18n.swift`로 확인했다.
|
|
- 2026-07-13: `MainHomeFollowingView`에서 `response.followingCreators`가 비어 있으면 `MainHomeFollowingCreatorSection`을 렌더링하지 않도록 수정해, 팔로잉 크리에이터가 없을 때 상단 `전체` 버튼도 표시되지 않게 했다.
|
|
- 2026-07-13: PRD와 plan-task의 기존 “크리에이터가 없어도 `전체` 버튼 표시” 요구사항을 “크리에이터가 있을 때만 섹션과 `전체` 버튼 표시”로 갱신했다.
|
|
- 2026-07-13: `git diff --check -- SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift docs/20260630_메인_홈_팔로잉_탭/prd.md docs/20260630_메인_홈_팔로잉_탭/plan-task.md` 실행 결과 출력 없이 종료되어 변경 파일 공백 오류가 없음을 확인했다.
|
|
- 2026-07-13: 샌드박스 안의 `xcodebuild`는 Xcode/CoreSimulator/DerivedData/SwiftPM 캐시 접근 제한으로 실패했으나, 승인 후 샌드박스 밖에서 `xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -destination generic/platform=iOS\ Simulator -derivedDataPath /private/tmp/SodaLiveDerivedDataFollowingEmpty CODE_SIGNING_ALLOWED=NO build`를 실행해 `BUILD SUCCEEDED`를 확인했다. 기존 duplicate build file/dependency scan warning과 `LiveRoomViewModel`의 unused variable warning은 남아 있으나 컴파일 오류는 없었다.
|