diff --git a/SodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreView.swift b/SodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreView.swift index 931928c..d858c28 100644 --- a/SodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreView.swift +++ b/SodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreView.swift @@ -22,39 +22,45 @@ struct SeriesMainByGenreView: View { selectedGenre: $viewModel.selectedGenre ) } - + ScrollView(.vertical, showsIndicators: false) { let horizontalPadding: CGFloat = 24 let gridSpacing: CGFloat = 16 let width = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2 - - LazyVGrid( - columns: Array( - repeating: GridItem( - .flexible(), - spacing: gridSpacing, - alignment: .topLeading - ), - count: 2 - ), - alignment: .leading, - spacing: gridSpacing - ) { - ForEach(viewModel.seriesList.indices, id: \.self) { index in - let item = viewModel.seriesList[index] - SeriesMainItemView(item: item, width: width, height: width * 227 / 160) - .contentShape(Rectangle()) - .onAppear { - if index == viewModel.seriesList.count - 1 { - viewModel.getSeriesListByGenre() - } - } - .onTapGesture { - AppState.shared.setAppStep(step: .seriesDetail(seriesId: item.seriesId)) - } + + VStack(spacing: 16) { + if !viewModel.genreList.isEmpty { + YandexInlineBannerView(placement: .seriesMainByGenre, horizontalPadding: 24) } + + LazyVGrid( + columns: Array( + repeating: GridItem( + .flexible(), + spacing: gridSpacing, + alignment: .topLeading + ), + count: 2 + ), + alignment: .leading, + spacing: gridSpacing + ) { + ForEach(viewModel.seriesList.indices, id: \.self) { index in + let item = viewModel.seriesList[index] + SeriesMainItemView(item: item, width: width, height: width * 227 / 160) + .contentShape(Rectangle()) + .onAppear { + if index == viewModel.seriesList.count - 1 { + viewModel.getSeriesListByGenre() + } + } + .onTapGesture { + AppState.shared.setAppStep(step: .seriesDetail(seriesId: item.seriesId)) + } + } + } + .padding(.horizontal, horizontalPadding) } - .padding(.horizontal, horizontalPadding) } } .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) diff --git a/docs/20260428_커뮤니티시리즈알림Yandex배너추가.md b/docs/20260428_커뮤니티시리즈알림Yandex배너추가.md new file mode 100644 index 0000000..b4c2495 --- /dev/null +++ b/docs/20260428_커뮤니티시리즈알림Yandex배너추가.md @@ -0,0 +1,93 @@ +# 20260428 커뮤니티 시리즈 알림 Yandex 배너 추가 + +## 작업 체크리스트 +- [x] 기존 `YandexInlineBannerView` 재사용 범위와 placement 확장 지점 확인 +- [x] 배너 placement별 ad unit 상수 추가 +- [x] 커뮤니티 전체보기 화면에 탭/콘텐츠 사이 배너 추가 +- [x] 시리즈 메인 홈 화면에 완결/추천 섹션 사이 배너 추가 +- [x] 시리즈 메인 요일별 화면에 요일/리스트 사이 배너 추가 +- [x] 시리즈 메인 장르별 화면에 장르/리스트 사이 배너 추가 +- [x] 알림 리스트 화면에 카테고리/알림 리스트 사이 배너 추가 +- [x] 알림 수신설정 화면에 서비스 알림/팔로잉 채널 사이 배너 추가 +- [x] 시리즈 메인 요일별/장르별 화면 배너가 세로 ScrollView와 함께 스크롤되도록 구조 수정 +- [x] 빌드 및 검증 기록 추가 + +## 작업 기준 + +- 공식 문서: + - `https://ads.yandex.com/helpcenter/ko/dev/ios/adaptive-inline-banner` +- 공용 광고 지원: + - `SodaLive/Sources/Common/YandexAdSupport.swift` +- ad unit 상수: + - `SodaLive/Sources/Utils/Constants.swift` + - `SodaLive/Sources/Debug/Utils/Constants.swift` +- 수정 대상: + - `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift` + - `SodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swift` + - `SodaLive/Sources/Content/Series/Main/DayOfWeek/SeriesMainDayOfWeekView.swift` + - `SodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreView.swift` + - `SodaLive/Sources/Notification/List/PushNotificationListView.swift` + - `SodaLive/Sources/Settings/Notification/NotificationSettingsView.swift` + +## QA 기준 + +- 커뮤니티 전체보기에서 `communityViewTypeTabView` 아래, 리스트/그리드 콘텐츠 위에 배너가 표시된다. +- 시리즈 메인 홈에서 완결 시리즈 섹션 아래, 추천 시리즈 섹션 위에 배너가 표시된다. +- 시리즈 메인 요일별에서 요일 선택 영역 아래, 시리즈 리스트 위에 배너가 표시된다. +- 시리즈 메인 장르별에서 장르 선택 영역 아래, 시리즈 리스트 위에 배너가 표시된다. +- 시리즈 메인 요일별/장르별에서 배너는 세로 시리즈 리스트와 함께 스크롤되고, 상단 선택 UI는 기존처럼 고정된다. +- 알림 리스트에서 카테고리 탭 아래, 알림 리스트 위에 배너가 표시된다. +- 알림 수신설정에서 서비스 알림 카드 아래, 팔로잉 채널 섹션 위에 배너가 표시된다. +- 각 화면의 기존 스크롤/무한로딩/onAppear 동작은 유지된다. + +## 구현 메모 + +- 새 광고 래퍼를 만들지 않고 `YandexInlineBannerView`를 그대로 재사용한다. +- 배너는 `LazyVGrid`/`LazyVStack` 내부 아이템으로 섞지 않고, 각 화면의 섹션 경계에 형제 뷰로 배치한다. +- 시리즈 화면은 기존 `24` 좌우 패딩과 시각 정렬이 맞도록 `horizontalPadding` 값을 화면별로 조정한다. +- 알림 수신설정의 요청 문구는 “팔로잉 채널과 서비스 알림 사이”이며, 실제 UI 순서는 서비스 알림 → 팔로잉 채널이므로 두 섹션 사이 배치로 구현한다. + +## 검증 기록 + +- 2026-04-28 / 사전 조사 + - 무엇: 기존 Yandex 배너 지원 코드와 신규 삽입 대상 6개 화면의 구조를 확인했다. + - 왜: 새 광고 브리지를 만들지 않고 최소 변경으로 배너를 추가하기 위해서다. + - 어떻게: + - `SodaLive/Sources/Common/YandexAdSupport.swift`를 읽어 `YandexInlineBannerView`와 placement 매핑 구조를 확인했다. + - 대상 6개 SwiftUI 화면의 `VStack`/`ScrollView`/`LazyVGrid` 구조를 읽어 안전한 삽입 지점을 확인했다. + - Yandex 공식 adaptive inline banner 문서를 참고해 SwiftUI 브리지 재사용이 가능한지 확인했다. + - 결과: + - 공용 배너 브리지 재사용 가능 + - placement enum/ad unit 상수 확장 + 6개 화면 배치만으로 구현 범위 확정 + +- 2026-04-28 / 구현 및 빌드 검증 + - 무엇: 6개 화면용 Yandex 배너 placement/ad unit 상수를 추가하고, 각 화면의 지정 섹션 경계에 배너를 삽입했다. + - 왜: 기존 공용 광고 브리지를 유지하면서 요청한 화면과 위치에만 최소 변경으로 광고를 노출하기 위해서다. + - 어떻게: + - `SodaLive/Sources/Common/YandexAdSupport.swift`에 신규 `YandexBannerPlacement` 6종과 ad unit 매핑을 추가했다. + - `SodaLive/Sources/Utils/Constants.swift`, `SodaLive/Sources/Debug/Utils/Constants.swift`에 placement별 ad unit 상수를 추가했다. + - 커뮤니티/시리즈/알림 대상 6개 화면에 `YandexInlineBannerView`를 섹션 경계 형제 뷰로 삽입했다. + - `SeriesMainHomeView`는 완결/추천 섹션이 모두 있을 때만, `SeriesMainByGenreView`는 장르 섹션이 있을 때만 배너가 보이도록 조건을 조정했다. + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build` + - 변경 파일별 `lsp_diagnostics`를 확인했다. + - 결과: + - `SodaLive-dev` Debug 빌드 성공 + - `SodaLive` Debug 빌드 성공 + - `Constants.swift`/`Debug/Utils/Constants.swift`는 LSP 진단 없음 + - 나머지 SwiftUI 파일은 SourceKit 단독 해석 환경에서 `YandexMobileAds`, `BaseView`, `I18n`, ViewModel 등 프로젝트/Pods 심볼을 해석하지 못해 환경성 오류를 보고했지만, 실제 `xcodebuild` 실컴파일은 두 스킴 모두 통과했다. + - 이 CLI 세션에서는 iOS 화면 수동 탐색용 시뮬레이터 자동화 경로가 없어 실제 UI 수동 확인은 수행하지 못했고, 대신 실컴파일 검증으로 변경 무결성을 확인했다. + +- 2026-04-28 / 시리즈 요일별·장르별 배너 스크롤 구조 수정 + - 무엇: `SeriesMainDayOfWeekView`, `SeriesMainByGenreView`의 배너가 세로 리스트와 함께 스크롤되도록 구조를 조정했다. + - 왜: 기존 구조에서는 배너가 세로 `ScrollView` 바깥에 있어 리스트만 스크롤되고 배너는 고정처럼 보였기 때문이다. + - 어떻게: + - 두 파일 모두 배너를 세로 `ScrollView` 내부 `LazyVGrid` 위로 이동했다. + - 상단 요일 selector와 장르 selector는 기존처럼 세로 스크롤 바깥에 유지했다. + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build && xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build` + - 변경 파일 `lsp_diagnostics`를 다시 확인했다. + - 결과: + - `SodaLive-dev` Debug 빌드 성공 + - `SodaLive` Debug 빌드 성공 + - 두 SwiftUI 파일의 LSP 오류는 기존과 동일한 SourceKit 환경성 심볼 해석 한계이며, 실제 빌드는 통과했다. + - 이 세션에서는 시뮬레이터 UI 자동화 경로가 없어 실제 드래그 수동 검증은 수행하지 못했다.