Files
sodalive-android/docs/20260621_크리에이터_채널_커뮤니티_탭/plan-task.md

68 KiB

크리에이터 채널 커뮤니티 탭 구현 계획/TASK

For agentic workers: 각 단계는 체크박스(- [ ])로 추적하고, 완료 즉시 - [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.

Goal: GET /api/v2/creator-channels/{creatorId}/community 응답을 기반으로 크리에이터 채널의 커뮤니티 탭에 리스트/썸네일 보기 전환, 커뮤니티 게시글 목록, 유료/구매/본인 채널 표시 분기, 오디오 재생, empty 상태, 본인 채널 하단 커뮤니티 글 올리기 CTA와 pagination을 표시한다.

Architecture: 기존 CreatorChannelActivityViewPager2/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.kt
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • app/src/main/res/layout/activity_creator_channel.xml
  • 기존 채널 API/Repository:
    • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
    • app/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.kt
    • app/src/main/res/layout/view_feed_community.xml
    • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt
    • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllGridAdapter.kt
    • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityPostMenuBottomSheetDialog.kt
    • app/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이다.
  • 첫 페이지 page0, 기본 size20이다.
  • 보기 방식은 클라이언트 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:ktlintCheck
    • git diff --check

Figma 참조 필요 Phase

  • Phase 1: 제한 참조
    • 기존 코드 경계, 위젯 재사용 가능성, 기존 커뮤니티 정책 확인이 중심이며 Figma는 PRD 기준만 확인한다.
  • Phase 2: Figma 참조 불필요
    • API/DTO/Repository/ViewModel 상태 모델은 서버 계약과 기존 오디오/시리즈 탭 패턴을 따른다.
  • Phase 3: 제한 참조
    • mapper와 item state는 PRD, 기존 FeedCommunityView, 기존 CreatorCommunityAllGridAdapter 정책을 함께 확인한다.
  • Phase 4: 필수 참조
    • 리스트형 item, 유료 미구매 잠금 영역, 썸네일형 grid item, Sort-bar는 Figma 290:9061, 290:9073, 290:9066, 665:19021 기준으로 구현한다.
  • Phase 5: 제한 참조
    • 탭 연결, pagination, owner CTA, media player 연결은 기존 코드 패턴 중심으로 검증한다.
  • Phase 6: 필수 참조
    • 최종 수동 화면 검증은 PRD의 모든 Figma 노드와 실제 화면을 대조한다.

파일 구조

  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt
    • CreatorChannelTab.Community를 신규 CreatorChannelCommunityFragment로 연결한다.
  • 수정: app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
    • CreatorChannelCommunityFragment.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.kt
    • CreatorChannelCommunityTabResponse, 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으로 만든다.
  • 생성: 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.kt
    • CreatorChannelCommunityViewModel binding을 추가한다.
  • 테스트 생성:
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityMapperTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModelTest.kt
    • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityPaginationTest.kt
    • app/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.kt
    • app/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.kt
      • app/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.ktwhen (tab)이다.
      • 2026-06-21: CreatorChannelPagerAdapterTest는 Home/Live/Audio/Series 외 탭이 placeholder임을 검증하고, CreatorChannelActivitySourceTest는 현재 Community 분기가 없음을 확인한다. Phase 5에서 두 테스트의 기대값 갱신이 필요하다.
  • Task 1.2: v2 FeedCommunityView 재사용 가능성 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedCommunityView.kt
      • app/src/main/res/layout/view_feed_community.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/widget/feed/FeedItem.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt
    • 작업:
      • FeedItem.CommunitycreatorProfileUrl, 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)은 담을 수 있다. FeedCommunityViewview_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로 구현하는 것으로 경계를 고정한다.
  • Task 1.3: 기존 커뮤니티 그리드/더보기/재생 정책 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllGridAdapter.kt
      • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityPostMenuBottomSheetDialog.kt
      • app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/player/CreatorCommunityMediaPlayerManager.kt
      • app/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))를 사용한다.
    • 검증:
      • 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: CreatorCommunityMediaPlayerManagerCreatorCommunityContentItem(contentId, url)을 받아 toggleContent()로 재생/일시정지를 전환하고 isPlayingContent(contentId)stopContent()를 제공한다. 커뮤니티 탭 오디오 재생도 이 manager를 재사용한다.
  • Task 1.4: Owner CTA 진입점과 리소스 확인

    • 확인:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/src/main/res/layout/activity_creator_channel.xml
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/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 포함이 필요하다.

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 검증 대상으로 포함한다.
  • Task 2.2: 커뮤니티 탭 endpoint와 Repository method 추가

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt
      • app/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 검증 대상으로 포함한다.
  • Task 2.3: ViewModel RED 테스트 작성

    • 생성:
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityViewModelTest.kt
      • app/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 실패한다.
    • 검증 기록:
      • 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 실패함을 확인했다.
  • 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 구조로 구현하고 AppDI Koin 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했다.

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를 확인했다.
  • Task 3.2: UI model과 mapper 구현

    • 생성:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityUiModels.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/model/CreatorChannelCommunityMappers.kt
    • 작업:
      • CreatorChannelCommunityViewModeList, 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()로 매핑한다.
      • showOwnerMoreshowOwnerTopPriceisOwner == 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자 정책을 재사용했다. CreatorChannelCommunityViewModeList/Grid label과 drawable-mdpiic_new_list/ic_new_grid icon 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.communityPostsCreatorChannelCommunityPostUiModel 목록을 들도록 보정했다. 보정 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*", ./gradlew :app:compileDebugKotlin, ./gradlew :app:mergeDebugResources, ./gradlew :app:ktlintCheck, git diff --check PASS했다.
      • 2026-06-21: 후속 리뷰에서 ViewModel이 Context를 직접 주입받아 생명주기상 memory leak 가능성이 있다는 지적을 반영했다. UtcRelativeTimeTextFormatterAndroidUtcRelativeTimeTextFormatter를 추가하고, CreatorChannelCommunityViewModelContext 대신 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 --check PASS했다.
      • 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했다.

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 영역은 오디오/시리즈 탭 패턴을 따른다.
    • 검증 명령:
      • ./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:mergeDebugResources PASS로 신규 layout binding 생성을 확인했다.
  • Task 4.2: 리스트형 item layout/adapter 추가

    • 생성:
      • app/src/main/res/layout/item_creator_channel_community_list.xml
      • app/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으로 둔다.
    • 검증 명령:
      • ./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.xmlCreatorChannelCommunityListAdapter.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하도록 보정했다.
  • Task 4.3: 썸네일형 grid item layout/adapter 추가

    • 생성:
      • app/src/main/res/layout/item_creator_channel_community_grid.xml
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/ui/CreatorChannelCommunityGridAdapter.kt
    • 작업:
      • 3열 정사각형 item을 유지하도록 layout params를 adapter에서 계산하거나 GridLayoutManager span 폭에 맞춘다.
      • imageUrl != null && !isLocked이면 이미지 전체 표시.
      • imageUrl == null && !isLocked이면 gridPreviewText를 중앙 정렬로 표시.
      • isLocked == true이면 잠금/가격 표시를 노출한다.
      • showNotice == true이면 pin/notice icon을 상단에 표시한다.
      • root item click listener는 설정하지 않거나 no-op으로 둔다.
    • 검증 명령:
      • ./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.xmlCreatorChannelCommunityGridAdapter.kt를 추가해 3열 정사각형 grid, 이미지/텍스트 preview, 잠금/가격, notice 표시 정책을 구현했다.
      • 2026-06-21: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.CreatorChannelCommunityFragmentLayoutTest" PASS와 ./gradlew :app:mergeDebugResources PASS로 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했다.
  • 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/Grid LayoutManager 전환, 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로 CreatorCommunityMediaPlayerManagerprepareAsync() 완료 전 pauseContent()stopContent()가 호출될 수 있는 경로를 막는 prepared-state guard를 추가했다. prepare 전 pause/stop 호출은 MediaPlayer invalid state를 만들지 않도록 보호하고, Fragment 생명주기 정리 경로는 유지했다.
  • Task 4.5: 문자열/리소스 정리

    • 수정:
      • app/src/main/res/values/strings.xml
      • app/src/main/res/values-en/strings.xml
      • app/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:mergeDebugResources PASS, ./gradlew :app:compileDebugKotlin PASS, ./gradlew :app:ktlintCheck PASS, git diff --check PASS를 확인했다. ktlintCheck는 신규 layout test와 list adapter의 formatting-only 수정 후 PASS했다.

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.kt
      • app/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:31 assertion으로 실패함을 확인했다.
      • 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 연결을 확인했다.
  • Task 5.2: CreatorChannelActivity Host 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/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:455 assertion으로 실패함을 확인했다.
      • 2026-06-22: CreatorChannelActivityCreatorChannelCommunityFragment.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 계약을 확인했다.
  • 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:mergeDebugResources PASS로 source/resource 검증을 확인했다.
  • Task 5.4: media player 생명주기와 adapter 갱신 연결

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt
      • app/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에서 이미 구현된 CreatorChannelCommunityFragmentCreatorCommunityMediaPlayerManager(requireContext()) { listAdapter.notifyDataSetChanged() }, CreatorCommunityContentItem(item.postId, audioUrl) 기반 toggleContent(), adapter isPlayingContent(postId) icon bind, onPause()/onDestroyView() 정리 경로를 Phase 5 source test로 재확인했다. 신규 production 변경은 Activity/Pager 연결에 한정했다.
      • 2026-06-22: ./gradlew :app:compileDebugKotlin PASS로 기존 media player manager import와 호출 컴파일을 확인했다.
  • Task 5.5: reviewer gate 후속 no-op Host API 제거

    • 수정:
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/community/CreatorChannelCommunityFragment.kt
      • app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
      • app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
    • 작업:
      • CreatorChannelCommunityFragment.Host에서 실제 호출되지 않는 postId 기반 owner more fallback API를 제거한다.
      • CreatorChannelActivityonCreatorChannelCommunityOwnerMoreClicked(postId: Long) = Unit no-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로 CreatorChannelActivitySourceTestonCreatorChannelCommunityOwnerMoreClicked(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:481 assertion으로 실패함을 확인했다.
      • 2026-06-22: CreatorChannelCommunityFragment.Host를 item 기반 onCreatorChannelCommunityOwnerMoreClicked(item) 단일 API로 정리하고, CreatorChannelActivityonCreatorChannelCommunityOwnerMoreClicked(postId: Long) = Unit no-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 --check PASS를 확인했다. 최초 Gradle 실행은 sandbox의 ~/.gradle lock 파일 접근 제한으로 실패했으며, 사용자 승인 후 동일 명령을 재실행했다.

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:ktlintCheck
      • git 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 문구만 표시된다.
    • 검증 기록:
      • 구현 후 기록한다.

Verification Log

  • 계획 문서 생성 단계에서는 코드 변경을 수행하지 않았다. 구현 후 통합 검증, 회귀 검증, 최종 수동 확인 기록을 이 섹션에 누적한다.

  • 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 --check PASS.
  • 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 --check PASS.
  • 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 --check PASS. ktlintCheck는 신규 layout test와 list adapter의 formatting-only 수정 후 PASS했다.
    • 참고: 병렬 Gradle 실행 1건에서 Kotlin incremental cache/daemon 충돌과 timeout이 발생했고, 영향받은 community test를 단독 재실행해 PASS를 확인했다.
  • 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 item imageUrl clearing과 이미지 load 차단, locked price pill 검증, isPlayingContent(postId) 기반 play/pause icon, owner more item callback, Fragment onPause()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 --check PASS.
    • 최종 결과: post-fix reviewer gate PASS로 Phase 4 review-gate fixes 검증을 완료했다.
  • 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:mergeDebugResources PASS.
    • 컴파일: ./gradlew :app:compileDebugKotlin PASS.
    • 린트: ./gradlew :app:ktlintCheck PASS.
    • 공백 검증: git diff --check PASS.
  • 2026-06-22 Phase 4 보안 로그 제거 후 최종 검증:

    • 보안 수정 기록: CreatorChannelCommunityViewModel.ktLogger.e(message)Logger import를 제거했고, CreatorCommunityMediaPlayerManager.kte.printStackTrace()를 제거했다. authToken()/SharedPreferenceManager.token은 repository 호출용 bearer 생성 경로로만 남아 있으며 로그로 노출하지 않는다.
    • GREEN: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.community.*" PASS.
    • 컴파일: ./gradlew :app:compileDebugKotlin PASS.
    • 린트: ./gradlew :app:ktlintCheck PASS.
    • 공백 검증: git diff --check PASS.
    • 최종 보안 재리뷰: 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:ktlintCheck PASS.
    • 공백 검증: git diff --check PASS.
    • 참고: Gradle 실행 중 기존 WeekCalendarAdapter.kt Kotlin annotation target 경고, 기존 테스트 deprecation 경고, 기존 .editorconfigdisabled_rules deprecation 경고가 출력됐으나 이번 Phase 4 변경 파일의 실패는 없었다.
  • 2026-06-22 Phase 5 검증:

    • RED: CreatorChannelPagerAdapterTest는 production 연결 전 Community가 placeholder라 CreatorChannelPagerAdapterTest.kt:31 assertion으로 실패했고, CreatorChannelActivitySourceTest > Community tab source는 Fragment Host pagination owner CTA를 Activity에 연결한다는 production 연결 전 CreatorChannelActivitySourceTest.kt:455 assertion으로 실패함을 확인했다.
    • Production: CreatorChannelPagerAdapter에서 CreatorChannelTab.CommunityCreatorChannelCommunityFragment.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 --check PASS. ktlintCheck는 신규 source test의 긴 assertion formatting-only 수정 후 PASS했다.
    • 참고: 초반 병렬 Gradle 실행으로 app/build/kspCaches/debugUnitTest KSP cache corruption과 Kotlin daemon 충돌이 발생했다. 생성 캐시 app/build/kspCaches/debugUnitTest를 제거하고 ./gradlew --stop 후 순차 재실행해 동일 테스트가 PASS함을 확인했다.
  • 2026-06-22 Phase 5 reviewer gate 수정 및 재검증:

    • 초기 결과: reviewer gate가 CreatorChannelActivity.onCreatorChannelCommunityOwnerMoreClicked(item)onClickPin/onClickModify/onClickDelete no-op 콜백을 차단 이슈로 FAIL했다. 이는 기존 CreatorCommunityPostMenuBottomSheetDialog 수정/삭제/고정/고정 해제 정책을 따른다는 Phase 1.3/Phase 5 요구사항과 충돌하는 것으로 확인했다.
    • RED: CreatorChannelActivitySourceTest에 owner more callback no-op 방지 검증을 추가했고, production 보정 전 CreatorChannelActivitySourceTest.kt:473 assertion으로 실패함을 확인했다.
    • 수정 기록: CreatorChannelActivity에 기존 CreatorCommunityAllViewModel을 주입해 updateCommunityPostFixed(item.postId, !item.isPinned), CreatorCommunityModifyActivity 결과 launcher, delete 확인 SodaDialogdeleteCommunityPostList(postId = item.postId)를 연결했다. write/modify/pin/delete 성공 후 현재 Community Fragment가 onCreatorChannelCommunityRefreshRequested()로 첫 페이지를 재조회하도록 CreatorChannelCommunityFragment/CreatorChannelCommunityViewModel refresh entry를 추가했다.
    • 후속 reviewer 결과: pin/delete 성공 후 refresh가 기존 CreatorCommunityAllViewModel.communityPostListLiveData emit에 의존하면 빈 목록 성공 시 최신 화면 갱신이 누락될 수 있다는 차단 이슈를 확인했다. 또한 레거시 코드는 수정하지 않는다는 작업 원칙에 따라 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:mergeDebugResources PASS, ./gradlew :app:compileDebugKotlin PASS, git diff --check PASS.
    • 린트 참고: ./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를 확인했다.
  • 2026-06-22 Phase 5 후속 no-op Host API 제거 및 최종 검증:

    • 코드 리뷰: CreatorChannelCommunityFragment.Host에 실제 호출 경로가 item 기반으로 전환된 뒤에도 postId fallback API가 남아 있고, CreatorChannelActivity가 이를 no-op으로 구현하는 불필요한 확장 지점을 확인했다. owner more는 CreatorChannelCommunityPostUiModelisPinned 등 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:481 assertion으로 실패함을 확인했다.
    • 수정 기록: CreatorChannelCommunityFragment.HostonCreatorChannelCommunityOwnerMoreClicked(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 --check PASS.
    • 참고: 첫 Gradle 실행은 sandbox의 ~/.gradle lock 파일 접근 제한으로 실패했고, 사용자 승인 후 같은 테스트를 재실행해 RED/GREEN을 확인했다. 이번 최종 ktlintCheck는 PASS했으며 .editorconfig disabled_rules deprecation 경고만 출력됐다.