Files
sodalive-ios/docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md

30 KiB

메인 콘텐츠 탭 내부 추천 탭 구현 계획

기준 문서

  • PRD: docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md
  • 코드 스타일: docs/agent-guides/code-style.md
  • 빌드/검증: docs/agent-guides/build-test-verification.md
  • 문서 정책: docs/agent-guides/documentation-policy.md
  • Figma 전체 화면: https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=24-6737&m=dev
  • Figma 전체 화면 node: 24:6737
  • Figma 배너: 24:6741
  • Figma 새로 올라온 오디오: 24:6751
  • Figma New&Hot: 24:6758
  • Figma 오직 보이스온에서만: 24:6745
  • Figma 추천 시리즈: 24:6770 (이번 범위 제외)
  • Figma 무료 오디오: 24:6807
  • Figma 포인트 오디오: 24:6813
  • Figma 최근 댓글이 많은 오디오: 24:6820
  • Figma 키워드의 오디오: 24:6829 (이번 범위 제외)
  • Figma 추천 오디오: 24:6842

요구사항 요약

  • 메인 하단 콘텐츠 탭 안에 내부 추천 탭 화면을 구현한다.
  • API endpoint는 GET /api/v2/audio/recommendations를 사용한다.
  • title bar와 내부 tab bar는 고정하고, 배너부터 하위 콘텐츠만 세로 스크롤한다.
  • Figma에 보이는 섹션별로 Phase를 분리한다.
  • 이번 범위에서 추천 시리즈, 키워드의 오디오는 구현하지 않는다.
  • V2 패키지 하위에 구현된 재사용 가능한 위젯 후보를 먼저 확인하고 필요한 경우 최소 보완한다.
  • 오직 보이스온에서만! 섹션은 보이스온 전용 시리즈 표시를 사용한다.
    • ONLY 표시는 ic_series_originalimg_new_only 조합을 우선 재사용한다.
    • Figma localhost asset URL은 앱 코드에 넣지 않는다.

PRD 반영 기준

  • PRD 7.1 API, 7.2 Response model은 Phase 2에서 처리한다.
  • PRD 7.3 Fixed shell and scrolling은 Phase 1에서 처리한다.
  • PRD 7.4 Section visibility는 Phase 2 ViewModel과 각 섹션 Phase에서 처리한다.
  • PRD 7.5 구현 대상 섹션은 Phase 3~10에서 Figma 노드 순서대로 처리한다.
  • PRD 7.6 제외 대상 섹션은 Phase 11에서 검증한다.
  • PRD 7.7 Tag display는 Phase 4의 MainContentAudioTagOverlay를 기준으로 이후 섹션에서 재사용한다.
  • PRD 7.8 Navigation은 Phase 3의 배너 callback 설계, Phase 6의 original series 탭, Phase 12의 전체 라우팅 검증에서 처리한다.
  • PRD 8. UX / UI Expectations최근 댓글이 많은 오디오 댓글 preview bubble에는 nickname을 표시하지 않는다.
  • PRD 10. Success Criteria는 Phase 12의 빌드 검증과 각 Phase 검증 기준으로 확인한다.
  • PRD 11. Open Questions는 현재 미결정 사항 없음이므로 계획 문서에 구현 보류 항목을 남기지 않는다.

접근안 검토

  • 선택안: SodaLive/Sources/V2/Main/Content/Recommendation/** 하위에 API, 모델, ViewModel, 화면, 섹션 컴포넌트를 분리한다.
    • 장점: 기존 MainHomeRecommendation, MainHomeRanking, MainHomeFollowing 구조와 가장 유사하고 MainView.content placeholder를 작게 교체할 수 있다.
    • 단점: 기존 legacy SodaLive/Sources/Content/**와 별도 경로가 생긴다.
  • 대안 1: legacy SodaLive/Sources/Content/** 하위에 구현한다.
    • 장점: 기존 콘텐츠 전체/상세 화면과 가깝다.
    • 단점: 신규 V2 우선 원칙과 맞지 않고 SwiftUI/V2 컴포넌트 재사용 흐름이 약해진다.
  • 대안 2: MainHomeRecommendation의 컴포넌트를 직접 확장한다.
    • 장점: 홈 추천 구현 일부를 바로 가져올 수 있다.
    • 단점: 홈 추천과 콘텐츠 추천은 endpoint, 응답 모델, 섹션 구성이 달라 결합도가 커진다.

구현 원칙

  • 신규 코드는 SodaLive/Sources/V2/Main/Content/Recommendation/** 아래에 둔다.
  • 여러 화면에서 재사용 근거가 있는 UI만 SodaLive/Sources/V2/Component/**로 승격한다.
  • MainView.content 탭에서 신규 콘텐츠 탭 root를 보여주고, 상세 진입 guard callback만 전달한다.
  • 콘텐츠 탭 root는 title bar와 내부 tab bar를 고정 영역으로 두고, 추천 탭 body 내부에서만 ScrollView를 사용한다.
  • 섹션 배열이 비어 있으면 해당 섹션을 숨긴다.
  • 전체 응답이 비어 있거나 API 실패/디코딩 실패 시 최소 empty 또는 error 상태를 표시한다.
  • 오디오 상세 진입은 기존 handleRecommendationContentTap(contentId:)의 로그인/성인 콘텐츠 guard 흐름을 재사용한다.
  • 배너 탭은 eventItem, creatorId, seriesId, link 매핑을 명시적으로 분기한다.
  • seriesIdAppStep.seriesDetail(seriesId:) route로 연결한다.
  • linkAppDeepLinkHandler.handle(urlString:)로 deep link를 먼저 확인하고, deep link가 아니면 UIApplication.shared.open으로 외부 브라우저를 연다.
  • 외부 라이브러리는 추가하지 않는다.

API 응답 모델 기준

  • AudioRecommendationsResponse
    • banners: [AudioBannerResponse]
    • originalSeries: [OriginalSeriesResponse]
    • latestAudios: [AudioCardResponse]
    • newAndHotAudios: [AudioCardResponse]
    • freeAudios: [AudioCardResponse]
    • pointAudios: [AudioCardResponse]
    • mostCommentedAudios: [CommentedAudioResponse]
    • recommendedAudios: [AudioCardResponse]
  • AudioBannerResponse
    • imageUrl, eventItem, creatorId, seriesId, link
  • OriginalSeriesResponse
    • seriesId, coverImageUrl
  • AudioCardResponse
    • audioContentId, title, duration, imageUrl, price
    • isAdult, isPointAvailable, isFirstContent, isOriginalSeries, creatorNickname
  • CommentedAudioResponse
    • audioContentId, title, imageUrl, latestCommentWriterProfileImageUrl

재사용 가능한 V2 위젯 후보

  • SodaLive/Sources/V2/Component/HomeTitleBar.swift
    • 콘텐츠 탭 title bar와 우측 액션 영역의 기준 후보.
  • SodaLive/Sources/V2/Component/TextTabBar.swift
    • 내부 추천 탭 bar 구성 후보.
  • SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift
    • 배너 carousel 후보. 현재 정사각형 높이 계산이 들어 있어 Figma의 362x374 비율과 맞는지 확인 후 최소 보완한다.
  • SodaLive/Sources/V2/Component/SectionTitle.swift
    • 섹션 제목과 우측 chevron 표시 후보.
  • SodaLive/Sources/V2/Component/AudioContentCard.swift
    • 새로 올라온 오디오, 무료 오디오, 포인트 오디오, 추천 오디오 카드 후보. 태그 overlay가 없으므로 썸네일 overlay 슬롯 추가 또는 새 wrapper 컴포넌트 검토가 필요하다.
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
    • New&Hot, 최근 댓글이 많은 오디오 list row의 크기, 태그, 가격 표시 로직 참고 후보.
  • SodaLive/Sources/V2/CreatorChannel/Series/Components/CreatorChannelSeriesListItem.swift
    • ic_series_original + img_new_only 기반 ONLY 표시 참고 후보.
  • SodaLive/Resources/Assets.xcassets/v2/ic_series_original.imageset
    • 보이스온 전용 original 아이콘 asset.
  • SodaLive/Resources/Assets.xcassets/v2/img_new_only.imageset
    • 보이스온 전용 ONLY 표시 asset.

주요 대상 파일

생성

  • SodaLive/Sources/V2/Main/Content/MainContentView.swift
  • SodaLive/Sources/V2/Main/Content/MainContentTab.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationViewModel.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Models/AudioRecommendationsResponse.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Repository/MainContentRecommendationApi.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Repository/MainContentRecommendationRepository.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioBannerSection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioHorizontalCardSection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioListCarouselSection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentVoiceOnOnlySection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentMostCommentedAudioSection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentRecommendedAudioGridSection.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioTagOverlay.swift
  • SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioEmptyStateView.swift

수정

  • SodaLive/Sources/V2/Main/MainView.swift
  • SodaLive/Sources/I18n/I18n.swift
  • 필요 시 SodaLive/Sources/V2/Component/AudioContentCard.swift
  • 필요 시 SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift
  • 필요 시 SodaLive.xcodeproj/project.pbxproj

확인

  • SodaLive/Sources/V2/Main/Home/MainHomeView.swift
  • SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift
  • SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift
  • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
  • SodaLive/Sources/V2/CreatorChannel/Series/Components/CreatorChannelSeriesListItem.swift
  • SodaLive/Sources/V2/Component/AudioContentCard.swift
  • SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift
  • SodaLive/Sources/V2/Component/SectionTitle.swift
  • SodaLive/Sources/V2/Component/TextTabBar.swift
  • SodaLive/Sources/App/AppDeepLinkHandler.swift
  • SodaLive/Sources/App/AppStep.swift
  • SodaLive/Sources/ContentView.swift

TASK 체크리스트

Phase 1: 기준점 점검과 콘텐츠 탭 shell 설계

  • Task 1.1: Figma와 기존 탭 구조 확인

    • 대상 파일:
      • 확인: docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md
      • 확인: docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md
      • 확인: SodaLive/Sources/V2/Main/MainView.swift
      • 확인: SodaLive/Sources/V2/Main/MainTab.swift
      • 확인: SodaLive/Sources/V2/Main/Home/MainHomeView.swift
    • 작업 내용:
      • PRD의 Goals, Non-Goals, Core Requirements, Success Criteria를 다시 읽고 구현 범위를 확정한다.
      • .content 탭이 현재 MainPlaceholderTabView인지 확인한다.
      • 홈 탭의 title bar + tab bar + content 분리 구조를 참고한다.
      • Figma 24:6737에서 title bar와 tab bar가 고정이고 24:6741 배너부터 스크롤 대상임을 확인한다.
    • 검증 기준:
      • 실행 명령: rg "GET /api/v2/audio/recommendations|추천 시리즈|키워드의 오디오|Open Questions|미결정 사항 없음|seriesDetail|AppDeepLinkHandler|nickname" docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md && sed -n '140,190p' SodaLive/Sources/V2/Main/MainView.swift && sed -n '1,120p' SodaLive/Sources/V2/Main/Home/MainHomeView.swift
      • 기대 결과: PRD의 확정 범위가 확인되고, .content는 placeholder이며, MainHomeView는 fixed shell과 tab content를 분리한다.
  • Task 1.2: 내부 콘텐츠 탭 root 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/MainContentView.swift
      • 생성: SodaLive/Sources/V2/Main/Content/MainContentTab.swift
      • 수정: SodaLive/Sources/V2/Main/MainView.swift
    • 작업 내용:
      • MainContentTab에 이번 범위의 내부 recommendation case를 둔다.
      • MainContentView는 title bar와 내부 tab bar를 고정 영역으로 배치한다.
      • 추천 탭 콘텐츠만 MainContentRecommendationView로 분리한다.
      • MainView.content placeholder를 MainContentView로 교체한다.
    • 검증 기준:
      • 실행 명령: rg "enum MainContentTab|struct MainContentView|MainContentRecommendationView|case \\.content" SodaLive/Sources/V2/Main
      • 기대 결과: 콘텐츠 탭 root와 추천 탭 분기가 확인되고, .content placeholder가 제거된다.

Phase 2: API, Repository, Response model, ViewModel

  • Task 2.1: 추천 오디오 응답 모델 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Models/AudioRecommendationsResponse.swift
    • 작업 내용:
      • 사용자 제공 Kotlin response class를 Swift Decodable 구조체로 대응한다.
      • id가 있는 항목은 Identifiable을 채택하고 Int 또는 Int64 대신 기존 프로젝트 관례에 맞춰 Int를 우선 사용한다.
      • @JsonProperty("isAdult"), @JsonProperty("isPointAvailable")is prefix boolean은 Swift property명을 그대로 둔다.
      • EventItem 타입은 기존 모델을 재사용한다.
    • 검증 기준:
      • 실행 명령: rg "struct AudioRecommendationsResponse|struct AudioBannerResponse|struct OriginalSeriesResponse|struct AudioCardResponse|struct CommentedAudioResponse|isAdult|isPointAvailable|isFirstContent|isOriginalSeries" SodaLive/Sources/V2/Main/Content/Recommendation/Models/AudioRecommendationsResponse.swift
      • 기대 결과: API 응답 필드가 모두 Swift 모델에 반영된다.
  • Task 2.2: 추천 오디오 API endpoint 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Repository/MainContentRecommendationApi.swift
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Repository/MainContentRecommendationRepository.swift
      • 확인: SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift
    • 작업 내용:
      • MainContentRecommendationApiTargetType으로 만든다.
      • case는 getAudioRecommendations 하나만 둔다.
      • path는 /api/v2/audio/recommendations, method는 .get, task는 .requestPlain로 둔다.
      • headers는 기존 V2 홈 API와 동일하게 Authorization: Bearer {token} 패턴을 따른다.
      • repository는 getAudioRecommendations() -> AnyPublisher<Response, MoyaError>를 제공한다.
    • 검증 기준:
      • 실행 명령: rg "/api/v2/audio/recommendations|getAudioRecommendations|Authorization|AnyPublisher<Response, MoyaError>" SodaLive/Sources/V2/Main/Content/Recommendation/Repository
      • 기대 결과: 신규 endpoint와 repository 메서드가 추천 오디오 전용 경로에만 존재한다.
  • Task 2.3: ViewModel 생성과 섹션 표시 상태 정리

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationViewModel.swift
      • 수정: SodaLive/Sources/I18n/I18n.swift
    • 작업 내용:
      • MainContentRecommendationViewModelObservableObject로 만든다.
      • isLoading, isShowPopup, errorMessage, recommendations, hasLoaded 상태를 둔다.
      • fetchRecommendations()에서 ApiResponse<AudioRecommendationsResponse>를 디코딩한다.
      • 섹션별 배열이 모두 비어 있으면 I18n.MainContentRecommendation.emptyStateMessage를 표시할 수 있게 한다.
      • 실패 시 기존 V2 홈 추천과 같은 toast/popup 흐름 또는 empty state 중 하나를 선택하되, 사용자 노출 문구는 I18n에 ko/en/ja로 둔다.
    • 검증 기준:
      • 실행 명령: rg "final class MainContentRecommendationViewModel|fetchRecommendations|ApiResponse<AudioRecommendationsResponse>|hasLoaded|emptyStateMessage|ko:|en:|ja:" SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationViewModel.swift SodaLive/Sources/I18n/I18n.swift
      • 기대 결과: API 디코딩, 로딩/실패/empty 상태, I18n ko/en/ja 문구가 확인된다.

Phase 3: 배너 섹션

  • Task 3.1: 배너 섹션 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioBannerSection.swift
      • 확인/보완: SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift
    • 작업 내용:
      • Figma 24:6741 기준으로 배너 carousel을 표시한다.
      • AudioBannerResponse.imageUrlBannerCarouselItem 또는 신규 display model로 변환한다.
      • page indicator는 01 / 20 형식을 유지한다.
      • 배너 탭 시 eventItem, creatorId, seriesId, link를 상위 callback으로 전달한다.
      • seriesId가 있으면 seriesDetail route로 이동할 수 있도록 callback을 설계한다.
      • link가 있으면 deep link 처리 결과가 true인지 먼저 확인하고, 처리되지 않은 URL만 외부 브라우저로 연다.
      • 기존 BannerCarousel이 정사각형 고정 높이를 강제하면, 콘텐츠 추천 배너 전용 wrapper에서 Figma 비율을 맞추거나 공용 컴포넌트를 최소 보완한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentAudioBannerSection|BannerCarousel|AudioBannerResponse|imageUrl|eventItem|creatorId|seriesId|link|seriesDetail|AppDeepLinkHandler|UIApplication.shared.open" SodaLive/Sources/V2/Main/Content/Recommendation SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/V2/Component/Banner/BannerCarousel.swift
      • 기대 결과: 배너 섹션이 API 응답과 탭 callback을 연결한다.

Phase 4: 새로 올라온 오디오 섹션

  • Task 4.1: 가로 카드 섹션 공통 컴포넌트 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioHorizontalCardSection.swift
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioTagOverlay.swift
      • 확인/보완: SodaLive/Sources/V2/Component/AudioContentCard.swift
    • 작업 내용:
      • Figma 24:6751새로 올라온 오디오latestAudios로 표시한다.
      • AudioContentCard(size: .medium)를 우선 재사용한다.
      • isOriginalSeries, isFirstContent, isPointAvailable, price == 0, isAdult 상태를 태그 overlay로 표시한다.
      • 카드 탭 시 onTapContent(audioContentId)를 호출한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentAudioHorizontalCardSection|MainContentAudioTagOverlay|latestAudios|AudioContentCard|isOriginalSeries|isFirstContent|isPointAvailable|price == 0|isAdult|onTapContent" SodaLive/Sources/V2/Main/Content/Recommendation
      • 기대 결과: 최신 오디오 카드와 태그 overlay 조건이 확인된다.

Phase 5: New&Hot 섹션

  • Task 5.1: New&Hot list carousel 섹션 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioListCarouselSection.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
    • 작업 내용:
      • Figma 24:6758New&HotnewAndHotAudios로 표시한다.
      • 한 페이지에 3개 list row를 세로로 묶고, 묶음 단위로 가로 스크롤한다.
      • row는 88pt 썸네일, 제목 한 줄, creator nickname 한 줄 구조를 따른다.
      • MainContentAudioTagOverlay를 list thumbnail에도 재사용한다.
      • row 탭 시 onTapContent(audioContentId)를 호출한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentAudioListCarouselSection|newAndHotAudios|chunk|MainContentAudioTagOverlay|onTapContent" SodaLive/Sources/V2/Main/Content/Recommendation
      • 기대 결과: New&Hot 섹션이 3-row 묶음 가로 carousel로 구성된다.

Phase 6: 오직 보이스온에서만 섹션

  • Task 6.1: 보이스온 전용 시리즈 섹션 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentVoiceOnOnlySection.swift
      • 확인: SodaLive/Sources/V2/CreatorChannel/Series/Components/CreatorChannelSeriesListItem.swift
      • 확인: SodaLive/Resources/Assets.xcassets/v2/ic_series_original.imageset
      • 확인: SodaLive/Resources/Assets.xcassets/v2/img_new_only.imageset
    • 작업 내용:
      • Figma 24:6745오직 보이스온에서만!originalSeries로 표시한다.
      • 이번 섹션은 OriginalSeriesResponse.seriesId, coverImageUrl만 사용한다.
      • 카드 상단에는 ic_series_original + img_new_only 조합으로 ONLY 표시를 붙인다.
      • 추천 시리즈 카드형 상세 정보는 이번 범위에서 만들지 않는다.
      • 카드 탭 시 onTapSeries(seriesId)를 호출하고 상위에서 AppStep.seriesDetail(seriesId:)로 이동한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentVoiceOnOnlySection|originalSeries|OriginalSeriesResponse|ic_series_original|img_new_only|onTapSeries|seriesDetail" SodaLive/Sources/V2/Main/Content/Recommendation SodaLive/Sources/V2/Main/MainView.swift SodaLive/Resources/Assets.xcassets/v2
      • 기대 결과: 보이스온 전용 ONLY 표시와 seriesDetail 진입 연결이 확인된다.

Phase 7: 무료 오디오 섹션

  • Task 7.1: 무료 오디오 섹션 연결
    • 대상 파일:
      • 수정: SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift
      • 확인: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioHorizontalCardSection.swift
    • 작업 내용:
      • Figma 24:6807무료 오디오freeAudios로 표시한다.
      • price == 0 또는 API 섹션 성격에 따라 무료 태그를 표시한다.
      • 섹션 배열이 비어 있으면 숨긴다.
    • 검증 기준:
      • 실행 명령: rg "freeAudios|무료 오디오|MainContentAudioHorizontalCardSection" SodaLive/Sources/V2/Main/Content/Recommendation SodaLive/Sources/I18n/I18n.swift
      • 기대 결과: 무료 오디오 섹션이 View에 조립된다.

Phase 8: 포인트 오디오 섹션

  • Task 8.1: 포인트 오디오 섹션 연결
    • 대상 파일:
      • 수정: SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift
      • 확인: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentAudioHorizontalCardSection.swift
    • 작업 내용:
      • Figma 24:6813포인트 오디오pointAudios로 표시한다.
      • isPointAvailable 상태에 따라 포인트 태그를 표시한다.
      • 섹션 배열이 비어 있으면 숨긴다.
    • 검증 기준:
      • 실행 명령: rg "pointAudios|포인트 오디오|isPointAvailable|MainContentAudioHorizontalCardSection" SodaLive/Sources/V2/Main/Content/Recommendation SodaLive/Sources/I18n/I18n.swift
      • 기대 결과: 포인트 오디오 섹션과 포인트 태그 조건이 확인된다.

Phase 9: 최근 댓글이 많은 오디오 섹션

  • Task 9.1: 댓글 카드 carousel 섹션 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentMostCommentedAudioSection.swift
    • 작업 내용:
      • Figma 24:6820최근 댓글이 많은 오디오mostCommentedAudios로 표시한다.
      • 오디오 row와 댓글 preview bubble을 하나의 카드로 묶고, 카드 단위로 가로 스크롤한다.
      • latestCommentWriterProfileImageUrl을 댓글 작성자 profile image로 표시한다.
      • 댓글 preview bubble에는 nickname을 표시하지 않는다.
      • CommentedAudioResponse에는 creator nickname이 없으므로 댓글 영역에 nickname용 UI나 placeholder를 추가하지 않는다.
      • 카드 탭 시 onTapContent(audioContentId)를 호출한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentMostCommentedAudioSection|mostCommentedAudios|CommentedAudioResponse|latestCommentWriterProfileImageUrl|onTapContent" SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentMostCommentedAudioSection.swift
      • 기대 결과: 최근 댓글 섹션은 댓글 작성자 profile image와 오디오 탭 callback을 사용한다.
      • 실행 명령: rg "nickname|creatorNickname" SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentMostCommentedAudioSection.swift
      • 기대 결과: 검색 결과가 없어야 한다. 댓글 bubble에는 nickname을 표시하지 않는다.

Phase 10: 추천 오디오 섹션

  • Task 10.1: 추천 오디오 grid 섹션 생성
    • 대상 파일:
      • 생성: SodaLive/Sources/V2/Main/Content/Recommendation/Components/MainContentRecommendedAudioGridSection.swift
      • 수정: SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift
    • 작업 내용:
      • Figma 24:6842추천 오디오recommendedAudios로 표시한다.
      • 2열 grid를 구성한다.
      • 카드 크기는 부모 width 기반으로 계산하고, 고정 숫자 width만으로 화면을 제한하지 않는다.
      • MainContentAudioTagOverlay를 재사용한다.
      • grid item 탭 시 onTapContent(audioContentId)를 호출한다.
    • 검증 기준:
      • 실행 명령: rg "MainContentRecommendedAudioGridSection|recommendedAudios|LazyVGrid|GridItem\\(\\.flexible|MainContentAudioTagOverlay|onTapContent" SodaLive/Sources/V2/Main/Content/Recommendation
      • 기대 결과: 추천 오디오 2열 grid와 태그 overlay가 확인된다.

Phase 11: 제외 섹션 미구현 확인

  • Task 11.1: 추천 시리즈와 키워드의 오디오 제외 확인
    • 대상 파일:
      • 확인: SodaLive/Sources/V2/Main/Content/Recommendation/**
    • 작업 내용:
      • Figma 24:6770추천 시리즈 컴포넌트는 생성하지 않는다.
      • Figma 24:6829#키워드의 오디오 컴포넌트는 생성하지 않는다.
      • originalSeries오직 보이스온에서만! 섹션에만 사용한다.
    • 검증 기준:
      • 실행 명령: rg "추천 시리즈|Keyword|키워드의 오디오|RecommendedSeries" SodaLive/Sources/V2/Main/Content/Recommendation
      • 기대 결과: 이번 범위 제외 섹션 구현 파일/문구가 검색되지 않는다.

Phase 12: 라우팅, 프로젝트 포함, 전체 검증

  • Task 12.1: 상세 진입 callback 연결

    • 대상 파일:
      • 수정: SodaLive/Sources/V2/Main/MainView.swift
      • 확인: SodaLive/Sources/App/AppStep.swift
      • 확인: SodaLive/Sources/ContentView.swift
    • 작업 내용:
      • 오디오 카드/list/grid 탭은 기존 content detail guard callback을 재사용한다.
      • eventItem 배너는 기존 event detail 흐름이 있으면 재사용한다.
      • creator 이동이 필요한 배너는 기존 creator detail guard callback을 재사용한다.
      • seriesId 배너와 originalSeries 탭은 AppStep.seriesDetail(seriesId:) route로 연결한다.
      • link 배너는 AppDeepLinkHandler.handle(urlString:)로 deep link를 먼저 체크한다.
      • deep link가 아니면 UIApplication.shared.open으로 외부 브라우저를 연다.
    • 검증 기준:
      • 실행 명령: rg "contentDetail|eventItem|eventDetail|creatorDetail|seriesDetail|AppDeepLinkHandler|UIApplication.shared.open|MainContentView" SodaLive/Sources/V2/Main/MainView.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift
      • 기대 결과: 상세 진입과 event/creator/series route가 기존 route와 guard 흐름을 재사용하고, link는 deep link 우선 후 외부 브라우저로 처리한다.
  • Task 12.2: 프로젝트 포함 및 빌드 검증

    • 대상 파일:
      • 확인/보완: SodaLive.xcodeproj/project.pbxproj
      • 확인: SodaLive/Sources/V2/Main/Content/**
    • 작업 내용:
      • 생성한 Swift 파일이 빌드 대상에 포함되는지 확인한다.
      • 파일 시스템 동기화 방식으로 자동 포함되면 project file은 수정하지 않는다.
      • 프로젝트 파일 수정이 필요할 때만 최소 변경한다.
    • 검증 기준:
      • 실행 명령: plutil -lint SodaLive.xcodeproj/project.pbxproj
      • 기대 결과: OK
      • 실행 명령: xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
      • 기대 결과: BUILD SUCCEEDED

검증 기록

  • 2026-07-05 문서 작성:
    • 실행/확인:
      • docs/agent-guides/documentation-policy.md
      • docs/agent-guides/build-test-verification.md
      • docs/agent-guides/code-style.md
      • SodaLive/Sources/V2/Main/Home/MainHomeView.swift
      • SodaLive/Sources/V2/Main/MainView.swift
      • SodaLive/Sources/V2/Component/**
      • SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelAudioContentListItem.swift
      • SodaLive/Sources/V2/CreatorChannel/Series/Components/CreatorChannelSeriesListItem.swift
      • Figma node 24:6737, 24:6741, 24:6751, 24:6758, 24:6745, 24:6770, 24:6807, 24:6813, 24:6820, 24:6829, 24:6842
    • 결과:
      • 구현 대상/제외 대상 섹션을 Figma 기준으로 분리했다.
      • 기존 V2 재사용 후보와 필요한 신규 파일 후보를 문서화했다.
      • 이번 작업은 문서 작성만 수행했으므로 빌드/테스트는 실행하지 않았다.
  • 2026-07-05 문서 보완:
    • 실행/확인:
      • SodaLive/Sources/App/AppDeepLinkHandler.swift
      • SodaLive/Sources/App/AppStep.swift
      • SodaLive/Sources/ContentView.swift
      • SodaLive/Sources/V2/Main/MainView.swift
      • Figma node 24:6820
    • 결과:
      • 최근 댓글이 많은 오디오 댓글 bubble에는 nickname을 표시하지 않는 것으로 확정했다.
      • AudioBannerResponse.link는 deep link 우선 처리 후 외부 브라우저를 여는 것으로 확정했다.
      • AudioBannerResponse.seriesId, OriginalSeriesResponse.seriesIdseriesDetail route로 이동하는 것으로 확정했다.
      • 이번 작업은 문서 보완만 수행했으므로 빌드/테스트는 실행하지 않았다.
  • 2026-07-05 PRD 기반 계획 재보완:
    • 실행/확인:
      • docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md
      • docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md
    • 결과:
      • PRD Core Requirements와 계획 Phase의 대응 관계를 추가했다.
      • PRD에서 해소된 Open Questions가 계획 문서에 구현 보류 항목으로 남지 않게 정리했다.
      • 최근 댓글이 많은 오디오 댓글 bubble에 nickname이 없어야 한다는 검증 기준을 분리했다.
      • 이번 작업은 문서 보완만 수행했으므로 빌드/테스트는 실행하지 않았다.
  • 2026-07-06 PRD 동기화 점검 후 계획 보완:
    • 실행/확인:
      • docs/20260705_메인_콘텐츠_내부_추천_탭/prd.md
      • docs/20260705_메인_콘텐츠_내부_추천_탭/plan-task.md
    • 결과:
      • 신규 사용자 노출 문구를 I18n ko/en/ja로 추가한다는 기준을 Task 2.3에 명시했다.
      • 배너 eventItem의 기존 event detail 흐름 재사용 기준을 Phase 12 최종 라우팅 검증에 추가했다.
      • 이번 작업은 문서 보완만 수행했으므로 빌드/테스트는 실행하지 않았다.