81 KiB
크리에이터 채널 커뮤니티 탭 구현 계획/TASK
For agentic workers: 각 단계는 체크박스(
- [ ])로 추적하고, 완료 즉시- [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
Goal: GET /api/v2/creator-channels/{creatorId}/community 응답을 기반으로 크리에이터 채널의 커뮤니티 탭에 리스트/썸네일 보기 전환, 커뮤니티 게시글 목록, 유료/구매/본인 채널 표시 분기, 오디오 재생, empty 상태, 본인 채널 하단 커뮤니티 글 올리기 CTA와 pagination을 표시한다.
Architecture: 기존 CreatorChannelActivity의 ViewPager2/CreatorChannelPagerAdapter 구조를 유지하고, CreatorChannelTab.Community의 placeholder를 신규 CreatorChannelCommunityFragment로 교체한다. 커뮤니티 탭 전용 Fragment/ViewModel/DTO/mapper/adapter는 kr.co.vividnext.sodalive.v2.creator.channel.community 하위에 두되, API/Repository는 기존 채널 공통 CreatorChannelApi/CreatorChannelRepository에 endpoint만 추가한다. 리스트형은 v2 FeedCommunityView 재사용 가능성을 우선 검토하되 본인 채널 우측 액션, 댓글 불가 숨김, 중앙 재생 버튼이 기존 홈 피드에 영향을 주면 커뮤니티 탭 전용 item layout으로 제한한다.
Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
전제와 성공 기준
- PRD:
docs/20260621_크리에이터_채널_커뮤니티_탭/prd.md - 기존 채널 컨테이너:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktapp/src/main/res/layout/activity_creator_channel.xml
- 기존 채널 API/Repository:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
- 기존 커뮤니티/피드 참조:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.ktapp/src/main/res/layout/view_feed_community.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllGridAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityPostMenuBottomSheetDialog.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/player/CreatorCommunityMediaPlayerManager.kt
- Figma:
- 전체 리스트형:
290:9061 - 전체 썸네일형:
290:9073 - 유료이고 구매하지 않은 게시글:
290:9066 - 전체 리스트형 + 본인 채널:
665:19021
- 전체 리스트형:
- API endpoint는
GET /api/v2/creator-channels/{creatorId}/community이다. - 첫 페이지
page는0, 기본size는20이다. - 보기 방식은 클라이언트 UI 상태이며 API query parameter로 보내지 않는다.
- 기본 보기 방식은 리스트형이며 label/icon은
리스트형/ic_new_list이다. - 토글 후 썸네일형 label/icon은
썸네일형/ic_new_grid이다. - 게시글 item 자체를 터치해도 아무 동작을 수행하지 않는다.
- 본인 또는 구매한 사용자의 오디오 게시글 재생은 기존
CreatorCommunityMediaPlayerManager를 재사용한다. - 본인 채널에 본인이 쓴 리스트형 게시글에서만 우측 상단 더보기와 유료 가격을 표시한다.
- 댓글 불가 게시글은 댓글 icon과 댓글 수를 모두 숨긴다.
- 본인 채널이면 하단 고정
커뮤니티 글 올리기CTA를 표시하고 기존CreatorCommunityWriteActivity진입을 우선 재사용한다. - 구현 완료 후 최소 다음 명령을 실행한다.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
Figma 참조 필요 Phase
- Phase 1: 제한 참조
- 기존 코드 경계, 위젯 재사용 가능성, 기존 커뮤니티 정책 확인이 중심이며 Figma는 PRD 기준만 확인한다.
- Phase 2: Figma 참조 불필요
- API/DTO/Repository/ViewModel 상태 모델은 서버 계약과 기존 오디오/시리즈 탭 패턴을 따른다.
- Phase 3: 제한 참조
- mapper와 item state는 PRD, 기존
FeedCommunityView, 기존CreatorCommunityAllGridAdapter정책을 함께 확인한다.
- mapper와 item state는 PRD, 기존
- Phase 4: 필수 참조
- 리스트형 item, 유료 미구매 잠금 영역, 썸네일형 grid item, Sort-bar는 Figma
290:9061,290:9073,290:9066,665:19021기준으로 구현한다.
- 리스트형 item, 유료 미구매 잠금 영역, 썸네일형 grid item, Sort-bar는 Figma
- Phase 5: 제한 참조
- 탭 연결, pagination, owner CTA, media player 연결은 기존 코드 패턴 중심으로 검증한다.
- Phase 6: 필수 참조
- 최종 수동 화면 검증은 PRD의 모든 Figma 노드와 실제 화면을 대조한다.
파일 구조
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktCreatorChannelTab.Community를 신규CreatorChannelCommunityFragment로 연결한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktCreatorChannelCommunityFragment.Host구현, 커뮤니티 탭 선택 시 최초 로드, pagination trigger, ViewPager 높이 갱신, owner CTA 표시/클릭, media player 생명주기 연결을 추가한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt- 커뮤니티 탭 endpoint를 추가한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt- 커뮤니티 탭 repository method를 추가한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/data/CreatorChannelCommunityTabResponse.ktCreatorChannelCommunityTabResponse,CreatorChannelCommunityPostResponse를 정의한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModel.kt- 최초 조회, retry, pagination, 보기 방식 상태, loading/error/empty/content 상태를 관리한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.kt- 보기 방식, 게시글 item, 잠금/오디오/owner action 상태, 화면 상태 UI model을 정의한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt- DTO를 UI model로 변환하고 유료/구매/본인/댓글 가능/썸네일 preview 정책을 결정한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt- 커뮤니티 탭 UI, 보기 방식 토글, adapter 전환, pagination error toast, media player callback, host callback 연결을 담당한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt- 리스트형 RecyclerView adapter를 담당한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityGridAdapter.kt- 썸네일형 3열 RecyclerView adapter를 담당한다.
- 생성:
app/src/main/res/layout/fragment_creator_channel_community.xml- Sort-bar, RecyclerView, empty/error/retry 영역을 포함한다.
- 생성:
app/src/main/res/layout/item_creator_channel_community_list.xml- 리스트형 커뮤니티 item을 구현한다. 필요 시
FeedCommunityView대신 전용 layout으로 만든다.
- 리스트형 커뮤니티 item을 구현한다. 필요 시
- 생성:
app/src/main/res/layout/item_creator_channel_community_grid.xml- 썸네일형 정사각형 grid item을 구현한다.
- 수정 가능:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt,app/src/main/res/layout/view_feed_community.xml- 공통 위젯을 재사용하는 편이 더 작다고 판단되는 경우에만 optional bind flag를 추가한다. 기존 홈/추천 피드 동작이 변하면 안 된다.
- 수정:
app/src/main/res/values/strings.xml,app/src/main/res/values-en/strings.xml,app/src/main/res/values-ja/strings.xml- 보기 방식 label, empty/error/retry/notice/CTA 문구를 추가 또는 기존 문자열 재사용으로 정리한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.ktCreatorChannelCommunityViewModelbinding을 추가한다.
- 테스트 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityPaginationTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragmentLayoutTest.kt
- 테스트 수정:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
Phase 1: 기존 구조 확인과 재사용 경계 고정
-
Task 1.1: Community 탭 placeholder 연결 지점 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/model/CreatorChannelHomeUiModels.kt
- 작업:
CreatorChannelTab.Community가 현재CreatorChannelPlaceholderFragment로 연결되는지 확인한다.- 신규
CreatorChannelCommunityFragment.newInstance(creatorId)로 교체할 위치를 고정한다.
- 검증:
rg -n "CreatorChannelTab\\.Community|CreatorChannelPlaceholderFragment|createFragment" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel- 기대 결과: Community 탭 placeholder와 관련 테스트 갱신 지점이 확인된다.
- 검증 기록:
- 2026-06-21:
CreatorChannelPagerAdapter.createFragment()는 Home/Live/Audio/Series만 실제 Fragment를 반환하고 나머지는CreatorChannelPlaceholderFragment.newInstance(tab)로 처리한다. 따라서CreatorChannelTab.Community는 현재 placeholder이며, Phase 5에서CreatorChannelCommunityFragment.newInstance(creatorId)분기를 추가할 위치는CreatorChannelPagerAdapter.kt의when (tab)이다. - 2026-06-21:
CreatorChannelPagerAdapterTest는 Home/Live/Audio/Series 외 탭이 placeholder임을 검증하고,CreatorChannelActivitySourceTest는 현재 Community 분기가 없음을 확인한다. Phase 5에서 두 테스트의 기대값 갱신이 필요하다.
- 2026-06-21:
- 확인:
-
Task 1.2: v2
FeedCommunityView재사용 가능성 확인- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.ktapp/src/main/res/layout/view_feed_community.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt
- 작업:
FeedItem.Community가creatorProfileUrl,imageUrl,audioUrl,price,existOrdered매핑에 충분한지 확인한다.FeedCommunityView의 유료 미구매 잠금 overlay는 재사용 후보로 둔다.- 댓글 불가 숨김, 본인 채널 우측 상단 더보기/가격, 이미지 중앙 재생 버튼이 기존 홈/추천 피드에 영향을 줄 수 있으면 전용 item layout으로 구현한다.
- 검증:
rg -n "FeedCommunityView|FeedItem\\.Community|existOrdered|ll_feed_community_paid_overlay|tv_feed_community_comment_count" app/src/main/java app/src/main/res/layout/view_feed_community.xml- 기대 결과: 공통 위젯 수정 여부와 전용 layout 필요성이 기록된다.
- 검증 기록:
- 2026-06-21:
FeedItem.Community에는creatorImageUrl,imageUrl,audioUrl,price,existOrdered,commentCount,likeCount가 있어 기본 표시 데이터와 유료 미구매 판단(price > 0 && !existOrdered)은 담을 수 있다.FeedCommunityView와view_feed_community.xml에는ll_feed_community_paid_overlay,tv_feed_community_comment_count가 존재해 잠금 overlay는 재사용 후보로 확인했다. - 2026-06-21: 기존
FeedCommunityView는 댓글 수를 항상 표시하고, owner 우측 더보기/상단 가격/중앙 play-pause 버튼 상태를 제공하지 않으며, 홈 섹션과 홈 추천에서도 재사용된다. 기존 홈/추천 피드 영향 없이 댓글 불가 숨김, owner action, 중앙 재생 버튼을 넣으려면 커뮤니티 탭 전용 item layout/adapter로 구현하는 것으로 경계를 고정한다.
- 2026-06-21:
- 확인:
-
Task 1.3: 기존 커뮤니티 그리드/더보기/재생 정책 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllGridAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityPostMenuBottomSheetDialog.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/player/CreatorCommunityMediaPlayerManager.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/player/CreatorCommunityContentItem.kt
- 작업:
- 썸네일형 text preview는 기존
CreatorCommunityAllGridAdapter.CONTENT_PREVIEW_MAX_LENGTH = 24정책을 우선 따른다. - 우측 더보기는 기존
CreatorCommunityPostMenuBottomSheetDialog의 수정/삭제/고정/고정 해제 액션 정책을 따른다. - 재생은
CreatorCommunityMediaPlayerManager.toggleContent(CreatorCommunityContentItem(postId, audioUrl))를 사용한다.
- 썸네일형 text preview는 기존
- 검증:
rg -n "CONTENT_PREVIEW_MAX_LENGTH|CreatorCommunityPostMenuBottomSheetDialog|CreatorCommunityMediaPlayerManager|CreatorCommunityContentItem|toggleContent" app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community- 기대 결과: 기존 그리드 preview, 더보기 메뉴, 재생 manager 사용 방식이 확인된다.
- 검증 기록:
- 2026-06-21:
CreatorCommunityAllGridAdapter.CONTENT_PREVIEW_MAX_LENGTH = 24이며 text-only grid preview는 줄바꿈을 공백으로 치환하고 trim 후 24자까지만 사용한다. 신규 grid preview 정책은 이 구현을 따른다. - 2026-06-21:
CreatorCommunityPostMenuBottomSheetDialog는 creator 여부에 따라 신고 또는 고정/수정/삭제 메뉴를 표시하고, 고정 상태에 따라 pin/unpin 문구와 icon을 전환한다. 본인 채널 우측 더보기는 이 dialog 정책을 재사용한다. - 2026-06-21:
CreatorCommunityMediaPlayerManager는CreatorCommunityContentItem(contentId, url)을 받아toggleContent()로 재생/일시정지를 전환하고isPlayingContent(contentId)와stopContent()를 제공한다. 커뮤니티 탭 오디오 재생도 이 manager를 재사용한다.
- 2026-06-21:
- 확인:
-
Task 1.4: Owner CTA 진입점과 리소스 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/res/layout/activity_creator_channel.xmlapp/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xml
- 작업:
- 기존
onOwnerFabCommunityClicked()가CreatorCommunityWriteActivity를 여는지 확인한다. - 하단 고정 owner CTA도 Community 탭에서 같은 method를 호출하도록 연결 계획을 고정한다.
ic_new_upload_community_post,creator_channel_owner_fab_community리소스 존재를 확인한다.
- 기존
- 검증:
rg -n "onOwnerFabCommunityClicked|CreatorCommunityWriteActivity|ic_new_upload_community_post|creator_channel_owner_fab_community" app/src/main/java app/src/main/res- 기대 결과: 기존 커뮤니티 작성 진입점과 icon/string 리소스가 확인된다.
- 검증 기록:
- 2026-06-21:
CreatorChannelActivity.onOwnerFabCommunityClicked()는 owner FAB를 닫고communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java))로 기존 작성 화면을 연다. Community 탭 하단 owner CTA는 같은 method를 호출하도록 연결한다. - 2026-06-21:
activity_creator_channel.xml에는owner_fab_community_button이@drawable/ic_new_upload_community_post와@string/creator_channel_owner_fab_community를 사용하고,strings.xml/values-en/values-ja에 해당 문구가 이미 존재한다.currentOwnerCtaTab()은 현재 Live/Audio만 반환하므로 Phase 5에서 Community 포함이 필요하다.
- 2026-06-21:
- 확인:
Phase 2: API/DTO/Repository/ViewModel 계약 추가
-
Task 2.1: 커뮤니티 탭 DTO 추가
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/data/CreatorChannelCommunityTabResponse.kt
- 작업:
@Keep,@SerializedName기반으로CreatorChannelCommunityTabResponse,CreatorChannelCommunityPostResponse를 추가한다.CreatorChannelCommunityPostResponse에는postId,creatorId,creatorNickname,creatorProfileUrl,createdAtUtc,content,imageUrl,audioUrl,price,existOrdered,isCommentAvailable,likeCount,commentCount,isPinned를 포함한다.@JsonProperty가 아닌 프로젝트 기존 Gson 패턴인@SerializedName을 사용한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- 신규 DTO 추가 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-21:
CreatorChannelCommunityTabResponse,CreatorChannelCommunityPostResponse를 전용community/data패키지에 추가했고,@Keep/@SerializedName기반 Gson DTO 계약을 구성했다. 최종./gradlew :app:compileDebugKotlin검증 대상으로 포함한다.
- 2026-06-21:
- 생성:
-
Task 2.2: 커뮤니티 탭 endpoint와 Repository method 추가
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt
- 작업:
@GET("/api/v2/creator-channels/{creatorId}/community")endpoint를 추가한다.- query parameter
page,size만 전달한다. - Repository method는
getCommunity(creatorId, page, size, token)형태로 둔다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- API/Repository 추가 후 기존 Koin graph와 충돌 없이 컴파일된다.
- 검증 기록:
- 2026-06-21:
CreatorChannelApi.getCommunity()에@GET("/api/v2/creator-channels/{creatorId}/community")를 추가하고 query parameter는page,size만 전달하도록 구성했다.CreatorChannelRepository.getCommunity(creatorId, page, size, token)도 동일 계약으로 추가했다. 최종./gradlew :app:compileDebugKotlin검증 대상으로 포함한다.
- 2026-06-21:
- 수정:
-
Task 2.3: ViewModel RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityPaginationTest.kt
- 테스트 케이스:
- 최초 로딩이
page=0,size=20으로 호출된다. - 기본 보기 방식은
List이다. - 보기 방식 toggle은
List -> Grid -> List순서이며 API를 재호출하지 않는다. communityPostCount == 0이면Empty상태가 된다.- 표시 가능한
communityPosts가 없으면Empty상태가 된다. hasNext == true일 때 다음 페이지는 마지막 응답의page + 1로 요청한다.- load-more 요청에는
size=20을 유지한다. - loading 중 중복 load-more 요청은 무시된다.
- 다음 페이지 성공 시 기존 게시글 뒤에 append한다.
- 다음 페이지 실패 시 기존 목록은 유지하고 pagination error message만 설정한다.
consumePaginationErrorMessage()호출 후 pagination error message가 null이 된다.
- 최초 로딩이
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityViewModelTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityPaginationTest"
- 기대 결과:
- production 구현 전
CreatorChannelCommunityViewModel미구현으로 RED 실패한다.
- production 구현 전
- 검증 기록:
- 2026-06-21: production ViewModel 구현 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityPaginationTest"실행 결과:app:compileDebugUnitTestKotlin에서Unresolved reference 'CreatorChannelCommunityViewModel',CreatorChannelCommunityUiState,CreatorChannelCommunityViewMode로 RED 실패함을 확인했다.
- 2026-06-21: production ViewModel 구현 전
- 생성:
-
Task 2.4:
CreatorChannelCommunityViewModel구현- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModel.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
DEFAULT_PAGE_SIZE = 20,FIRST_PAGE = 0, 기본viewMode = CreatorChannelCommunityViewMode.List로 둔다.loadCommunity(creatorId: Long, isOwner: Boolean)는 같은creatorId와 기존 state가 있으면 중복 최초 조회를 막는다.toggleViewMode()는 현재 로드된 데이터를 유지하고 API를 재호출하지 않는다.retryCommunity()는 첫 페이지를 다시 조회한다.loadMore()는 content 상태,hasNext,isLoadingMore,creatorId를 확인해 중복 요청을 막는다.requestGeneration으로 오래된 응답이 최신 상태를 덮어쓰지 않게 한다.- 첫 페이지 성공 후
communityPostCount == 0또는 표시 가능한 item이 0개이면Empty상태로 전환한다. - pagination 실패는 기존 content를 유지하고
paginationErrorMessage에만 반영한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"
- 기대 결과:
- ViewModel 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-21:
CreatorChannelCommunityViewModel을 기존 Audio/Series 탭 패턴에 맞춰LiveData,BaseViewModel,requestGeneration, pagination error consume 구조로 구현하고AppDIKoin binding을 추가했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"실행 결과 BUILD SUCCESSFUL로 GREEN 확인했다. - 2026-06-21: Phase 2 코드 리뷰에서 DTO/API/Repository/ViewModel/test 변경을
CreatorChannelAudioViewModel,CreatorChannelSeriesViewModel패턴과 대조했고, 수정이 필요한 결함은 발견하지 않았다. 추가 검증으로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*",./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --check를 실행했으며 모두 PASS했다.
- 2026-06-21:
- 생성:
Phase 3: Mapper/UI model 정책 추가
-
Task 3.1: Mapper RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.kt
- 테스트 케이스:
createdAtUtc는 기존 상대 시간 포맷 helper를 사용해 UI text로 변환된다.creatorProfileUrl은 profile image URL로 매핑된다.isPinned == true이면 notice/pin 표시 상태가 true다.isCommentAvailable == false이면 댓글 icon/count 표시 상태가 false다.price > 0 && !existOrdered && !isOwner이면 유료 미구매 잠금 상태다.- 유료 미구매 상태에서는 image placeholder mode가
LockedGray이고 play button 표시 상태가 false다. isOwner == true또는existOrdered == true이면audioUrl != null && imageUrl != null에서 play button 표시 상태가 true다.- 본인 채널에 본인이 쓴 게시글에서만 owner more button 표시 상태가 true다.
- 본인 채널에 본인이 쓴 유료 게시글에서만 top price 표시 상태가 true다.
- 타인 채널에서는 top more/price 표시 상태가 false다.
- grid text-only preview는 줄바꿈을 공백으로 바꾸고 24자까지만 사용한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest"
- 기대 결과:
- mapper 미구현 상태에서 RED 실패한다.
- 검증 기록:
- 2026-06-21:
CreatorChannelCommunityMapperTest를 추가해 보기 방식 label/icon, 상대 시간, profile URL, notice/comment 표시, 유료 미구매 잠금, play button, owner more/top price, grid preview 정책을 RED 테스트로 고정했다. Production mapper/model 구현 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest"실행 결과:app:compileDebugUnitTestKotlin에서community.model패키지,toCommunityPostUiModels,CreatorChannelCommunityViewMode.labelResId/iconResId, UI model fields 미구현으로 실패해 RED를 확인했다.
- 2026-06-21:
- 생성:
-
Task 3.2: UI model과 mapper 구현
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt
- 작업:
CreatorChannelCommunityViewMode는List,Grid로 정의하고 label/icon resource를 가진다.CreatorChannelCommunityPostUiModel에는postId,creatorId,creatorNickname,creatorProfileUrl,createdAtText,content,imageUrl,audioUrl,price,existOrdered,likeCount,commentCount,showComment,showNotice,isLocked,showOwnerMore,showOwnerTopPrice,showPlayButton,gridPreviewText를 둔다.showComment = isCommentAvailable로 매핑한다.isLocked = price > 0 && !existOrdered && !isOwner로 매핑한다.showPlayButton = !isLocked && !audioUrl.isNullOrBlank() && !imageUrl.isNullOrBlank()로 매핑한다.showOwnerMore와showOwnerTopPrice는isOwner == true && creatorId == currentUserId조건으로 제한한다.gridPreviewText는 기존CreatorCommunityAllGridAdapter처럼 줄바꿈 제거 후 trim하고 24자까지 사용한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest"
- 기대 결과:
- Mapper 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-21:
CreatorChannelCommunityViewMode,CreatorChannelCommunityPostUiModel,CreatorChannelCommunityImageMode,toCommunityPostUiModels()를 추가하고formatUtcRelativeTimeText()와 기존 grid preview 24자 정책을 재사용했다.CreatorChannelCommunityViewMode는List/Gridlabel과drawable-mdpi의ic_new_list/ic_new_gridicon resource를 가진다. - 2026-06-21:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityMapperTest",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --check모두 PASS했다. 최초ktlintCheck는 typealias 파일명/line length 위반으로 실패했고, 파일명을CreatorChannelCommunityViewMode.kt로 맞춘 뒤 재실행 PASS했다. - 2026-06-21: Reviewer gate의 컨텍스트 마이닝에서
CreatorChannelCommunityViewModel.Content.communityPosts가 DTO 목록을 유지해 mapper가 production 상태 경로에 적용되지 않았다는 FAIL을 확인했다.CreatorChannelCommunityViewModel생성자에Context를 주입하고 first page/load-more 성공 경로에서data.communityPosts.toCommunityPostUiModels(context, isOwner, SharedPreferenceManager.userId)를 적용해Content.communityPosts가CreatorChannelCommunityPostUiModel목록을 들도록 보정했다. 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*",./gradlew :app:compileDebugKotlin,./gradlew :app:mergeDebugResources,./gradlew :app:ktlintCheck,git diff --checkPASS했다. - 2026-06-21: 후속 리뷰에서 ViewModel이
Context를 직접 주입받아 생명주기상 memory leak 가능성이 있다는 지적을 반영했다.UtcRelativeTimeTextFormatter와AndroidUtcRelativeTimeTextFormatter를 추가하고,CreatorChannelCommunityViewModel은Context대신 formatter만 주입받도록 변경했다. mapper는 formatter로createdAtText를 생성하며, Android 구현체는 application context만 보관한다. 보정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*",./gradlew :app:compileDebugKotlin,./gradlew :app:mergeDebugResources,./gradlew :app:ktlintCheck,git diff --checkPASS했다. - 2026-06-21: Phase 3 코드 리뷰에서 mapper 정책과 ViewModel 상태 경로를
CreatorCommunityAllGridAdapter의 24자 preview 정책, PRD의 유료/구매/본인/댓글 표시 분기와 대조했다. 수정이 필요한 결함은 발견하지 않았다. 재검증으로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --check를 실행했으며 모두 PASS했다.
- 2026-06-21:
- 생성:
Phase 4: Fragment, Adapter, XML UI 구현
-
Task 4.1: Fragment layout 추가
- 생성:
app/src/main/res/layout/fragment_creator_channel_community.xml
- 작업:
- Sort-bar에는 좌측
전체+communityPostCount, 우측 보기 방식 label + icon을 배치한다. - RecyclerView는 리스트형과 썸네일형을 같은
RecyclerView에서LayoutManager/adapter 교체로 처리한다. - empty, error message, retry button 영역은 오디오/시리즈 탭 패턴을 따른다.
- Sort-bar에는 좌측
- 검증 명령:
./gradlew :app:mergeDebugResources
- 기대 결과:
- 신규 layout binding 생성이 PASS한다.
- 검증 기록:
- 2026-06-21: RED 단계에서
CreatorChannelCommunityFragmentLayoutTest를 먼저 추가했고, production 파일 추가 전compileDebugUnitTestKotlin이 fragment/list/grid layout ID, resource, source file 미구현으로 실패함을 확인했다. - 2026-06-21:
fragment_creator_channel_community.xml을 추가해 Sort-bar, 단일RecyclerView, empty/error/retry 영역을 구성했다../gradlew :app:mergeDebugResourcesPASS로 신규 layout binding 생성을 확인했다.
- 2026-06-21: RED 단계에서
- 생성:
-
Task 4.2: 리스트형 item layout/adapter 추가
- 생성:
app/src/main/res/layout/item_creator_channel_community_list.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt
- 작업:
- Figma
290:9061,290:9066,665:19021기준의 feed card를 구현한다. creatorProfileUrl, nickname, 상대 시간, notice, 본문, 이미지/잠금 영역, reaction 영역을 표시한다.showComment == false이면 댓글 icon/count view를GONE처리한다.isLocked == true이면 이미지 대신 회색 RoundedRectangle과 lock/가격 캡슐을 표시하고 play button을 숨긴다.showPlayButton == true이면 이미지 가운데 play/pause button을 표시한다.showOwnerMore == true이면 우측 상단 더보기 버튼을 표시하고 기존CreatorCommunityPostMenuBottomSheetDialog호출 callback을 연결한다.- root item click listener는 설정하지 않거나 no-op으로 둔다.
- Figma
- 검증 명령:
./gradlew :app:mergeDebugResources./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"
- 기대 결과:
- layout resource와 adapter bind source 검증이 PASS한다.
- 검증 기록:
- 2026-06-21:
item_creator_channel_community_list.xml과CreatorChannelCommunityListAdapter.kt를 추가해 리스트형 card, 댓글 숨김, 유료 미구매 잠금, play button, owner more/price 표시 정책을 binding 경로에 반영했다. - 2026-06-21:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS로 리스트 layout/resource와 adapter bind source 검증을 확인했다. - 2026-06-21: Phase 4 reviewer gate에서 유료 미구매 locked item의
imageUrl유지와 이미지 로드 가능성, locked price pill 표시 검증 부족, play/pause icon이 실제 재생 상태와 분리된 점, owner more callback에 item 정보와 고정 상태가 부족한 점으로 초기 FAIL을 확인했다. 보정으로 locked item은imageUrl을 비우고 이미지 로드를 막았으며, 가격 pill 표시를 테스트로 고정하고,isPlayingContent(postId)기반 play/pause icon bind와 owner more item callback에isPinned보존 정보를 반영했다. 보정 후 Phase 4 관련 검증은 PASS했다. - 2026-06-21: 최종 Phase 4 review fix로 리스트형 locked price capsule을
tv_creator_channel_community_list_locked_price가 이미지 잠금 영역 안에 표시되도록 이동했다.ListAdapter의 locked price와 top price 표시 조건도 분리해, 상단 가격은 본인 채널 owner-only 조건에서만 다시 보이도록 복구했다. - 2026-06-22: Phase 4 코드 리뷰에서 Figma
665:19021본인 채널 리스트형 유료 게시글의 가격 태그와 더보기 버튼이 feed 카드 우측 상단etc영역에 함께 배치되는 것을 확인했다. 기존 XML은tv_creator_channel_community_list_top_price가 reaction row 우측에 있어 요구사항과 어긋났으므로 RED 테스트를 추가한 뒤,layout_creator_channel_community_list_top_actions컨테이너 안으로 가격 태그와 더보기 버튼을 이동했다.CreatorChannelCommunityListAdapter는 상단 액션 컨테이너 visibility를showOwnerMore || showOwnerTopPrice로 bind하도록 보정했다.
- 2026-06-21:
- 생성:
-
Task 4.3: 썸네일형 grid item layout/adapter 추가
- 생성:
app/src/main/res/layout/item_creator_channel_community_grid.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityGridAdapter.kt
- 작업:
- 3열 정사각형 item을 유지하도록 layout params를 adapter에서 계산하거나
GridLayoutManagerspan 폭에 맞춘다. imageUrl != null && !isLocked이면 이미지 전체 표시.imageUrl == null && !isLocked이면gridPreviewText를 중앙 정렬로 표시.isLocked == true이면 잠금/가격 표시를 노출한다.showNotice == true이면 pin/notice icon을 상단에 표시한다.- root item click listener는 설정하지 않거나 no-op으로 둔다.
- 3열 정사각형 item을 유지하도록 layout params를 adapter에서 계산하거나
- 검증 명령:
./gradlew :app:mergeDebugResources./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"
- 기대 결과:
- grid layout/resource 검증이 PASS한다.
- 검증 기록:
- 2026-06-21:
item_creator_channel_community_grid.xml과CreatorChannelCommunityGridAdapter.kt를 추가해 3열 정사각형 grid, 이미지/텍스트 preview, 잠금/가격, notice 표시 정책을 구현했다. - 2026-06-21:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS와./gradlew :app:mergeDebugResourcesPASS로 grid layout/resource 검증을 확인했다. - 2026-06-21: Phase 4 reviewer gate에서 grid item 크기가 좌우 margin을 반영하지 않아 3열 정사각형 sizing이 과대 계산될 수 있다는 FAIL을 확인했다. 보정으로 grid adapter의 item 크기 계산에 RecyclerView padding과 item margin을 반영했고, margin-aware sizing 테스트를 추가했다. 보정 후 grid layout 검증은 PASS했다.
- 2026-06-21:
- 생성:
-
Task 4.4:
CreatorChannelCommunityFragment구현- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt
- 작업:
CreatorChannelCommunityViewModel을 observe하고 Loading/Empty/Error/Content를 bind한다.- Sort-bar 우측 toggle click에서
viewModel.toggleViewMode()를 호출한다. - view mode가 List이면
LinearLayoutManager, Grid이면GridLayoutManager(spanCount = 3)를 적용한다. - pagination error message는 Toast로 표시하고 consume한다.
onCreatorChannelCommunityTabSelected(),onCreatorChannelCommunityScrolledToBottom(),onCreatorChannelCommunityOwnerCtaVisibilityChanged()entry를 제공한다.- media player update callback에서 adapter의 play/pause 상태를 갱신한다.
- Fragment
onDestroyView()또는onDestroy()에서CreatorCommunityMediaPlayerManager.stopContent()를 호출해 재생 리소스를 정리한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"
- 기대 결과:
- Fragment와 adapter 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-21:
CreatorChannelCommunityFragment.kt를 추가해 ViewModel 상태 observe, Loading/Empty/Error/Content bind, 보기 방식 toggle, List/GridLayoutManager전환, pagination error consume, tab/scroll/owner CTA entry, media player 정리 경로를 구현했다. - 2026-06-21: 병렬 Gradle 실행 1건에서 Kotlin incremental cache/daemon 충돌과 timeout이 있었고, 영향받은 community test를 단독 재실행해 PASS를 확인했다. 이후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS와./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS를 확인했다. - 2026-06-21: Phase 4 reviewer gate에서 Fragment가 화면 이탈
onPause()시 오디오를 멈추지 않아 백그라운드 재생이 남을 수 있다는 FAIL을 확인했다. 보정으로onPause()에서pauseContent()를 호출하고, 기존 정리 경로의stopContent()는 유지했다. 보정 후 Fragment 생명주기 검증은 PASS했다. - 2026-06-21: 최종 Phase 4 review fix로
CreatorCommunityMediaPlayerManager에prepareAsync()완료 전pauseContent()와stopContent()가 호출될 수 있는 경로를 막는 prepared-state guard를 추가했다. prepare 전 pause/stop 호출은 MediaPlayer invalid state를 만들지 않도록 보호하고, Fragment 생명주기 정리 경로는 유지했다.
- 2026-06-21:
- 생성:
-
Task 4.5: 문자열/리소스 정리
- 수정:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xml
- 작업:
리스트형,썸네일형, empty/error/retry/notice 문구를 추가한다.- 기존
creator_channel_owner_fab_community,screen_creator_community_purchase_with_can는 재사용 가능하면 중복 추가하지 않는다. ic_new_list,ic_new_grid,ic_new_upload_community_post가 없으면 기존 drawable 정책에 맞게 추가 여부를 별도 확인 후 진행한다.
- 검증 명령:
./gradlew :app:mergeDebugResources
- 기대 결과:
- 한국어/영어/일본어 string 참조가 모두 해소된다.
- 검증 기록:
- 2026-06-21: community 관련 문자열을
strings.xml,values-en/strings.xml,values-ja/strings.xml에 추가하고 기존 재사용 가능한 문구는 중복하지 않았다. - 2026-06-21:
./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:ktlintCheckPASS,git diff --checkPASS를 확인했다.ktlintCheck는 신규 layout test와 list adapter의 formatting-only 수정 후 PASS했다.
- 2026-06-21: community 관련 문자열을
- 수정:
Phase 5: Activity/Pager/Owner CTA/Pagination 연결
-
Task 5.1: PagerAdapter에서 Community Fragment 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
- 작업:
CreatorChannelTab.Community -> CreatorChannelCommunityFragment.newInstance(creatorId)분기를 추가한다.- 기존 placeholder 기대 테스트를 Community 실제 Fragment 기대값으로 갱신한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"
- 기대 결과:
- Community 탭이 신규 Fragment로 연결됨이 검증된다.
- 검증 기록:
- 2026-06-22: RED로
CreatorChannelPagerAdapterTest에 Community 실제 Fragment 기대값을 먼저 추가했고, production 변경 전CreatorChannelPagerAdapterTest > createFragment는 Home Live Audio Series Community를 실제 Fragment로 생성하고 나머지는 placeholder를 유지한다가CreatorChannelPagerAdapterTest.kt:31assertion으로 실패함을 확인했다. - 2026-06-22:
CreatorChannelPagerAdapter.createFragment()에CreatorChannelTab.Community -> CreatorChannelCommunityFragment.newInstance(creatorId)분기를 추가하고 placeholder 기대에서 Community를 제외했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"PASS로 Community 탭 Fragment 연결을 확인했다.
- 2026-06-22: RED로
- 수정:
-
Task 5.2:
CreatorChannelActivityHost 연결- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 작업:
CreatorChannelCommunityFragment.Host를 구현한다.findCommunityFragment()를 추가한다.onPageSelected와 header 변경 시 현재 탭이 Community이면onCreatorChannelCommunityTabSelected()를 호출한다.notifyCurrentCreatorChannelTabScrolledToBottom()에 Community 분기를 추가한다.isCreatorChannelLoadMoreTab()에 Community를 포함한다.onCreatorChannelCommunityContentChanged()에서 ViewPager 높이 갱신과 추가 load-more 필요 여부 확인을 호출한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
- 기대 결과:
- Community 탭 선택/스크롤/높이 갱신 source 검증이 PASS한다.
- 검증 기록:
- 2026-06-22: RED로
CreatorChannelActivitySourceTest에 Community Host/pagination/owner CTA source 계약을 먼저 추가했고, production 변경 전CreatorChannelActivitySourceTest > Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다가CreatorChannelActivitySourceTest.kt:455assertion으로 실패함을 확인했다. - 2026-06-22:
CreatorChannelActivity가CreatorChannelCommunityFragment.Host를 구현하도록 연결하고,findCommunityFragment(), 탭 선택/header 변경 시onCreatorChannelCommunityTabSelected(), 하단 스크롤 시onCreatorChannelCommunityScrolledToBottom(), load-more 대상 포함,onCreatorChannelCommunityContentChanged()의 높이 갱신/추가 load-more 재평가를 추가했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS로 source 계약을 확인했다.
- 2026-06-22: RED로
- 수정:
-
Task 5.3: Owner CTA Community 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
currentOwnerCtaTab()에 Community 탭을 포함한다.updateOwnerCtaVisibility()에서 Community일 때ic_new_upload_community_post,creator_channel_owner_fab_community를 bind한다.onOwnerCtaClicked()에서 Community일 때 기존onOwnerFabCommunityClicked()를 호출한다.findCommunityFragment()?.onCreatorChannelCommunityOwnerCtaVisibilityChanged(ownerCtaTab == CreatorChannelTab.Community)를 호출해 하단 padding/inset을 반영하게 한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"./gradlew :app:mergeDebugResources
- 기대 결과:
- 본인 채널 Community 탭 하단 CTA 연결이 source/resource 검증으로 확인된다.
- 검증 기록:
- 2026-06-22:
currentOwnerCtaTab()에 Community를 포함하고, Community 탭 CTA는ic_new_upload_community_post/creator_channel_owner_fab_community를 bind하며 클릭 시 기존onOwnerFabCommunityClicked()를 호출하도록 연결했다.findCommunityFragment()?.onCreatorChannelCommunityOwnerCtaVisibilityChanged(ownerCtaTab == CreatorChannelTab.Community)경로도 추가해 Fragment padding 반영을 호출한다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS,./gradlew :app:mergeDebugResourcesPASS로 source/resource 검증을 확인했다.
- 2026-06-22:
- 수정:
-
Task 5.4: media player 생명주기와 adapter 갱신 연결
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityListAdapter.kt
- 작업:
CreatorCommunityMediaPlayerManager(requireContext()) { listAdapter.notifyDataSetChanged() }형태로 생성한다.- play/pause button click에서
CreatorCommunityContentItem(postId, audioUrl)를 전달해toggleContent()를 호출한다. - bind 시
mediaPlayerManager.isPlayingContent(postId)값으로 play/pause icon을 결정한다. - Fragment 정리 시
stopContent()를 호출한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- 기존 media player manager import와 호출이 컴파일된다.
- 검증 기록:
- 2026-06-22: Phase 4에서 이미 구현된
CreatorChannelCommunityFragment의CreatorCommunityMediaPlayerManager(requireContext()) { listAdapter.notifyDataSetChanged() },CreatorCommunityContentItem(item.postId, audioUrl)기반toggleContent(), adapterisPlayingContent(postId)icon bind,onPause()/onDestroyView()정리 경로를 Phase 5 source test로 재확인했다. 신규 production 변경은 Activity/Pager 연결에 한정했다. - 2026-06-22:
./gradlew :app:compileDebugKotlinPASS로 기존 media player manager import와 호출 컴파일을 확인했다.
- 2026-06-22: Phase 4에서 이미 구현된
- 수정:
-
Task 5.5: reviewer gate 후속 no-op Host API 제거
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 작업:
CreatorChannelCommunityFragment.Host에서 실제 호출되지 않는postId기반 owner more fallback API를 제거한다.CreatorChannelActivity의onCreatorChannelCommunityOwnerMoreClicked(postId: Long) = Unitno-op 구현을 제거한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"./gradlew :app:compileDebugKotlin
- 기대 결과:
- owner more Host API가 item 기반 콜백으로 단일화되고 no-op 구현 없이 컴파일된다.
- 검증 기록:
- 2026-06-22: RED로
CreatorChannelActivitySourceTest에onCreatorChannelCommunityOwnerMoreClicked(postId: Long)및onCreatorChannelCommunityOwnerMoreClicked(item.postId)fallback 부재 검증을 추가했고, production 보정 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"가CreatorChannelActivitySourceTest.kt:481assertion으로 실패함을 확인했다. - 2026-06-22:
CreatorChannelCommunityFragment.Host를 item 기반onCreatorChannelCommunityOwnerMoreClicked(item)단일 API로 정리하고,CreatorChannelActivity의onCreatorChannelCommunityOwnerMoreClicked(postId: Long) = Unitno-op override를 제거했다. - 2026-06-22: 단일 GREEN으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS를 확인했다. 회귀로./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS를 확인했다. - 2026-06-22:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS를 확인했다. 최초 Gradle 실행은 sandbox의~/.gradlelock 파일 접근 제한으로 실패했으며, 사용자 승인 후 동일 명령을 재실행했다.
- 2026-06-22: RED로
- 수정:
Phase 6: 통합 검증과 수동 확인
-
Task 6.1: 단위 테스트 실행
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"
- 기대 결과:
- 커뮤니티 탭 mapper/ViewModel/pagination/layout/source 테스트가 모두 PASS한다.
- 검증 기록:
- 구현 후 기록한다.
- 실행:
-
Task 6.2: 리소스/컴파일/린트 검증
- 실행:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
- 기대 결과:
- resource merge, Kotlin compile, ktlint, whitespace 검증이 모두 PASS한다.
- 검증 기록:
- 구현 후 기록한다.
- 실행:
-
Task 6.3: 수동 화면 확인
- 확인 항목:
- 타인 채널 리스트형 기본값에서 Sort-bar 우측이
리스트형/ic_new_list로 표시된다. - 토글 시 썸네일형 3열 grid로 바뀌고 API 재호출 없이 현재 데이터를 표시한다.
- 다시 토글 시 리스트형으로 복귀한다.
- 유료 미구매 게시글은 회색 RoundedRectangle, lock, 가격 캡슐을 표시하고 재생 버튼을 숨긴다.
- 본인 또는 구매한 사용자의 오디오 게시글은 이미지 중앙에 재생/일시정지 버튼을 표시하고 기존 media player manager로 재생된다.
- 댓글 불가 게시글은 댓글 icon과 댓글 수가 보이지 않는다.
- 본인 채널 리스트형에서 본인이 쓴 게시글만 우측 상단 더보기와 유료 가격이 보인다.
- 게시글 item 터치 시 아무 동작도 하지 않는다.
- 본인 채널 Community 탭 하단
커뮤니티 글 올리기CTA가 고정 표시되고 기존 작성 화면으로 진입한다. - CTA가 목록 마지막 item 또는 empty 문구를 가리지 않는다.
hasNext == true일 때 스크롤 하단에서 다음 page가 append된다.- empty 상태에서 Sort-bar와 목록/grid가 숨겨지고 empty 문구만 표시된다.
- 타인 채널 리스트형 기본값에서 Sort-bar 우측이
- 검증 기록:
- 구현 후 기록한다.
- 확인 항목:
Verification Log
-
계획 문서 생성 단계에서는 코드 변경을 수행하지 않았다. 구현 후 통합 검증, 회귀 검증, 최종 수동 확인 기록을 이 섹션에 누적한다.
-
2026-06-22 수동 확인 후속 UI 불일치 수정 계획:
- 발견 항목: 썸네일형 전환 시 3열 Grid가 시각적으로 유지되지 않는 문제, 리스트형 공지 표시가 Figma와 다른 문제, 중앙/우측 상단 유료 가격 capsule이 Figma와 다른 문제, 게시물 이미지 14dp radius 미반영 및 GIF 미재생 문제를 확인했다.
- 수정 범위:
item_creator_channel_community_grid.xml,item_creator_channel_community_list.xml,CreatorChannelCommunityGridAdapter.kt,CreatorChannelCommunityListAdapter.kt에 한정한다. 레거시 커뮤니티 파일과 공용ImageExtensions.loadUrl()은 수정하지 않는다. - TDD 예외 사유: 이번 후속 수정은 RecyclerView item 측정, XML 배치, bitmap/GIF 로딩 라이브러리 전환처럼 실제 Android 렌더링 표면에서 확인되는 시각 보정이다. 기존 layout/source 테스트가 구조 존재와 연결을 이미 검증하고 있어, 새 production 변경 전 실패하는 순수 단위 테스트를 안정적으로 만들기 어렵다. 대신 resource merge, Kotlin compile, ktlint, diff check와 소스 기반 표면 검증으로 확인한다.
- 기대 결과: 썸네일형은
GridLayoutManager(3)에서 정사각형 item 높이를 명시적으로 받아 3열 Grid로 표시되고, 리스트형 공지는 pin icon +Notice행으로 표시되며, 유료 가격 capsule은 중앙/우측 상단 Figma 형태를 따른다. 게시물 이미지는 GlideCenterCrop + RoundedCorners(14dp)로 로드되어 GIF 재생과 14dp radius를 지원한다.
-
2026-06-22 수동 확인 후속 UI 불일치 수정 검증:
- 수정 기록: Grid item root 높이를 adapter가 계산한 정사각형 크기로 직접 지정할 수 있게 XML root를
match_parent로 변경하고, Grid/List 게시물 이미지는 GlideCenterCrop + RoundedCorners(14dp)로 로딩하도록 전환했다. 리스트형 공지는 item 최상단 pin icon +Notice행으로 이동했고, owner 우측 상단 가격 capsule과 중앙 locked 가격 capsule의 크기/typography/icon gap을 Figma 기준에 맞게 조정했다. - 보정 기록: 최초 검증에서
@color/point_400미존재로 resource linking이 실패해 기존 design token@color/green_400(#73FF01)로 교체했다. 기존 layout/source 테스트 3건은 새 XML 구조와 adapter 정사각형 계약에 맞게 기대값을 갱신했다. - 검증:
./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS,./gradlew :app:ktlintCheckPASS,git diff --checkPASS. - 수동 확인:
adb devices결과 연결된 기기가 없어 실제 앱에서 리스트/썸네일 토글, GIF 재생, radius, 공지/가격 표시를 직접 조작 검증하지 못했다. 이번 세션에서는 Figma screenshot 대조, source/layout 검증, resource/compile/test/lint로 대체했다. - 리뷰어 게이트: visual-engineering reviewer가 변경 파일, Figma 이슈 4건, 검증 증거, 실제 기기 QA 제한을 검토했고 blocking issue 없음으로 PASS했다.
- 수정 기록: Grid item root 높이를 adapter가 계산한 정사각형 크기로 직접 지정할 수 있게 XML root를
-
2026-06-22 썸네일형 후속 동작 보정 계획:
- 요구사항: 썸네일형에서는 GIF 재생을 요구하지 않고 정적 썸네일 표시를 허용한다. 썸네일형 item은 모두 정사각형 비율을 유지해야 하며, 썸네일형 상태에서도 스크롤 하단 pagination이 동작해야 한다.
- 수정 계획:
CreatorChannelCommunityGridAdapter의 Glide 요청은 grid 전용으로asBitmap()을 사용해 정적 이미지를 로드하고, 리스트형 adapter의 GIF 재생 가능 경로는 유지한다.CreatorChannelActivity는 Community content/view mode 변경으로 ViewPager 높이를 갱신한 뒤 현재 하단 조건을 재평가해 NestedScrollView의 추가 scroll 이벤트가 없어도 load-more 경로가 호출되도록 보정한다. - 검증 계획:
CreatorChannelCommunityFragmentLayoutTest,CreatorChannelActivitySourceTest, community 관련 unit test,mergeDebugResources,compileDebugKotlin,ktlintCheck,git diff --check를 실행한다. - 수정 기록: Grid adapter 이미지 요청에
asBitmap()을 적용해 썸네일형 GIF를 정적 이미지로 로드하도록 했다.updateViewPagerHeight()에 height 갱신 이후 callback을 추가하고 Community/Live/Audio/Series content 변경 시 높이 갱신 후 bottom 조건을 재평가하도록 순서를 보장했다. - 검증: focused source/layout test 최초 실행은 기존 source test가
updateViewPagerHeight()시그니처와 height 비교 문자열을 고정해 둔 탓에 2건 실패했고, 새 계약에 맞춰 테스트 기대값을 갱신한 뒤./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS를 확인했다. - 회귀/빌드:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS,./gradlew :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheckPASS,git diff --checkPASS.ktlintCheck는 기존.editorconfig disabled_rulesdeprecation 경고만 출력됐다. - 수동 확인: 현재 세션에서는 연결된 기기/에뮬레이터가 없어 실제 화면에서 썸네일형 스크롤 pagination과 GIF 정적 표시를 직접 조작 검증하지 못했다.
- 리뷰 보정 계획: context mining reviewer가 content/view mode 변경 후 bottom 재평가 경로가 기존 scroll listener의 200dp threshold와 달리
remainingScroll <= 0만 사용한다고 지적했다.checkCreatorChannelCurrentTabNeedsMore()도 동일 threshold를 사용하도록 보정하고, PRD의 GIF 재생 문구를 리스트형/썸네일형 기준으로 분리한다. - 리뷰 보정 검증:
checkCreatorChannelCurrentTabNeedsMore()가CREATOR_CHANNEL_LOAD_MORE_THRESHOLD_DP를 사용하도록 수정했고,CreatorChannelActivitySourceTest에 threshold 재사용 검증을 추가했다. PRD의 GIF 문구는 리스트형은 재생, 썸네일형은 정적 썸네일 허용으로 분리했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS,./gradlew :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheckPASS,git diff --checkPASS. - 추가 보정 계획: 수동 확인 요청에 따라 썸네일형 Grid는 3등분 구조와 기존 item margin을 유지하되, 이미지가 있는 item content가 세로로 길어지지 않고 1:1 비율을 가져야 한다. margin을 포함한 cell 폭에서 좌우 margin을 제외한 content width를 계산하고 같은 값을 height로 지정한다.
- 추가 보정 기록:
item_creator_channel_community_grid.xmlroot margin은 유지했고,CreatorChannelCommunityGridAdapter의 item size 계산은(availableWidth - itemHorizontalMargins * 3) / 3을 사용하는 helper로 분리했다.CreatorChannelCommunityFragmentLayoutTest는 360px 가용 폭과 좌우 margin 8px 기준 content 112px, 365px 기준 113px, 좁은 폭 0px 계산과height = squareSize계약을 검증한다. - 추가 보정 검증:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS.
-
2026-06-22 썸네일형 Grid Figma/레거시 재보정:
- 기준 확인: Figma
290:9078은 134px 정사각 item이 gap 0으로 배치되고, Figma290:9079공지 item은 우측 상단 pin icon만 표시한다. 레거시CreatorCommunityAllGridAdapter는 bind 시 rootlayoutParams.width와height를 같은itemSize로 지정하고,item_creator_community_all_grid.xml은 pin iconImageView를top|end에 둔다. - RED: 새 source/layout 계약을 먼저 추가했고, production 보정 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"는iv_creator_channel_community_grid_notice미존재와 helper signature 불일치로compileDebugUnitTestKotlin실패를 확인했다. - 수정 기록: v2 Grid adapter도 레거시처럼
setItemSizePx()로 item size를 받아 root width/height를 같은 값으로 고정하도록 변경했다. Grid mode에서는 RecyclerView 좌우 padding을 0으로 조정해 Figma처럼 item 사이 gap이 생기지 않게 했고, list mode 복귀 시 좌우 14dp padding을 복구한다. Grid notice는 text label 대신 우측 상단ic_pinImageView로 변경했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS. - 리뷰 게이트 FAIL: 5-agent review에서 code quality reviewer는 Grid item size가 RecyclerView width 측정 전 0으로 계산되면
itemSizePx가 0으로 남을 수 있다고 지적했고, context mining reviewer는 locked grid tile에서 text preview가 lock overlay 뒤에 표시될 수 있다고 지적했다. QA reviewer는 debug build 설치와 deep link 진입까지 확인했지만 테스트 creator id의 API error 상태로 실제 grid content visual QA는 blocked로 판정했다. - 리뷰 보정 RED:
CreatorChannelCommunityFragmentLayoutTest에 layout 이후doOnLayout기반 size 재계산 계약과 locked grid item에서 text preview를 숨기는 계약을 추가했다. Production 보정 전 focused test는updateGridItemSize()/doOnLayout미존재와 locked text 조건 미충족으로 2건 실패했다. - 리뷰 보정 기록: Grid mode bind 시
updateGridItemSize()를 즉시 호출하고RecyclerView.doOnLayout { updateGridItemSize() }로 width 측정 이후 item size를 재계산하도록 했다. Grid adapter는!item.isLocked && item.imageMode != CreatorChannelCommunityImageMode.Image일 때만 text preview를 표시해 locked tile은 lock/price overlay만 보이도록 했다. - 리뷰 보정 GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS.
- 기준 확인: Figma
-
2026-06-22 썸네일형 Grid rounded corner 제거 계획:
- 요구사항: 썸네일형 Grid item은 rounded corner를 제거하고 정사각 모서리로 표시한다. 리스트형 게시물 이미지의 14dp radius와 GIF 재생 정책은 유지한다.
- 수정 계획:
CreatorChannelCommunityGridAdapter에서 Grid 전용RoundedCorners(14dp)transform과 rootclipToOutline을 제거한다.item_creator_channel_community_grid.xml의 Grid root/image rounded drawable background는 non-rounded 배경으로 교체한다. 공유 drawable과 레거시 파일은 수정하지 않는다. - 검증 계획:
CreatorChannelCommunityFragmentLayoutTest에 Grid rounded corner 제거 계약을 RED로 추가한 뒤 production 보정 후 focused/community regression, resource merge, Kotlin compile, ktlint,git diff --check를 실행한다. - RED:
CreatorChannelCommunityFragmentLayoutTest에 Grid XML이bg_feed_card/bg_feed_community_image를 사용하지 않고, Grid adapter source가RoundedCorners,14f.dpToPx(),root.clipToOutline = true를 포함하지 않는다는 계약을 추가했다. Production 보정 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"는 rounded drawable background와RoundedCorners사용으로 2건 실패했다. - 수정 기록:
CreatorChannelCommunityGridAdapter에서RoundedCornerstransform,dpToPximport, rootclipToOutline설정을 제거하고CenterCrop()만 유지했다.item_creator_channel_community_grid.xml의 root/image background는 공유 rounded drawable 대신@color/gray_900으로 변경했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS. - 회귀/빌드:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"PASS,./gradlew :app:mergeDebugResources :app:compileDebugKotlin :app:ktlintCheckPASS,git diff --checkPASS.ktlintCheck는 기존.editorconfig disabled_rulesdeprecation 경고만 출력됐다.
-
2026-06-21 Phase 2 검증:
- RED:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityViewModelTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityPaginationTest"는 production ViewModel 구현 전CreatorChannelCommunityViewModel/CreatorChannelCommunityUiState/CreatorChannelCommunityViewMode미구현으로:app:compileDebugUnitTestKotlin실패를 확인했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 확장 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"는 229개 중CreatorChannelHomeViewModelTest > 채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다1건 실패. 동일 테스트 단독 재실행./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest.채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다"는 PASS하여 Phase 2 변경과 직접 관련 없는 비결정적 실패로 기록한다. - 빌드/리소스/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS.
- RED:
-
2026-06-21 Phase 3 코드 리뷰 및 검증:
- 코드 리뷰:
CreatorChannelCommunityMappers,CreatorChannelCommunityUiModels,CreatorChannelCommunityViewModel의 first page/load-more UI model 적용 경로, Koin binding, string resource를 Phase 3 정책과 대조했고 수정이 필요한 결함은 발견하지 않았다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 확장 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"PASS. - 빌드/리소스/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS.
- 코드 리뷰:
-
2026-06-21 Phase 4 검증:
- RED:
CreatorChannelCommunityFragmentLayoutTest를 먼저 추가했고 production 구현 전compileDebugUnitTestKotlin이 fragment/list/grid layout ID, resource, source file 미구현으로 실패함을 확인했다. - Production:
fragment_creator_channel_community.xml,item_creator_channel_community_list.xml,item_creator_channel_community_grid.xml,CreatorChannelCommunityFragment.kt,CreatorChannelCommunityListAdapter.kt,CreatorChannelCommunityGridAdapter.kt, ko/en/ja community strings를 추가했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 빌드/리소스/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS.ktlintCheck는 신규 layout test와 list adapter의 formatting-only 수정 후 PASS했다. - 참고: 병렬 Gradle 실행 1건에서 Kotlin incremental cache/daemon 충돌과 timeout이 발생했고, 영향받은 community test를 단독 재실행해 PASS를 확인했다.
- RED:
-
2026-06-21 Phase 4 reviewer gate 수정 및 재검증:
- 초기 결과: reviewer gate가 locked image와 price pill, play/pause 상태, owner more item 정보,
onPause()media pause, grid margin sizing 문제로 FAIL했다. - 수정 기록:
isPinned보존, locked itemimageUrlclearing과 이미지 load 차단, locked price pill 검증,isPlayingContent(postId)기반 play/pause icon, owner more item callback, FragmentonPause()의pauseContent(), grid margin-aware sizing, 관련 테스트 갱신을 반영했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 빌드/리소스/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS. - 최종 결과: post-fix reviewer gate PASS로 Phase 4 review-gate fixes 검증을 완료했다.
- 초기 결과: reviewer gate가 locked image와 price pill, play/pause 상태, owner more item 정보,
-
2026-06-21 Phase 4 최종 리뷰 수정 및 검증:
- 최종 수정 기록: 리스트형 locked list price capsule은
tv_creator_channel_community_list_locked_price를 이미지 잠금 영역 안으로 이동해 locked card 내부에서 표시되게 했다.ListAdapter는 locked price와 top price 조건을 분리했고, top price는 본인 채널 owner-only 게시글에서만 보이도록 복구했다.CreatorCommunityMediaPlayerManager에는prepareAsync()완료 전pauseContent()/stopContent()호출을 막는 prepared-state guard를 추가했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 리소스:
./gradlew :app:mergeDebugResourcesPASS. - 컴파일:
./gradlew :app:compileDebugKotlinPASS. - 린트:
./gradlew :app:ktlintCheckPASS. - 공백 검증:
git diff --checkPASS.
- 최종 수정 기록: 리스트형 locked list price capsule은
-
2026-06-22 Phase 4 보안 로그 제거 후 최종 검증:
- 보안 수정 기록:
CreatorChannelCommunityViewModel.kt의Logger.e(message)와Loggerimport를 제거했고,CreatorCommunityMediaPlayerManager.kt의e.printStackTrace()를 제거했다.authToken()/SharedPreferenceManager.token은 repository 호출용 bearer 생성 경로로만 남아 있으며 로그로 노출하지 않는다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 컴파일:
./gradlew :app:compileDebugKotlinPASS. - 린트:
./gradlew :app:ktlintCheckPASS. - 공백 검증:
git diff --checkPASS. - 최종 보안 재리뷰: Oracle verdict PASS, severity none, blocking_issues 없음.
- 보안 수정 기록:
-
2026-06-22 Phase 4 코드 리뷰 및 검증:
- 코드 리뷰: Figma
665:19021과 Phase 4 요구사항을 기준으로 리스트형 owner 유료 가격 태그 위치, 잠금 이미지 처리, play/pause 상태, owner more callback, grid sizing, media player 생명주기를 대조했다. 가격 태그가 reaction row에 배치된 결함 1건을 발견했고, 우측 상단 액션 컨테이너로 이동해 수정했다. - RED:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"는layout_creator_channel_community_list_top_actions미구현으로 실패함을 확인했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS. - 리소스/컴파일/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheckPASS. - 공백 검증:
git diff --checkPASS. - 참고: Gradle 실행 중 기존
WeekCalendarAdapter.ktKotlin annotation target 경고, 기존 테스트 deprecation 경고, 기존.editorconfig의disabled_rulesdeprecation 경고가 출력됐으나 이번 Phase 4 변경 파일의 실패는 없었다.
- 코드 리뷰: Figma
-
2026-06-22 Phase 5 검증:
- RED:
CreatorChannelPagerAdapterTest는 production 연결 전 Community가 placeholder라CreatorChannelPagerAdapterTest.kt:31assertion으로 실패했고,CreatorChannelActivitySourceTest > Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다는 production 연결 전CreatorChannelActivitySourceTest.kt:455assertion으로 실패함을 확인했다. - Production:
CreatorChannelPagerAdapter에서CreatorChannelTab.Community를CreatorChannelCommunityFragment.newInstance(creatorId)로 연결했다.CreatorChannelActivity에는CreatorChannelCommunityFragment.Host,findCommunityFragment(), 탭 선택/header 변경 최초 로드, nested scroll pagination, content changed 높이 갱신/추가 load-more 재평가, Community owner CTA icon/text/click/padding callback 연결을 추가했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"PASS. 기존tab source는 기존 custom tab no op 정책을 제거한다의 Community 부재 assertion은 Phase 5 목표와 충돌해 제거했다. - 리소스/컴파일/린트:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS.ktlintCheck는 신규 source test의 긴 assertion formatting-only 수정 후 PASS했다. - 참고: 초반 병렬 Gradle 실행으로
app/build/kspCaches/debugUnitTestKSP cache corruption과 Kotlin daemon 충돌이 발생했다. 생성 캐시app/build/kspCaches/debugUnitTest를 제거하고./gradlew --stop후 순차 재실행해 동일 테스트가 PASS함을 확인했다.
- RED:
-
2026-06-22 Phase 5 reviewer gate 수정 및 재검증:
- 초기 결과: reviewer gate가
CreatorChannelActivity.onCreatorChannelCommunityOwnerMoreClicked(item)의onClickPin/onClickModify/onClickDeleteno-op 콜백을 차단 이슈로 FAIL했다. 이는 기존CreatorCommunityPostMenuBottomSheetDialog수정/삭제/고정/고정 해제 정책을 따른다는 Phase 1.3/Phase 5 요구사항과 충돌하는 것으로 확인했다. - RED:
CreatorChannelActivitySourceTest에 owner more callback no-op 방지 검증을 추가했고, production 보정 전CreatorChannelActivitySourceTest.kt:473assertion으로 실패함을 확인했다. - 수정 기록:
CreatorChannelActivity에 기존CreatorCommunityAllViewModel을 주입해updateCommunityPostFixed(item.postId, !item.isPinned),CreatorCommunityModifyActivity결과 launcher, delete 확인SodaDialog와deleteCommunityPostList(postId = item.postId)를 연결했다. write/modify/pin/delete 성공 후 현재 Community Fragment가onCreatorChannelCommunityRefreshRequested()로 첫 페이지를 재조회하도록CreatorChannelCommunityFragment/CreatorChannelCommunityViewModelrefresh entry를 추가했다. - 후속 reviewer 결과: pin/delete 성공 후 refresh가 기존
CreatorCommunityAllViewModel.communityPostListLiveDataemit에 의존하면 빈 목록 성공 시 최신 화면 갱신이 누락될 수 있다는 차단 이슈를 확인했다. 또한 레거시 코드는 수정하지 않는다는 작업 원칙에 따라CreatorCommunityAllViewModel변경을 제거했다. - 최종 수정 기록:
CreatorChannelActivity에서 기존 레거시 ViewModel을 수정/주입하지 않고CreatorCommunityRepository를 직접 호출해 고정/고정 해제와 삭제 요청을 수행한다. 성공 응답에서는 즉시findCommunityFragment()?.onCreatorChannelCommunityRefreshRequested()를 호출해 마지막 게시글 삭제처럼 빈 목록이 되는 경우에도 Community 탭이 자체 first page refresh를 수행하도록 보정했다. 수정 진입은 기존CreatorCommunityModifyActivity를 호출만 하며, 결과 성공 시 동일 refresh entry를 호출한다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*"PASS,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"PASS. - 리소스/컴파일/whitespace:
./gradlew :app:mergeDebugResourcesPASS,./gradlew :app:compileDebugKotlinPASS,git diff --checkPASS. - 린트 참고:
./gradlew :app:ktlintCheck는 현재 diff가 없는 레거시CreatorCommunityAllViewModel.kt의 기존 package underscore/indentation 위반으로 FAIL했다. 사용자 지침에 따라 레거시 파일은 수정하지 않았고,GIT_MASTER=1 git diff -- app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllViewModel.kt결과 변경 없음으로 확인했다. - 최종 reviewer gate: Phase 5 변경, owner more callback, pin/delete 성공 refresh, 레거시
CreatorCommunityAllViewModel.kt미수정 상태, 레거시 ktlint 실패 범위 문서화를 재검토했고 PASS를 확인했다.
- 초기 결과: reviewer gate가
-
2026-06-22 Phase 5 후속 no-op Host API 제거 및 최종 검증:
- 코드 리뷰:
CreatorChannelCommunityFragment.Host에 실제 호출 경로가 item 기반으로 전환된 뒤에도postIdfallback API가 남아 있고,CreatorChannelActivity가 이를 no-op으로 구현하는 불필요한 확장 지점을 확인했다. owner more는CreatorChannelCommunityPostUiModel의isPinned등 item 상태가 필요하므로 item 기반 콜백 하나로 단일화하는 것이 Phase 5 요구사항에 더 맞다고 판단했다. - RED:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"는 production 보정 전CreatorChannelActivitySourceTest.kt:481assertion으로 실패함을 확인했다. - 수정 기록:
CreatorChannelCommunityFragment.Host의onCreatorChannelCommunityOwnerMoreClicked(postId: Long)fallback API와CreatorChannelActivity의 no-op override를 제거했고, source test에 no-op/fallback 부재 검증을 추가했다. - GREEN:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다"PASS. - 회귀:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*Community*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*"PASS. - 리소스/컴파일/린트/공백:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --checkPASS. - 참고: 첫 Gradle 실행은 sandbox의
~/.gradlelock 파일 접근 제한으로 실패했고, 사용자 승인 후 같은 테스트를 재실행해 RED/GREEN을 확인했다. 이번 최종ktlintCheck는 PASS했으며.editorconfig disabled_rulesdeprecation 경고만 출력됐다.
- 코드 리뷰: