Files
sodalive-ios/docs/20260630_메인_홈_팔로잉_탭/plan-task.md

49 KiB

메인 홈 팔로잉 탭 구현 계획

기준 문서

  • 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관심 있는 크리에이터를 팔로우해 보세요.를 사용한다.
  • API 실패 문구는 팔로잉 정보를 불러오지 못했습니다.\n잠시 후 다시 시도해 주세요.를 사용한다.
  • 섹션별 배열이 비어 있으면 해당 섹션은 숨긴다.
  • 모든 뷰의 루트 container와 섹션 container에는 불필요한 고정 숫자 width/height를 사용하지 않는다.
  • 프로필 이미지, 썸네일, 아이콘, tag, 최소 터치 영역처럼 UI 의미상 크기가 정해진 요소에만 고정 크기를 사용한다.
  • 외부 라이브러리는 추가하지 않는다.
  • Figma localhost asset URL은 앱 코드에 사용하지 않는다.

구현 제약

  • FollowingCommunityPostNewsResponse에는 creatorId가 없다.
  • 현재 기존 커뮤니티 게시글 직접 진입 구조는 AppState.setPendingCommunityCommentDeepLink(creatorId:postId:).creatorCommunityAll(creatorId:) 조합이므로 postId만으로는 기존 화면에 정확히 진입할 수 없다.
  • COMMUNITY_POST tap 액션은 구현 전 서버가 creatorId를 추가하거나, postId 단독 상세 진입 route/API가 있는지 확인해야 한다. 이 확인 없이 임의로 수정 화면(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 설계

  • 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인지 확인한다.
      • MainHomeViewTextTabBar 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 역할만 담당한다.
  • Task 1.2: 팔로잉 탭 선택 로그인 guard 연결

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/Main/Home/MainHomeView.swift
      • 수정: SodaLive/Sources/V2/Main/MainView.swift
      • 확인: SodaLive/Sources/V2/Component/TextTabBar.swift
    • 작업 내용:
      • TextTabBar 자체는 수정하지 않는다.
      • MainHomeViewonSelectFollowingTab: () -> Bool callback을 추가한다.
      • TextTabBarselectedItem에는 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가 MainHomeViewMainView에 존재하고, TextTabBar.swift는 변경되지 않는다.

Phase 2: API, Repository, Response model, ViewModel

  • 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 처리가 확인된다.
  • Task 2.2: 팔로잉 API endpoint 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Home/Following/Repository/MainHomeFollowingApi.swift
      • 확인: SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift
    • 작업 내용:
      • MainHomeFollowingApiTargetType으로 만든다.
      • 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와 인증 헤더가 확인된다.
  • 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와 같은 패턴이 확인된다.
  • 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
    • 작업 내용:
      • MainHomeFollowingViewModelObservableObject로 만든다.
      • @Published var isLoading, @Published var response, @Published var isLoginRequired, @Published var message, @Published var hasLoaded를 둔다.
      • fetchFollowing()에서 ApiResponse<HomeFollowingTabResponse>를 디코딩한다.
      • 성공 시 responseisLoginRequired를 갱신한다.
      • 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 실패 문구 처리가 확인된다.
  • 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: 팔로잉 크리에이터 섹션과 전체 버튼

  • 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가 비어 있어도 표시한다.
      • 전체 버튼 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 목록과 마지막 전체 버튼 요구사항이 확인된다.
  • 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
    • 작업 내용:
      • MainHomeViewonTapFollowingAll: () -> Void callback을 추가한다.
      • MainHomeFollowingView까지 callback을 전달한다.
      • MainView에서 onTapFollowingAllAppState.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 호출 연결과 데이터 상태 세팅

  • 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.responseHomeFollowingTabResponse가 세팅되는 흐름을 먼저 확보한다.
      • 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 호출 후 응답 데이터가 기존 크리에이터 섹션에 전달되는 흐름이 확인된다.
  • 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 섹션

  • 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: 최근 대화 섹션

  • 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)를 호출한다.
    • 검증 기준:
      • 실행 명령: 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이 확인된다.
  • 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
    • 작업 내용:
      • MainHomeViewonTapChatRoom: (Int) -> Void callback을 추가한다.
      • MainHomeFollowingView까지 callback을 전달한다.
      • MainView에서 onTapChatRoomAppState.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
      • 기대 결과: 최근 대화 item tap이 기존 채팅방 화면으로 연결된다.

Phase 7: 이달의 스케줄 섹션

  • 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이 확인된다.
  • Task 7.2: 스케줄 상세 진입 연결

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/Main/Home/MainHomeView.swift
      • 수정: SodaLive/Sources/V2/Main/MainView.swift
    • 작업 내용:
      • MainHomeViewonTapFollowingSchedule: (CreatorActivityType, Int) -> Void callback을 추가한다.
      • .live, .liveReplay은 기존 live 입장 callback으로 연결한다.
      • .audioAppState.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: 최근 소식 랭킹 카드

  • Task 8.1: 최근 소식 랭킹 카드 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingRankingNewsCard.swift
    • 작업 내용:
      • Figma 24:5717 기준 카드 형태로 만든다.
      • CREATOR_RANKINGFollowingCreatorRankingNewsResponse를 사용한다.
      • CONTENT_RANKINGFollowingContentRankingNewsResponse를 사용한다.
      • 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: 최근 소식 커뮤니티 카드

  • Task 9.1: 최근 소식 커뮤니티 카드 조립

    • 대상 파일:
      • 생성 또는 수정: SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift
      • 확인: SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift
    • 작업 내용:
      • COMMUNITY_POSTcommunityPost payload가 있을 때만 렌더링한다.
      • 표시 값은 postId, creatorProfileImage, creatorNickname, imageUrl, content, createdAt, likeCount, commentCount를 사용한다.
      • 기존 CommunityPostCard를 재사용할 경우 audioUrl = nil, price = 0, existOrdered = true, isLike = false로 매핑한다.
      • createdAt은 기존 CommunityPostCardcreatedAt 표시 형식에 맞춰 전달한다.
    • 검증 기준:
      • 실행 명령: 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 표시값으로 매핑된다.
  • Task 9.2: 커뮤니티 게시글 tap 라우팅 제약 처리

    • 대상 파일:
      • 확인: SodaLive/Sources/App/AppState.swift
      • 확인: SodaLive/Sources/App/AppStep.swift
      • 확인: SodaLive/Sources/ContentView.swift
      • 확인: SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift
    • 작업 내용:
      • postId 단독으로 게시글 상세/댓글 화면에 진입 가능한 기존 route가 있는지 확인한다.
      • 현재 확인된 기존 route가 creatorId를 요구하는 경우, 잘못된 화면 이동을 구현하지 않는다.
      • 서버 응답에 creatorId가 추가되거나 postId 단독 route가 생기기 전까지 COMMUNITY_POST tap은 표시 카드 내부의 좋아요/댓글 표시만 제공하고 상세 진입은 연결하지 않는다.
      • 이 제약을 구현 결과 보고와 검증 기록에 남긴다.
    • 검증 기준:
      • 실행 명령: 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
      • 기대 결과: 기존 커뮤니티 직접 진입 경로가 creatorId를 요구하는지 확인된다.

Phase 10: 최근 소식 오디오/화보 콘텐츠 카드

  • 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 연결

  • 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는 검색되지 않고, 팔로잉 화면 상태 분기와 섹션 조립이 확인된다.
  • 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가 확인된다.
  • Task 11.3: 최근 소식 섹션 조립

    • 대상 파일:
      • 생성 또는 수정: SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift
    • 작업 내용:
      • recentNews를 순서대로 렌더링한다.
      • visibleFromAtUtc를 모든 item의 표시 시간 기준으로 사용한다.
      • .creatorRankingcreatorRanking payload가 있을 때만 MainHomeFollowingRankingNewsCard를 표시한다.
      • .contentRankingcontentRanking payload가 있을 때만 MainHomeFollowingRankingNewsCard를 표시한다.
      • .communityPostcommunityPost payload가 있을 때만 community card를 표시한다.
      • .audioContentaudioContent payload가 있을 때만 MainHomeFollowingContentNewsCard를 표시한다.
      • .photoContentphotoContent 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 숨김 정책이 확인된다.
  • 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가 없으면 상세 진입을 연결하지 않는다.
    • 검증 기준:
      • 실행 명령: 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 검증

  • 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
      • 기대 결과: 신규 파일 참조와 소스 파일이 확인된다.
  • 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, 모델, 문구, 라우팅, 색상 요구사항이 확인된다.
  • 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 섹션 순서가 충족된다.

검증 기록

  • 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.swiftAppState의 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: MainHomeFollowingCreatorSectionfollowingCreatorsviewModel.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까지 기다리지 않고 MainHomeFollowingViewMainHomeFollowingLiveSection을 조기 조립했다. viewModel.response?.onAirLives ?? []를 전달하고, MainHomeView의 기존 onTapLive callback을 팔로잉 On Air 섹션까지 연결했다.
  • 2026-07-01: SodaLive.xcodeproj/project.pbxprojMainHomeFollowingLiveSection.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 태그를 표시하고, lastMessageAtDateParser.relativeTimeText(fromUTC:fallback:now:)로 표시한다.
  • 2026-07-01: MainHomeFollowingView -> MainHomeView -> MainView.handleFollowingChatRoomTap(roomId:) 경로로 최근 대화 item tap을 .chatRoom(id:) 라우팅에 연결했다.
  • 2026-07-01: SodaLive.xcodeproj/project.pbxprojMainHomeFollowingChatSection.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.pbxprojMainHomeFollowingScheduleSection.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은 남아 있으나 컴파일 오류는 없었다.