52 KiB
크리에이터 채널 FanTalk 탭 구현 계획/TASK
For agentic workers: 각 단계는 체크박스(
- [ ])로 추적하고, 완료 즉시- [x]로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
Goal: GET /api/v2/creator-channels/{creatorId}/fan-talks 응답을 기반으로 크리에이터 채널의 FanTalk 탭에 전체 개수, FanTalk 목록, 크리에이터 답글 1개, 권한별 신고/더보기/삭제, empty 상태, 표시 전용 글쓰기 버튼과 pagination을 표시한다.
Architecture: 기존 CreatorChannelActivity의 ViewPager2/CreatorChannelPagerAdapter 구조를 유지하고, CreatorChannelTab.FanTalk의 placeholder를 신규 CreatorChannelFanTalkFragment로 교체한다. FanTalk 탭 전용 Fragment/ViewModel/DTO/mapper/adapter/popup은 kr.co.vividnext.sodalive.v2.creator.channel.fantalk 하위에 두되, API/Repository는 기존 채널 공통 CreatorChannelApi/CreatorChannelRepository에 endpoint와 FanTalk 삭제/신고 위임만 추가한다. 날짜 표시는 v2 공통 UtcRelativeTimeTextFormatter를 사용하고, 신고/삭제는 기존 레거시 CheersReportDialog, ReportRepository, ExplorerRepository.modifyCheers() 계약을 재사용한다.
Tech Stack: Kotlin, Android XML Views, ViewBinding, RecyclerView, Retrofit, Gson, RxJava3, Koin, JUnit4/Robolectric local unit test.
전제와 성공 기준
- PRD:
docs/20260622_FanTalk_탭/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
- 기존 v2 날짜 포맷:
app/src/main/java/kr/co/vividnext/sodalive/common/RelativeTimeFormatter.ktUtcRelativeTimeTextFormatterAndroidUtcRelativeTimeTextFormatter
- 기존 FanTalk 홈 카드 참조:
app/src/main/res/layout/item_creator_channel_home_fantalk.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelFanTalkCardView.kt
- 기존 신고/삭제 흐름 참조:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/fantalk/UserProfileFantalkAllViewActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/fantalk/UserProfileFantalkAllViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/report/CheersReportDialog.ktapp/src/main/java/kr/co/vividnext/sodalive/report/ReportRequest.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/cheers/PutModifyCheersRequest.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt
- Figma:
- 전체 목록:
290:9139 - empty 상태:
290:9000
- 전체 목록:
- API endpoint는
GET /api/v2/creator-channels/{creatorId}/fan-talks이다. - 첫 페이지
page는0, 기본size는20이다. - query parameter는
page,size만 전달한다. - 정렬 query parameter와 sort popup은 FanTalk 탭에서 사용하지 않는다.
creatorReplies는 응답 순서 기준 첫 번째 1개만 표시한다.- 내가 쓴 글이면 더보기 popup에
수정하기,삭제하기를 표시한다. - 내 채널에서 타인이 작성한 글이면 더보기 popup에
삭제하기만 표시한다. 수정하기는 표시만 하고 화면/API를 연결하지 않는다.삭제하기는PutModifyCheersRequest(cheersId = fanTalkId, isActive = false)를ExplorerRepository.modifyCheers()로 요청한다.- 신고는
ReportRequest(type = ReportType.CHEERS, reason = reason, cheersId = fanTalkId)를ReportRepository.report()로 요청한다. - 목록 content 상태에서는 우측 하단 floating 글쓰기 버튼을 표시하되 클릭 시 아무 화면/API도 연결하지 않는다.
- empty 상태에서는 Sort-bar와 우측 하단 floating 버튼을 숨기고 중앙 empty 문구와
응원 남기기capsule button을 표시한다. - empty 상태의
응원 남기기button도 클릭 시 아무 화면/API도 연결하지 않는다. - empty 문구는 다음 다국어 문자열 리소스로 제공한다.
- 한국어:
아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요! - 영어:
No cheers yet.\nBe the first to cheer for the creator! - 일본어:
まだ応援がありません。\n最初にクリエイターを応援してみましょう!
- 한국어:
- 구현 완료 후 최소 다음 명령을 실행한다.
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"./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: 제한 참조
- 기존 코드 경계, placeholder, 신고/삭제 재사용 경계 확인이 중심이며 Figma는 PRD 기준만 확인한다.
- Phase 2: Figma 참조 불필요
- API/DTO/Repository/ViewModel 상태 모델은 서버 계약과 기존 Community/Series 탭 패턴을 따른다.
- Phase 3: 제한 참조
- mapper와 action state는 PRD, 기존 FanTalk adapter, v2 날짜 formatter 정책을 함께 확인한다.
- Phase 4: 필수 참조
- Sort-bar, list item, 답글 card, 권한별 우측 action, popup, floating button은 Figma
290:9139기준으로 구현한다.
- Sort-bar, list item, 답글 card, 권한별 우측 action, popup, floating button은 Figma
- Phase 5: 필수 참조
- empty 상태는 Figma
290:9000기준으로 구현한다.
- empty 상태는 Figma
- Phase 6: 제한 참조
- 탭 연결, pagination, 신고/삭제 dialog/API 연결은 기존 코드 패턴 중심으로 검증한다.
- Phase 7: 필수 참조
- 최종 수동 화면 검증은 PRD의 Figma 노드와 실제 화면을 대조한다.
파일 구조
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.ktCreatorChannelTab.FanTalk을 신규CreatorChannelFanTalkFragment로 연결한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktCreatorChannelFanTalkFragment.Host구현, FanTalk 탭 선택 시 최초 로드, pagination trigger, ViewPager 높이 갱신, 신고/삭제 확인 dialog 표시, 삭제 성공 후 refresh 연결을 추가한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelApi.kt- FanTalk 탭 endpoint를 추가한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt- FanTalk 목록 조회, 신고, 삭제 repository method를 추가한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/data/CreatorChannelFanTalkTabResponse.ktCreatorChannelFanTalkTabResponse,CreatorChannelFanTalkResponse,CreatorChannelFanTalkReplyResponse를 정의한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModel.kt- 최초 조회, retry, refresh, pagination, 신고, 삭제, loading/error/empty/content 상태를 관리한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/model/CreatorChannelFanTalkUiModels.kt- FanTalk item, reply, 권한별 action, 화면 상태 UI model을 정의한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/model/CreatorChannelFanTalkMappers.kt- DTO를 UI model로 변환하고
creatorReplies.firstOrNull(), 날짜 포맷, 권한별 action 표시를 결정한다.
- DTO를 UI model로 변환하고
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt- FanTalk 탭 UI, adapter, popup, retry, pagination error toast, report/delete callback, host callback 연결을 담당한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkAdapter.kt- FanTalk 목록 RecyclerView adapter를 담당한다.
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkMorePopup.kt수정하기,삭제하기popup 표시와 click callback을 담당한다.
- 생성:
app/src/main/res/layout/fragment_creator_channel_fantalk.xml- Sort-bar 없는 count bar, RecyclerView, empty, error/retry, floating write button 영역을 포함한다.
- 생성:
app/src/main/res/layout/item_creator_channel_fantalk.xml- 원글, 권한별 우측 action, 크리에이터 답글 card 1개를 구현한다.
- 생성:
app/src/main/res/layout/view_creator_channel_fantalk_more_popup.xml- 권한별
수정하기/삭제하기popup menu layout을 구현한다.
- 권한별
- 생성 가능:
app/src/main/res/drawable/bg_creator_channel_fantalk_reply.xml- 답글 card 배경이 기존 drawable로 대응되지 않을 때만 추가한다.
- 생성 가능:
app/src/main/res/drawable/bg_creator_channel_fantalk_empty_button.xml- empty capsule button 배경이 기존 home FanTalk/donation drawable로 대응되지 않을 때만 추가한다.
- 수정:
app/src/main/res/values/strings.xml- FanTalk 탭 empty/error/retry/action/report/delete/count 문구를 추가하거나 기존 문자열을 재사용한다.
- 수정:
app/src/main/res/values-en/strings.xml,app/src/main/res/values-ja/strings.xml- 신규 문자열의 다국어 값을 추가한다.
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.ktCreatorChannelFanTalkViewModelbinding을 추가한다.
- 테스트 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkMapperTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkPaginationTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkActionTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragmentLayoutTest.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: FanTalk 탭 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.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapterTest.kt
- 작업:
CreatorChannelTab.FanTalk이 현재CreatorChannelPlaceholderFragment로 연결되는지 확인한다.- 신규
CreatorChannelFanTalkFragment.newInstance(creatorId)로 교체할 위치를 고정한다.
- 검증:
rg -n "CreatorChannelTab\\.FanTalk|CreatorChannelPlaceholderFragment|createFragment" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel- 기대 결과: FanTalk placeholder와 관련 테스트 갱신 지점이 확인된다.
- 검증 기록:
- 2026-06-22:
rg -n "CreatorChannelTab\\.FanTalk|CreatorChannelPlaceholderFragment|createFragment" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel실행. CreatorChannelPagerAdapter.kt:20-29에서 Home/Live/Audio/Series/Community만 실제 Fragment로 분기하고FanTalk은 현재else -> CreatorChannelPlaceholderFragment.newInstance(tab)경로에 포함됨을 확인했다.CreatorChannelPagerAdapterTest.kt:23-43은 FanTalk을 포함한 나머지 탭 placeholder 기대값을 갖고 있으며,CreatorChannelActivitySourceTest.kt:419는 FanTalk 분기 부재를 검증하므로 Phase 5에서 함께 갱신해야 한다.
- 2026-06-22:
- 확인:
-
Task 1.2: 기존 v2 목록 탭 패턴 확인
- 확인:
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/CreatorChannelCommunityViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesViewModel.kt
- 작업:
- FanTalk ViewModel도
FIRST_PAGE = 0,DEFAULT_PAGE_SIZE = 20,requestGeneration,paginationErrorMessage,consumePaginationErrorMessage()패턴을 따른다. - Fragment도
onCreatorChannelFanTalkTabSelected(),onCreatorChannelFanTalkScrolledToBottom(),onCreatorChannelFanTalkRefreshRequested()entry를 제공한다.
- FanTalk ViewModel도
- 검증:
rg -n "requestGeneration|paginationErrorMessage|consumePaginationErrorMessage|ScrolledToBottom|RefreshRequested" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel- 기대 결과: Community/Series의 pagination과 refresh 패턴이 확인된다.
- 검증 기록:
- 2026-06-22:
rg -n "requestGeneration|paginationErrorMessage|consumePaginationErrorMessage|ScrolledToBottom|RefreshRequested" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel실행. CreatorChannelCommunityViewModel.kt:30-38,CreatorChannelCommunityViewModel.kt:61-92,CreatorChannelCommunityViewModel.kt:94-168에서 중복 최초 조회 방지,FIRST_PAGE = 0,DEFAULT_PAGE_SIZE = 20,requestGeneration, pagination error 유지/consume 패턴을 확인했다.CreatorChannelSeriesViewModel.kt:30-38,CreatorChannelSeriesViewModel.kt:54-85,CreatorChannelSeriesViewModel.kt:87-157에서도 동일한 pagination/requestGeneration 패턴을 확인했다.CreatorChannelCommunityFragment.kt:91-103,CreatorChannelCommunityFragment.kt:154-156에서 탭 선택/하단 스크롤/refresh entry와 pagination toast consume 패턴을 확인했고,CreatorChannelActivity.kt:820-825의 현재 탭 하단 스크롤 dispatcher에 FanTalk 분기 추가 지점이 있음을 확인했다.
- 2026-06-22:
- 확인:
-
Task 1.3: 기존 신고/삭제 재사용 경계 확인
- 확인:
app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/fantalk/UserProfileFantalkAllViewActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/fantalk/UserProfileFantalkAllViewModel.ktapp/src/main/java/kr/co/vividnext/sodalive/report/CheersReportDialog.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/profile/cheers/PutModifyCheersRequest.ktapp/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt
- 작업:
- 신고 dialog는
CheersReportDialog(requireActivity(), layoutInflater)를 사용한다. - 신고 사유가 비어 있으면
screen_user_profile_fantalk_report_reason_requiredtoast를 표시한다. - 신고 요청은
ReportType.CHEERS,cheersId = fanTalkId로 고정한다. - 삭제 요청은
PutModifyCheersRequest(cheersId = fanTalkId, isActive = false)로 고정한다. - 레거시 파일은 직접 수정하지 않는다.
- 신고 dialog는
- 검증:
rg -n "CheersReportDialog|ReportType\\.CHEERS|PutModifyCheersRequest|isActive = false|modifyCheers" app/src/main/java/kr/co/vividnext/sodalive/explorer app/src/main/java/kr/co/vividnext/sodalive/report- 기대 결과: 기존 신고/삭제 계약이 확인된다.
- 검증 기록:
- 2026-06-22:
rg -n "CheersReportDialog|ReportType\\.CHEERS|PutModifyCheersRequest|isActive = false|modifyCheers" app/src/main/java/kr/co/vividnext/sodalive/explorer app/src/main/java/kr/co/vividnext/sodalive/report실행. UserProfileFantalkAllViewActivity.kt:111-128에서 삭제 확인 후viewModel.modifyCheers(..., isActive = false)호출 패턴을 확인했다.UserProfileFantalkAllViewActivity.kt:200-214에서CheersReportDialog(this, layoutInflater)사용, blank reason 시screen_user_profile_fantalk_report_reason_requiredtoast, reason 존재 시 신고 요청 위임 패턴을 확인했다.UserProfileFantalkAllViewModel.kt:85-119에서ReportRequest(ReportType.CHEERS, reason, cheersId = cheersId)를ReportRepository.report()로 위임하는 계약을 확인했다.UserProfileFantalkAllViewModel.kt:167-232,ExplorerRepository.kt:64-67에서PutModifyCheersRequest(cheersId)에isActive를 설정해ExplorerRepository.modifyCheers()로 위임하는 계약을 확인했다. 레거시 파일은 수정하지 않고 신규 v2 경계에서 호출만 재사용한다.
- 2026-06-22:
- 확인:
-
Task 1.4: FanTalk 문자열과 리소스 재사용 경계 확인
- 확인:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xmlapp/src/main/res/drawableapp/src/main/res/layout/item_creator_channel_home_fantalk.xml
- 작업:
ic_new_more,ic_new_fantalk_plus,ic_placeholder_profile재사용 가능 여부를 확인한다.- empty 문구는 신규
creator_channel_fantalk_empty_message로 추가한다. - button label은 기존
creator_channel_fantalk_support_action을 재사용한다. 수정하기,삭제하기,신고,전체문구는 기존 문자열이 PRD 문구와 다르면 FanTalk 탭 전용 문자열을 추가한다.
- 검증:
rg -n "ic_new_more|ic_new_fantalk_plus|creator_channel_fantalk_support_action|screen_user_profile_cheer_edit|confirm_delete_title|report|creator_channel_.*all" app/src/main/res- 기대 결과: 재사용 가능한 리소스와 신규 문자열 추가 대상이 구분된다.
- 검증 기록:
- 2026-06-22:
rg -n "ic_new_more|ic_new_fantalk_plus|creator_channel_fantalk_support_action|screen_user_profile_cheer_edit|confirm_delete_title|report|creator_channel_.*all" app/src/main/res실행. item_creator_channel_home_fantalk.xml:58-64,item_creator_channel_home_fantalk.xml:117-129에서ic_placeholder_profile,ic_new_fantalk_plus,creator_channel_fantalk_support_action재사용 가능성을 확인했다.activity_creator_channel.xml:235,item_creator_channel_community_list.xml:131에서ic_new_more재사용 사례를 확인했다.strings.xml:99-101,strings.xml:331,strings.xml:950,strings.xml:953및 en/ja 대응 문자열에서report_button,confirm_delete_title,creator_channel_fantalk_support_action,screen_user_profile_cheer_edit,screen_user_profile_fantalk_report_reason_required재사용 가능성을 확인했다.- PRD의 탭 전용 empty/error/retry/count/action 문구 중 기존 문구와 다른 값은 Phase 4에서
creator_channel_fantalk_empty_message,creator_channel_fantalk_error_message,creator_channel_fantalk_retry,creator_channel_fantalk_all_label,creator_channel_fantalk_report,creator_channel_fantalk_edit,creator_channel_fantalk_delete로 신규 추가한다.
- 2026-06-22:
- 확인:
Phase 2: API/DTO/Repository/ViewModel 계약 추가
-
Task 2.1: FanTalk 탭 DTO 추가
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/data/CreatorChannelFanTalkTabResponse.kt
- 작업:
@Keep,@SerializedName기반으로CreatorChannelFanTalkTabResponse,CreatorChannelFanTalkResponse,CreatorChannelFanTalkReplyResponse를 추가한다.- PRD의 서버 필드명과 동일하게
fanTalkCount,fanTalks,page,size,hasNext,fanTalkId,writerId,writerNickname,writerProfileImageUrl,content,createdAtUtc,creatorReplies를 정의한다. @JsonProperty("hasNext")대신 프로젝트 기존 Gson 패턴인@SerializedName("hasNext")를 사용한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- 신규 DTO 추가 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkTabResponse.kt에@Keep/@SerializedName기반CreatorChannelFanTalkTabResponse,CreatorChannelFanTalkResponse,CreatorChannelFanTalkReplyResponse를 추가했다. fanTalkCount,fanTalks,page,size,hasNext,fanTalkId,writerId,writerNickname,writerProfileImageUrl,content,createdAtUtc,creatorReplies필드가 PRD/계획 문서의 서버 필드명과 일치함을 확인했다../gradlew :app:compileDebugKotlin실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 2.2: FanTalk 탭 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}/fan-talks")endpoint를 추가한다.- query parameter
page,size만 전달한다. - Repository method는
getFanTalks(creatorId, page, size, token)형태로 둔다. CreatorChannelRepository.reportFanTalk(fanTalkId, reason, token)는ReportRequest(ReportType.CHEERS, reason, cheersId = fanTalkId)를reportRepository.report()로 위임한다.CreatorChannelRepository.deleteFanTalk(fanTalkId, token)는PutModifyCheersRequest(cheersId = fanTalkId, isActive = false)를explorerRepository.modifyCheers()로 위임한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- API/Repository 추가 후 기존 Koin graph와 충돌 없이 컴파일된다.
- 검증 기록:
- 2026-06-22:
CreatorChannelApi.getFanTalks()에@GET("/api/v2/creator-channels/{creatorId}/fan-talks")endpoint를 추가하고 query parameter가page,size만 전달되도록 구현했다. CreatorChannelRepository.getFanTalks(creatorId, page, size, token),reportFanTalk(fanTalkId, reason, token),deleteFanTalk(fanTalkId, token)를 추가했다. 신고는ReportRequest(ReportType.CHEERS, reason, cheersId = fanTalkId), 삭제는PutModifyCheersRequest(cheersId = fanTalkId, isActive = false)로 기존 repository에 위임함을 확인했다../gradlew :app:compileDebugKotlin실행 결과 PASS.
- 2026-06-22:
- 수정:
-
Task 2.3: ViewModel RED 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModelTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkPaginationTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkActionTest.kt
- 테스트 케이스:
- 최초 로딩이
page=0,size=20으로 호출된다. fanTalkCount == 0이면Empty상태가 된다.- 표시 가능한
fanTalks가 없으면Empty상태가 된다. fanTalkCount > 0이지만 첫 페이지fanTalks가 비어 있으면Empty상태가 되고 count 값은 유지된다.hasNext == true일 때 다음 페이지는 마지막 응답의page + 1로 요청한다.- load-more 요청에는
size=20을 유지한다. - loading 중 중복 load-more 요청은 무시된다.
- 다음 페이지 성공 시 기존 FanTalk 뒤에 append한다.
- 다음 페이지 실패 시 기존 목록은 유지하고 pagination error message만 설정한다.
consumePaginationErrorMessage()호출 후 pagination error message가 null이 된다.- 신고 성공 시 toast message가 설정된다.
- 신고 실패 시 기존 content 상태는 유지하고 toast/error message가 설정된다.
- 삭제 성공 시 첫 페이지 refresh가 호출된다.
- 삭제 실패 시 기존 content 상태는 유지하고 toast/error message가 설정된다.
- 최초 로딩이
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkViewModelTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkPaginationTest"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkActionTest"
- 기대 결과:
- production 구현 전
CreatorChannelFanTalkViewModel미구현으로 RED 실패한다.
- production 구현 전
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkViewModelTest.kt,CreatorChannelFanTalkPaginationTest.kt,CreatorChannelFanTalkActionTest.kt를 추가했다. - production 구현 전
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkMapperTest"를 실행했을 때 unit test compile 단계에서CreatorChannelFanTalkViewModel,CreatorChannelFanTalkTabResponse,getFanTalks,reportFanTalk,deleteFanTalk등 미구현 참조로 실패해 RED 상태를 확인했다. - 구현 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 2.4:
CreatorChannelFanTalkViewModel구현- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkViewModel.kt
- 수정:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
DEFAULT_PAGE_SIZE = 20,FIRST_PAGE = 0으로 둔다.loadFanTalks(creatorId: Long, isOwner: Boolean)는 같은creatorId와isOwner로 기존 state가 있으면 중복 최초 조회를 막는다.retryFanTalks()와refreshFanTalks()는 첫 페이지를 다시 조회한다.loadMore()는 content 상태,hasNext,isLoadingMore,creatorId를 확인해 중복 요청을 막는다.requestGeneration으로 오래된 응답이 최신 상태를 덮어쓰지 않게 한다.- 첫 페이지 성공 후
fanTalkCount == 0또는 표시 가능한 item이 0개이면Empty(fanTalkCount)상태로 전환한다. - pagination 실패는 기존 content를 유지하고
paginationErrorMessage에만 반영한다. reportFanTalk(fanTalkId, reason)는 blank reason을 ViewModel에서 호출하지 않는 전제로 두고 repository 결과 message를 toast state로 전달한다.deleteFanTalk(fanTalkId)는 성공 시refreshFanTalks()를 호출한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"
- 기대 결과:
- ViewModel 관련 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkViewModel.kt를 추가하고AppDI.kt에viewModel { CreatorChannelFanTalkViewModel(get(), get()) }binding을 추가했다. DEFAULT_PAGE_SIZE = 20,FIRST_PAGE = 0, 동일 조건 최초 조회 중복 방지,requestGeneration, load-more guard, pagination error 유지/consume, 신고/삭제 action message, 삭제 성공 후 refresh 동작을 테스트로 확인했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"실행 결과 PASS.- 2026-06-22 리뷰 수정: 신고 성공 응답의
message가 null이면character_comment_report_submittedfallback toast message를 사용하도록 수정했다. - RED:
CreatorChannelFanTalkActionTest에ApiResponse(true, Any(), null)케이스를 추가한 뒤./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkActionTest"실행 시expected:<Your report has been submitted.> but was:<null>로 실패함을 확인했다. - GREEN:
SodaLiveApplicationHolder초기화가 포함된 action 테스트와 ViewModel fallback 수정 후./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkActionTest",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*",./gradlew :app:compileDebugKotlin,git diff --check실행 결과 PASS.
- 2026-06-22:
- 생성:
Phase 3: Mapper와 권한별 UI model 추가
-
Task 3.1: FanTalk UI model 추가
- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/model/CreatorChannelFanTalkUiModels.kt
- 작업:
CreatorChannelFanTalkUiModel에는fanTalkId,writerId,writerNickname,writerProfileImageUrl,content,createdAtText,reply,rightAction을 포함한다.CreatorChannelFanTalkReplyUiModel에는fanTalkId,writerId,writerNickname,writerProfileImageUrl,content,createdAtText를 포함한다.CreatorChannelFanTalkRightAction은Report,OwnerMore(showEdit: Boolean, showDelete: Boolean)로 정의한다.- 내가 쓴 글이면
OwnerMore(showEdit = true, showDelete = true)이다. - 내 채널의 타인 글이면
OwnerMore(showEdit = false, showDelete = true)이다. - 내가 쓴 글도 아니고 내 채널도 아니면
Report이다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- UI model 추가 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkUiModels.kt에 원글/답글 UI model과CreatorChannelFanTalkRightAction.Report,OwnerMore(showEdit, showDelete)를 추가했다. - mapper/action 테스트에서 내가 쓴 글은
OwnerMore(showEdit = true, showDelete = true), 내 채널의 타인 글은OwnerMore(showEdit = false, showDelete = true), 타인 채널의 타인 글은Report로 매핑됨을 확인했다. ./gradlew :app:compileDebugKotlin실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 3.2: Mapper RED/GREEN 테스트 작성
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkMapperTest.kt
- 테스트 케이스:
createdAtUtc는UtcRelativeTimeTextFormatter.format()결과로 매핑된다.creatorReplies가 비어 있으면reply == null이다.creatorReplies가 2개 이상이면 첫 번째 reply만 매핑된다.- 내가 쓴 글이면
OwnerMore(showEdit = true, showDelete = true)이다. - 내 채널의 타인 글이면
OwnerMore(showEdit = false, showDelete = true)이다. - 내가 쓴 글도 아니고 내 채널도 아니면
Report이다. - 원글/답글 content가 빈 문자열이어도 item은 유지된다.
- 구현:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/model/CreatorChannelFanTalkMappers.ktList<CreatorChannelFanTalkResponse>.toFanTalkUiModels(relativeTimeTextFormatter, isOwner, currentUserId)를 추가한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkMapperTest"
- 기대 결과:
- Mapper 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkMapperTest.kt를 추가하고 production 구현 전 미구현 참조로 RED 실패함을 확인했다. CreatorChannelFanTalkMappers.kt에List<CreatorChannelFanTalkResponse>.toFanTalkUiModels(relativeTimeTextFormatter, isOwner, currentUserId)를 추가했다.createdAtUtc의UtcRelativeTimeTextFormatter.format()매핑,creatorReplies.firstOrNull()1개 reply 매핑, 권한별 right action, 빈 content 유지 케이스를 테스트로 확인했다../gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"및./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"실행 결과 PASS.
- 2026-06-22:
- 생성:
Phase 4: FanTalk 목록 UI와 popup 구현
-
Task 4.1: Fragment layout과 문자열 추가
- 생성:
app/src/main/res/layout/fragment_creator_channel_fantalk.xml
- 수정:
app/src/main/res/values/strings.xmlapp/src/main/res/values-en/strings.xmlapp/src/main/res/values-ja/strings.xml
- 작업:
- content 상태에서는
전체label과fanTalkCount만 있는 count bar를 배치한다. - sort label/icon/popup 진입 영역은 layout에 넣지 않는다.
- RecyclerView, error message, retry button, empty container, floating write button을 포함한다.
- empty container에는 PRD의 empty 문구와
creator_channel_fantalk_support_actionbutton을 배치한다. - empty 상태에서는 count bar, RecyclerView, floating write button을 숨기는 구조로 둔다.
- 신규 문자열은
creator_channel_fantalk_empty_message,creator_channel_fantalk_error_message,creator_channel_fantalk_retry,creator_channel_fantalk_all_label,creator_channel_fantalk_report,creator_channel_fantalk_edit,creator_channel_fantalk_delete이름으로 추가한다.
- content 상태에서는
- 검증 명령:
./gradlew :app:mergeDebugResources
- 기대 결과:
- 신규 layout/string resource merge가 PASS한다.
- 검증 기록:
- 2026-06-22:
fragment_creator_channel_fantalk.xml을 추가하고 content 상태 count bar, RecyclerView, error/retry, empty container, floating write button을 배치했다. sort label/icon/popup 진입 영역은 추가하지 않았다. - 2026-06-22:
creator_channel_fantalk_empty_message,creator_channel_fantalk_error_message,creator_channel_fantalk_retry,creator_channel_fantalk_all_label,creator_channel_fantalk_report,creator_channel_fantalk_edit,creator_channel_fantalk_delete문자열을 한국어/영어/일본어 리소스에 추가했다. - 2026-06-22:
./gradlew :app:mergeDebugResources실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 4.2: FanTalk item layout과 adapter 추가
- 생성:
app/src/main/res/layout/item_creator_channel_fantalk.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkAdapter.kt
- 작업:
- 원글 profile image는 42dp 원형 수준으로 표시한다.
- 원글 writer nickname, createdAtText, content를 표시한다.
- 우측 action은
OwnerMore이면ic_new_moreImageView,Report이면신고TextView만 표시한다. - reply가 있으면 원글 아래 회색 배경 답글 card를 표시한다.
- reply profile image는 20dp 원형 수준으로 표시한다.
- reply writer nickname, createdAtText, content를 표시한다.
- reply가 없으면 답글 card와 연결선을 숨긴다.
- profile image url이 비어 있거나 실패하면
ic_placeholder_profile을 사용한다. - item click 자체는 동작하지 않는다.
- 검증 명령:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin
- 기대 결과:
- item layout과 adapter 추가 후 resource merge와 Kotlin compile이 PASS한다.
- 검증 기록:
- 2026-06-22:
item_creator_channel_fantalk.xml과CreatorChannelFanTalkAdapter.kt를 추가했다. 원글/답글 프로필, 닉네임, 시간, 본문, 권한별신고/ic_new_more, reply card와 connector visibility를 UI model 기준으로 바인딩한다. - 2026-06-22: profile image는
loadUrl과CircleCropTransformation,ic_placeholder_profileplaceholder/error를 사용하도록 구현했다. - 2026-06-22:
./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 4.3: 더보기 popup 추가
- 생성:
app/src/main/res/layout/view_creator_channel_fantalk_more_popup.xmlapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/ui/CreatorChannelFanTalkMorePopup.kt
- 작업:
showEdit == true이면수정하기를 표시한다.showDelete == true이면삭제하기를 표시한다.수정하기터치 시 popup만 닫고 API/화면 이동은 호출하지 않는다.삭제하기터치 시 popup을 닫고 adapter callback으로fanTalkId를 전달한다.- popup은 anchor view 기준 우측 영역에 표시하되 화면 밖으로 벗어나지 않도록
PopupWindow기본 clipping을 유지한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- popup class 추가 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-22:
view_creator_channel_fantalk_more_popup.xml과CreatorChannelFanTalkMorePopup.kt를 추가했다.showEdit,showDelete에 따라수정하기,삭제하기를 표시하고 수정은 dismiss만, 삭제는 dismiss 후 callback으로fanTalkId를 전달한다. - 2026-06-22:
./gradlew :app:compileDebugKotlin실행 결과 PASS.
- 2026-06-22:
- 생성:
-
Task 4.4: Fragment layout source 테스트 추가
- 생성:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragmentLayoutTest.kt
- 테스트 케이스:
- fragment layout에 sort icon/정렬 label이 없다.
- fragment layout에
전체count TextView가 있다. - fragment layout에 empty 문구와
응원 남기기button이 있다. - fragment layout에 content floating write button이 있다.
- item layout에
ic_new_more와신고TextView가 있다. - item layout에 reply container가 있다.
- popup layout에
수정하기,삭제하기TextView가 있다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkFragmentLayoutTest"
- 기대 결과:
- Layout source 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-22:
CreatorChannelFanTalkFragmentLayoutTest.kt를 추가했다. fragment layout의 sort UI 부재, count/empty/floating write button, item action/reply container, popup 수정/삭제 TextView, adapter/popup source 계약을 검증한다. - 2026-06-22: RED 확인으로 신규 layout/resource 구현 전 테스트가 layout/id 부재로 실패함을 확인했고, 구현 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkFragmentLayoutTest"실행 결과 PASS.
- 2026-06-22:
- 생성:
Phase 5: Fragment 동작, 신고/삭제, empty 상태 연결
-
Task 5.1:
CreatorChannelFanTalkFragment구현- 생성:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt
- 작업:
CreatorChannelFanTalkViewModel을 Koinby viewModel()로 주입한다.RecyclerView는LinearLayoutManager와CreatorChannelFanTalkAdapter를 사용한다.onCreatorChannelFanTalkTabSelected()에서viewModel.loadFanTalks(creatorId, isOwner = host.isCreatorChannelOwner())를 호출한다.onCreatorChannelFanTalkScrolledToBottom()에서viewModel.loadMore()를 호출한다.onCreatorChannelFanTalkRefreshRequested()에서viewModel.refreshFanTalks()를 호출한다.- Loading/Error/Empty/Content 상태별 visibility를 명확히 분기한다.
- Content 상태에서는 count bar, RecyclerView, floating write button을 표시한다.
- Empty 상태에서는 empty container만 표시하고 count bar, RecyclerView, floating write button을 숨긴다.
- floating write button과 empty
응원 남기기button click listener는 비워 두거나 no-op으로 둔다. - 신고 action click 시
CheersReportDialog를 열고 blank reason이면 기존 reason required toast를 표시한다. - 삭제 action click 시 host에
fanTalkId를 전달해 삭제 확인 dialog를 열도록 한다. - pagination error와 action toast message는 Toast로 표시 후 consume한다.
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- Fragment 추가 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-22: TDD RED로
CreatorChannelPagerAdapterTest,CreatorChannelActivitySourceTest,CreatorChannelFanTalkFragmentLayoutTest에 FanTalk Fragment/Host/source 계약 기대값을 먼저 추가했다. production 변경 전./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 시CreatorChannelFanTalkFragmentunresolved reference로 실패해 신규 Fragment와 wiring 누락을 확인했다. - 2026-06-22:
CreatorChannelFanTalkFragment.kt를 추가하고 Koinby viewModel(),LinearLayoutManager,CreatorChannelFanTalkAdapter, Loading/Error/Empty/Content visibility, no-op write buttons, report dialog, owner more popup, delete confirmed entry, pagination/action toast consume, Host callback을 구현했다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkFragmentLayoutTest"실행 결과 PASS.
- 2026-06-22: TDD RED로
- 생성:
-
Task 5.2:
CreatorChannelActivity에 FanTalk Host 연결- 수정:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
- Activity가
CreatorChannelFanTalkFragment.Host를 구현한다. isCreatorChannelOwner()기존 method를 FanTalk Host에서도 재사용한다.- FanTalk 탭 선택 시
findFanTalkFragment()?.onCreatorChannelFanTalkTabSelected()를 호출한다. - 스크롤 하단 도달 시
findFanTalkFragment()?.onCreatorChannelFanTalkScrolledToBottom()를 호출한다. - 새로고침이 필요한 공통 경로에서
findFanTalkFragment()?.onCreatorChannelFanTalkRefreshRequested()를 호출한다. onCreatorChannelFanTalkContentChanged()에서 ViewPager 높이 갱신을 호출한다.onCreatorChannelFanTalkDeleteClicked(fanTalkId)에서SodaDialog를 표시한다.- 삭제 확인 dialog의 confirm에서 Fragment 또는 ViewModel으로 삭제 요청을 다시 전달한다.
- Activity가
- 검증 명령:
./gradlew :app:compileDebugKotlin
- 기대 결과:
- Activity Host 연결 후 컴파일이 PASS한다.
- 검증 기록:
- 2026-06-22:
CreatorChannelActivity가CreatorChannelFanTalkFragment.Host를 구현하도록 추가하고findFanTalkFragment(), 탭 선택/header 변경 dispatch, bottom scroll dispatch, load-more tab 포함, content changed 높이 갱신, 삭제 확인SodaDialog및 confirm 시 Fragment 삭제 entry 호출을 연결했다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 결과 PASS.
- 2026-06-22:
- 수정:
-
Task 5.3:
CreatorChannelPagerAdapter에 FanTalk 탭 연결- 수정:
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.FanTalk -> CreatorChannelFanTalkFragment.newInstance(creatorId)분기를 추가한다.- 기존 테스트를 Home/Live/Audio/Series/Community/FanTalk 실제 Fragment 생성 검증으로 갱신한다.
- FanTalk 외 placeholder 유지 기대값은 유지한다.
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"
- 기대 결과:
- PagerAdapter 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-22:
CreatorChannelTab.FanTalk -> CreatorChannelFanTalkFragment.newInstance(creatorId)분기를 추가하고CreatorChannelPagerAdapterTest에서 FanTalk 실제 Fragment 생성 및 placeholder 제외 기대값을 갱신했다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest"실행 결과 PASS.
- 2026-06-22:
- 수정:
-
Task 5.4: Activity source 테스트 갱신
- 수정:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 작업:
- 기존
assertFalse(source.contains("CreatorChannelTab.FanTalk ->"))기대값을 제거하거나 FanTalk 연결 검증으로 변경한다. CreatorChannelFanTalkFragment.Host구현을 검증한다.findFanTalkFragment()?.onCreatorChannelFanTalkTabSelected()호출을 검증한다.findFanTalkFragment()?.onCreatorChannelFanTalkScrolledToBottom()호출을 검증한다.onCreatorChannelFanTalkContentChanged()구현을 검증한다.- 신고/삭제 dialog 관련 source 검증은 과도한 문자열 고정 대신 주요 class/method 호출만 검증한다.
- 기존
- 검증 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"
- 기대 결과:
- Activity source 테스트가 GREEN이다.
- 검증 기록:
- 2026-06-22:
CreatorChannelActivitySourceTest의 FanTalk 분기 부재 기대값을 제거하고 Pager/Activity/Fragment source 계약 검증으로 교체했다. Host 구현,findFanTalkFragment(), 탭 선택/header selected dispatch, scrolled bottom, load more tab 포함, content changed callback, delete clicked dialog, delete confirmed callback을 검증한다. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"실행 결과 PASS.
- 2026-06-22:
- 수정:
Phase 6: 통합 검증과 회귀 확인
-
Task 6.1: FanTalk 단위 테스트 실행
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"
- 기대 결과:
- FanTalk mapper/ViewModel/pagination/action/layout 테스트가 모두 PASS한다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"실행 결과 PASS.
- 2026-06-22:
- 실행:
-
Task 6.2: Creator Channel 관련 회귀 테스트 실행
- 실행:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"
- 기대 결과:
- FanTalk 연결로 인해 Home/Live/Audio/Series/Community 기존 테스트가 깨지지 않는다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"실행 결과 PASS. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"최초 실행 시CreatorChannelHomeViewModelTest > 채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다1건이CreatorChannelHomeViewModelTest.kt:285에서 실패했다. - 2026-06-22: 동일 테스트 단독 재실행
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest.채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다"결과 PASS. - 2026-06-22:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"재실행 결과 PASS.
- 2026-06-22:
- 실행:
-
Task 6.3: Resource/Kotlin/Lint 검증 실행
- 실행:
./gradlew :app:mergeDebugResources./gradlew :app:compileDebugKotlin./gradlew :app:ktlintCheckgit diff --check
- 기대 결과:
- resource merge, Kotlin compile, ktlint, whitespace check가 모두 PASS한다.
- 검증 기록:
- 2026-06-22:
./gradlew :app:mergeDebugResources실행 결과 PASS. - 2026-06-22:
./gradlew :app:compileDebugKotlin실행 결과 PASS. - 2026-06-22:
./gradlew :app:ktlintCheck실행 결과 PASS. - 2026-06-22:
git diff --check실행 결과 출력 없이 PASS.
- 2026-06-22:
- 실행:
-
Task 6.4: 수동 화면 검증
- 확인:
- content 상태에서 Sort-bar에는
전체와 count만 보이고 정렬 UI가 없다. - 내가 쓴 글에는
ic_new_more가 보이고 popup에수정하기,삭제하기가 보인다. - 내 채널의 타인 글에는
ic_new_more가 보이고 popup에삭제하기만 보인다. - 타인 채널의 타인 글에는
신고만 보인다. 수정하기를 눌러도 화면 이동/API 호출이 없다.- 삭제 confirm을 취소하면 API 호출이 없다.
- 삭제 confirm을 확인하면 삭제 API 호출 후 목록이 갱신된다.
- 신고 reason 미선택 시
screen_user_profile_fantalk_report_reason_requiredtoast가 표시된다. - 신고 reason 선택 후 report API가 호출된다.
creatorReplies가 여러 개 내려와도 첫 번째 1개만 표시된다.- empty 상태에서 Sort-bar와 우측 하단 floating 버튼이 숨겨지고 중앙 empty 문구와
응원 남기기button이 보인다. - content 상태에서 우측 하단 floating 버튼이 보이지만 클릭해도 화면 이동/API 호출이 없다.
- 목록 하단 스크롤 시
hasNext == true일 때만 다음 page가 로딩된다.
- content 상태에서 Sort-bar에는
- 검증 기록:
- 2026-06-22: 현재 실행 환경에서는 Android 앱 화면을 기기/에뮬레이터로 띄운 실기 수동 검증을 수행하지 못했다.
- 2026-06-22: Phase 4, 5에서 Figma
290:9139,290:9000기준 화면 구조와 source/layout 테스트를 대조했고, Phase 6에서는 관련 unit/source/layout/resource/Kotlin/ktlint/whitespace 검증이 PASS함을 확인했다. - 2026-06-22: 실기/에뮬레이터 수동 QA가 가능해지면 위 체크리스트의 권한별 action, 신고/삭제 dialog/API, empty/content/pagination 동작을 실제 화면에서 추가 확인해야 한다.
- 확인:
Verification Log
- 문서 작성 시점에는 구현을 수행하지 않았으므로 빌드/테스트 검증 기록이 없다.
- 2026-06-22: Phase 2, 3 코드 리뷰를 수행했다. API/DTO/Repository/ViewModel/mapper/action model/test 변경이 PRD와 계획 문서의 계약 범위 안에 있으며, 레거시 파일 직접 수정 없이 기존 repository/dialog 계약을 호출하는 구조임을 확인했다. blocking finding은 발견하지 못했다.
- 2026-06-22: Phase 2, 3 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*",./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --check,git diff --cached --check를 실행했고 모두 PASS했다. - 2026-06-22: Phase 4, 5 코드 리뷰를 수행했다. Figma
290:9139,290:9000기준으로 count bar, sort UI 부재, 원글/답글 item, 권한별 action, more popup, floating/empty write button, empty 문구를 구현과 대조했고 blocking finding은 발견하지 못했다. - 2026-06-22: Phase 4, 5 재검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkFragmentLayoutTest",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelPagerAdapterTest",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*",git diff --check를 실행했고 모두 PASS했다. 최초mergeDebugResources는 Gradle wrapper lock 파일 샌드박스 접근 제한으로 실패했으며, 동일 명령을 승인 실행해 PASS를 확인했다. - 2026-06-22: Phase 6 통합 검증으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*",./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*",./gradlew :app:mergeDebugResources,./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,git diff --check를 실행했다. 전체 Creator Channel 테스트 최초 실행에서CreatorChannelHomeViewModelTest > 채널 후원 성공은 기존 후원 API를 호출하고 홈을 다시 로드한다1건이 일시 실패했으나, 해당 테스트 단독 재실행과 전체 Creator Channel 테스트 재실행은 모두 PASS했다.