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

44 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한다.
  • 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한다.
  • 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한다.
  • 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이다.
  • 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 참조가 모두 해소된다.

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로 연결됨이 검증된다.
  • 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한다.
  • 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 검증으로 확인된다.
  • 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와 호출이 컴파일된다.

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.