From 9fce374842fd85856094dce705f0dd3474a78115 Mon Sep 17 00:00:00 2001 From: klaus Date: Mon, 29 Jun 2026 17:38:10 +0900 Subject: [PATCH] =?UTF-8?q?docs(home):=20FIRST=20=EC=98=A4=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EC=A0=9C=EA=B1=B0=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=EC=9D=84=20=EA=B8=B0=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 24 ++++++++++++-- .../prd.md | 32 ++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/20260601_메인_홈_추천_UI와_API_연동/plan-task.md b/docs/20260601_메인_홈_추천_UI와_API_연동/plan-task.md index d3b3e722..679fe3f1 100644 --- a/docs/20260601_메인_홈_추천_UI와_API_연동/plan-task.md +++ b/docs/20260601_메인_홈_추천_UI와_API_연동/plan-task.md @@ -54,7 +54,7 @@ - `FollowRecommendedCreatorsRequest(val creatorIds: List)` - `HomeRecommendationResponse`와 PRD의 모든 item DTO - 주의: 서버 필드명은 임의 변경하지 않고 필요할 때만 `@SerializedName`을 추가한다. - - 검증: `HomeRecommendationResponse` 필드가 `lives`, `banners`, `recentlyActiveCreators`, `recentDebutCreators`, `firstAudioContents`, `aiCharacters`, `genreCreators`, `cheerCreators`, `popularCommunityPosts`를 모두 포함한다. + - 검증: `HomeRecommendationResponse` 필드가 현재 계약 기준 `lives`, `banners`, `recentlyActiveCreators`, `recentDebutCreators`, `aiCharacters`, `genreCreators`, `cheerCreators`, `popularCommunityPosts`를 포함하고 `firstAudioContents`는 포함하지 않는다. - [x] **Task 2.2: Repository 생성** - 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeRecommendationRepository.kt` @@ -121,7 +121,6 @@ - 구현 내용: - `Loading`, `Content`, `Empty`, `Error` 상태 - 섹션별 UI model 또는 adapter item - - `HomeFirstAudioContentItem` -> `AudioContentTag.First`, `Point`, `Free` 조건 매핑 - `HomeAiCharacterItem.originalWorkTitle == null`이면 기존 `CharacterChatThumbnailView`의 `shouldShowOriginalTitle = false` - `HomePopularCommunityPostItem`의 `price`, `existOrdered` 기반 유료 상태 모델 - 검증: DTO를 Fragment/ViewHolder에 직접 노출하지 않는다. @@ -716,3 +715,24 @@ - 2026-06-05: Phase 9.4 리뷰에서 `ImageLoaderProviderTest`가 상수명 차이만 검증해 `init()`의 legacy cache 삭제와 신규 cache 생성 계약을 충분히 고정하지 못한다는 차단 이슈를 확인했다. 테스트를 보강해 Robolectric 환경에서 `cacheDir/image_cache/journal`을 만든 뒤 `ImageLoaderProvider.init(context)` 호출 후 legacy directory 삭제와 `coil_image_cache` 생성까지 검증하도록 수정했다. 보강 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.ImageLoaderProviderTest"`, `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`를 재실행했고 모두 BUILD SUCCESSFUL을 확인했다. - 2026-06-05: Phase 9.5로 `BannerView.setItems()` 직후 앱이 멈추는 문제를 배너 모델 payload 누락과 `BannerAdapter`의 huge range notify 가능성으로 분리했다. `BannerItem`을 삭제하지 않고 `HomeRecommendationBannerUiModel`과 동일한 payload(`imageUrl`, `eventItem`, `creatorId`, `seriesId`, `link`)를 담도록 확장해 공용 위젯 독립성을 유지했으며, `HomeBannerBinder`는 synthetic id 변환 없이 홈 배너 UI model과 `BannerItem`을 변환하도록 단순화했다. 또한 carousel 목록에서 `itemCount == Int.MAX_VALUE`여도 `notifyItemRangeInserted(0, Int.MAX_VALUE)`와 `notifyItemRangeChanged(0, Int.MAX_VALUE)`를 호출하지 않도록 했고, 단일 배너는 specific notify를 쓰되 `Int.MAX_VALUE` 가상 adapter 상태에서만 `NotifyDataSetChanged` lint를 좁은 helper에 제한했다. 구현 전 `BannerViewTest`는 기존 `BannerItem`이 변경된 홈 배너 payload를 담지 못해 클릭 데이터 보존을 검증할 수 없는 RED 상태였고, 구현 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.widget.banner.BannerViewTest"`가 BUILD SUCCESSFUL임을 확인했다. - 2026-06-05: Phase 9.5 회귀 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLayoutTest"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`를 추가 실행했고 모두 BUILD SUCCESSFUL을 확인했다. `ktlintCheck`에서는 기존 `.editorconfig disabled_rules` deprecation warning만 출력됐고 lint 실패는 없었다. + +### Phase 12: `처음부터 함께 성장!` 섹션 제거 + +- [x] **Task 10.1: 홈 추천 Response와 UI에서 firstAudio 제거** + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/data/HomeRecommendationModels.kt` + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeRecommendationMappers.kt` + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeRecommendationUiModels.kt` + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeRecommendationUiState.kt` + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt` + - 삭제: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFirstAudioAdapter.kt` + - 삭제: `app/src/main/res/layout/item_home_first_audio_content.xml` + - 검증 기록: 2026-06-29 기존 코드에서 `rvHomeFirstAudioContents`, `llHomeFirstAudioSection`, `viewHomeFirstAudioTitle` binding 미해결로 RED 컴파일 실패를 확인했다. 제거 후 targeted unit test와 compile이 포함된 Gradle 실행이 BUILD SUCCESSFUL임을 확인했다. + +- [x] **Task 10.2: 홈 first audio 전체보기와 리소스 제거** + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/data/ContentOverviewModels.kt` + - 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/model/ContentOverviewUiModels.kt` + - 수정: `app/src/main/res/values/strings.xml`, `values-en/strings.xml`, `values-ja/strings.xml` + - 검증 기록: 2026-06-29 `ContentOverviewType.FIRST_AUDIO_CONTENT`와 `home_recommendation_section_first_audio_contents` production 참조를 제거하고, `NEW_AND_HOT_AUDIO` 경로는 유지했다. + +## Verification Log +- 2026-06-29: `처음부터 함께 성장!` 제거 회귀 검증으로 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.*" --tests "kr.co.vividnext.sodalive.v2.main.content.overview.*" :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheck`를 실행했고 BUILD SUCCESSFUL을 확인했다. `git diff --check` 출력 없음도 확인했다. Android 기기/에뮬레이터 접근이 없어 실제 화면 조작은 수행하지 못했으며, `rg`로 `app/src/main`의 first audio/FIRST_AUDIO_CONTENT production 참조 제거를 확인했다. 리뷰 게이트에서 차단 이슈 없이 승인받았다. diff --git a/docs/20260601_메인_홈_추천_UI와_API_연동/prd.md b/docs/20260601_메인_홈_추천_UI와_API_연동/prd.md index 958d014e..7b105af6 100644 --- a/docs/20260601_메인_홈_추천_UI와_API_연동/prd.md +++ b/docs/20260601_메인_홈_추천_UI와_API_연동/prd.md @@ -68,7 +68,6 @@ data class HomeRecommendationResponse( val banners: List, val recentlyActiveCreators: List, val recentDebutCreators: List, - val firstAudioContents: List, val aiCharacters: List, val genreCreators: List, val cheerCreators: List, @@ -88,16 +87,6 @@ data class HomeRecommendationResponse( #### Changed Item Contracts ```kotlin -data class HomeFirstAudioContentItem( - val contentId: Long, - val creatorId: Long, - val creatorNickname: String, - val creatorProfileImage: String, - val title: String, - val price: Int, - val coverImage: String?, - val isPointAvailable: Boolean -) data class HomeAiCharacterItem( val characterId: Long, @@ -126,8 +115,6 @@ data class HomePopularCommunityPostItem( ``` #### Changed Item Requirements -- `HomeFirstAudioContentItem.price`는 첫 오디오 콘텐츠의 무료 여부 표시 정책에 사용한다. -- `HomeFirstAudioContentItem.isPointAvailable`은 첫 오디오 콘텐츠 카드 바인딩 시 포인트 사용 가능 표시 정책에 반영한다. - `HomeAiCharacterItem.creatorId`는 `크리에이터와 이야기를 나눠요!` 섹션 item 터치 시 크리에이터 채널 이동 값으로 사용한다. - `HomeAiCharacterItem.characterId`는 `CharacterChatThumbnailView` 표시 모델의 캐릭터 식별 값으로 유지하되, 추천 홈 item 터치 목적지로는 사용하지 않는다. - `HomeAiCharacterItem.profileImage`는 `CharacterChatThumbnailView`의 실제 이미지 URL로 사용한다. @@ -241,18 +228,6 @@ data class FollowRecommendedCreatorsRequest( - 유료이고 구매하지 않은 UI: 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=309-19774&m=dev - 유료인데 구매함 또는 무료 UI: 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=309-19775&m=dev -#### AudioContentCardView Requirements -- 첫 오디오 콘텐츠는 `v2.widget.AudioContentCardView`를 사용한다. -- `HomeFirstAudioContentItem.isPointAvailable == true`이면 `ic_content_tag_point`를 쓰는 `ImageView`를 표시한다. -- `HomeFirstAudioContentItem`에서는 오리지널 여부를 판단하지 않는다. -- 오리지널 판단 변수가 없으므로 `ic_content_tag_original`을 쓰는 `ImageView`는 항상 `GONE` 처리한다. -- `HomeFirstAudioContentItem.price == 0`이면 무료 콘텐츠로 판단하고 `무료` TextView를 `VISIBLE`로 표시한다. -- `HomeFirstAudioContentItem.price > 0`이면 `무료` TextView를 `GONE` 처리한다. -- `HomeFirstAudioContentItem`으로 표시하는 아이템은 모두 크리에이터의 첫 콘텐츠이므로 `FIRST` 글자를 포함하는 `LinearLayout`을 항상 표시한다. -- 위 조건에 해당하지 않는 태그는 모두 `GONE` 처리한다. -- 기존 `AudioContentCardView`의 `AudioContentTag.Original`, `AudioContentTag.Point`, `AudioContentTag.First`, `AudioContentTag.Free` 계약을 우선 재사용한다. -- `HomeFirstAudioContentItem` 응답에는 오리지널 여부 필드가 없으므로 오리지널 태그는 표시하지 않는다. - ### 섹션 구성과 UI 재사용 도식 Figma `24:5514` 기준 홈 추천 화면은 아래 순서로 배치한다. 제외 섹션은 구현하지 않는다. @@ -312,7 +287,6 @@ HomeRecommendation 화면 | `banners` | `24:5525` | 배너 캐러셀 | `BannerView` | 없음 | | `recentlyActiveCreators` | `24:5529` | 방금 활동한 크리에이터 | `view_section_title` | `list-act` 신규 필요 | | `recentDebutCreators` | `24:5534` | 최근 데뷔한 크리에이터 | `view_section_title` | 신규 카드 필요 | -| `firstAudioContents` | `24:5539` | 첫 오디오 콘텐츠 | `AudioContentCardView` | 태그 조건 매핑 확인 필요 | | `aiCharacters` | `24:5551` | AI 캐릭터 | `view_section_title`, `CharacterChatThumbnailView` | DTO mapping 필요 | | `genreCreators` | `24:5611`, `24:5636` 후보 | 장르별 크리에이터 | `view_section_title` | 그룹 카드, 모두 팔로우 버튼 신규 필요 | | `cheerCreators` | `24:5636` 후보 | 최근 응원이 많은 크리에이터 | `view_section_title` | profile grid, 모두 팔로우 버튼 신규 필요 | @@ -436,3 +410,9 @@ HomeRecommendation 화면 - 2026-06-05: 유료 미구매 item 재바인딩 시 이전 Coil 요청이 뒤늦게 완료될 가능성을 방지하기 위해 locked image 경로에서 기존 Coil 요청을 `dispose()`로 취소한 뒤 drawable을 비우도록 추가 보완했다. - 2026-06-05: 인기 커뮤니티 Phase 7 UI를 실제 기기에서 확인할 수 있도록 `HomeMainFragment`의 임시 샘플 content에 무료/유료 미구매/구매 완료 커뮤니티 3건을 추가했다. 관련 컴파일, 홈 레이아웃 테스트, ktlint, resource merge는 성공했으나 연결 기기가 없어 `installDebug` 기반 실기기 확인은 수행하지 못했다. - 2026-06-05: 커뮤니티 게시물 이미지가 고정 크기 때문에 잘려 보이는 문제를 수정했다. `view_feed_community.xml`에서 root와 이미지 container의 고정 폭/높이를 제거하고, `FeedCommunityView`에서 실제 card content width 기준 `346:236` 비율로 이미지 영역 높이를 계산하도록 변경했다. 관련 Feed/Home 테스트, compile, resource merge, ktlint가 성공했다. + +### 2026-06-29 변경: `처음부터 함께 성장!` 섹션 제거 +- 홈 추천 탭에서 `처음부터 함께 성장!` 섹션을 제거한다. +- `GET /api/v2/home/recommendations` 응답 계약에서 `firstAudioContents`와 `HomeFirstAudioContentItem`을 제거한다. +- UI model, mapper, Fragment binding, adapter, item layout, section title string도 함께 제거한다. +- 다른 섹션(`recentDebutCreators`, `aiCharacters`, `genreCreators`, `cheerCreators`, `popularCommunityPosts`)의 노출/매핑 계약은 유지한다.