386 lines
28 KiB
Markdown
386 lines
28 KiB
Markdown
# FanTalk 글 입력 구현 계획/TASK
|
|
|
|
> **For agentic workers:** 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
|
|
|
|
**Goal:** 크리에이터 채널 `FanTalk` 탭에서 고정형 plus button 또는 empty `응원 남기기` 버튼으로 글 입력 화면에 진입하고, 기존 FanTalk 등록 API로 응원글을 등록한다.
|
|
|
|
**Architecture:** 기존 `CreatorChannelFanTalkFragment`의 빈 글쓰기 click callback을 Host로 올리고, `CreatorChannelActivity`에서 신규 `CreatorChannelFanTalkWriteActivity`를 ActivityResult로 실행한다. 기존 Fragment 내부 floating button은 Activity overlay fixed button으로 대체해 스크롤과 분리하고, 등록 API는 `CreatorChannelRepository.writeFanTalk()` wrapper가 레거시 `ExplorerRepository.writeCheers()`를 호출한다.
|
|
|
|
**Tech Stack:** Kotlin, Android XML Views, ViewBinding, Activity Result API, RxJava3, Koin, JUnit4/Robolectric.
|
|
|
|
---
|
|
|
|
## 전제와 성공 기준
|
|
|
|
- PRD: `docs/20260708_FanTalk_글_입력/prd.md`
|
|
- 기존 FanTalk 탭 문서:
|
|
- `docs/20260622_FanTalk_탭/prd.md`
|
|
- `docs/20260622_FanTalk_탭/plan-task.md`
|
|
- 기존 FanTalk 탭 구현:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt`
|
|
- `app/src/main/res/layout/fragment_creator_channel_fantalk.xml`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- 기존 FanTalk 등록 API:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerRepository.kt`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/explorer/ExplorerApi.kt`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/cheers/PostWriteCheersRequest.kt`
|
|
- 기존 v2 repository:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
|
- fixed plus button은 `CreatorChannelActivity` layout overlay에 배치한다.
|
|
- fixed plus button icon은 `ic_plus_no_bg`를 사용하고 white tint를 적용한다.
|
|
- empty 상태의 `응원 남기기` 버튼은 Fragment 내부에 유지하고 글 입력 화면으로 연결한다.
|
|
- 글 등록 성공 후 `refreshCreatorChannelFanTalk()`와 `homeActionDelegate?.refreshHome()`를 호출한다.
|
|
- 구현 완료 판단 시 Gradle 검증뿐 아니라 Figma `290:9235`, `290:9282`, `290:9290`, `308:19046`의 주요 layout 구조, 버튼 상태, popup 문구가 실제 구현 화면에 반영되었는지 확인한다.
|
|
- 자동 검증에서도 Figma에서 확정된 UI 계약을 확인한다.
|
|
- 입력값 없음: 보내기 버튼 disabled
|
|
- 입력값 있음: 보내기 버튼 enabled
|
|
- 입력 종료 popup: title/desc/cancel/confirm 문구 일치
|
|
- fixed plus button: `ic_plus_no_bg`, white tint, Activity overlay 고정 배치
|
|
- Figma 대조 결과는 문서 최하단 `Verification Log`에 한국어로 누적 기록한다.
|
|
- 구현 완료 후 최소 다음 명령을 실행한다.
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"`
|
|
- `./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:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- `./gradlew :app:ktlintCheck`
|
|
- `git diff --check`
|
|
|
|
---
|
|
|
|
## Figma 참조
|
|
|
|
- 입력 값 없음 + keyboard 있음: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9235&m=dev`
|
|
- 입력 값 있음 + keyboard 있음: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9282&m=dev`
|
|
- 입력 값 있음 + keyboard 없음: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9290&m=dev`
|
|
- 입력 종료 modal: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=308-19046&m=dev`
|
|
- 입력 종료 modal 문구:
|
|
- title: `입력 종료`
|
|
- desc: `입력을 종료할까요?\n지금 나가면 입력한 내용이 저장되지 않아요.`
|
|
- cancel button: `계속 작성`
|
|
- confirm button: `종료`
|
|
|
|
---
|
|
|
|
## 파일 구조
|
|
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
|
- `writeFanTalk(creatorId, content, token)` wrapper를 추가한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt`
|
|
- 기존 빈 글쓰기 click listener를 Host callback으로 교체한다.
|
|
- content 상태에서 Activity fixed plus button 표시 여부를 Host에 알린다.
|
|
- Fragment 내부 `btn_creator_channel_fantalk_write`는 더 이상 floating 진입점으로 사용하지 않는다.
|
|
- 수정: `app/src/main/res/layout/fragment_creator_channel_fantalk.xml`
|
|
- Fragment 내부 `btn_creator_channel_fantalk_write`를 제거하거나 항상 `gone` 처리한다.
|
|
- empty `응원 남기기` 버튼은 유지한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- `CreatorChannelFanTalkWriteActivity` ActivityResult launcher를 추가한다.
|
|
- FanTalk fixed plus button 표시/숨김과 click 처리를 담당한다.
|
|
- 등록 성공 시 FanTalk 목록과 홈 요약을 refresh한다.
|
|
- 수정: `app/src/main/res/layout/activity_creator_channel.xml`
|
|
- FanTalk fixed plus button을 root overlay 우측 하단에 추가한다.
|
|
- icon은 `@drawable/ic_plus_no_bg`, tint는 `@color/white`를 사용한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt`
|
|
- Figma 기반 입력 화면, keyboard show/hide, 취소 popup, 등록 성공 result를 담당한다.
|
|
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteViewModel.kt`
|
|
- 입력값 상태, 보내기 버튼 enabled 상태, 등록 loading/result/error 상태를 관리한다.
|
|
- 생성: `app/src/main/res/layout/activity_creator_channel_fantalk_write.xml`
|
|
- 입력 화면 layout을 구현한다.
|
|
- 생성 가능: `app/src/main/res/drawable/bg_creator_channel_fantalk_write_send_enabled.xml`
|
|
- 기존 drawable로 대응되지 않을 때만 enabled 보내기 버튼 배경을 추가한다.
|
|
- 생성 가능: `app/src/main/res/drawable/bg_creator_channel_fantalk_write_send_disabled.xml`
|
|
- 기존 drawable로 대응되지 않을 때만 disabled 보내기 버튼 배경을 추가한다.
|
|
- 수정: `app/src/main/res/values/strings.xml`
|
|
- FanTalk 글 입력 title/hint/send/cancel/popup/오류 문구를 추가하거나 기존 문자열을 재사용한다.
|
|
- 수정: `app/src/main/res/values-en/strings.xml`, `app/src/main/res/values-ja/strings.xml`
|
|
- 신규 문자열의 다국어 값을 추가한다.
|
|
- 수정: `app/src/main/AndroidManifest.xml`
|
|
- 신규 `CreatorChannelFanTalkWriteActivity`를 등록한다.
|
|
- 수정: `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
|
- `CreatorChannelFanTalkWriteViewModel` binding을 추가한다.
|
|
- 테스트 생성:
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteViewModelTest.kt`
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteSourceTest.kt`
|
|
- 테스트 수정:
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragmentLayoutTest.kt`
|
|
|
|
---
|
|
|
|
### Phase 1: 기존 구조 확인과 fixed button 경계 고정
|
|
|
|
- [x] **Task 1.1: 기존 FanTalk 글쓰기 진입점 상태 확인**
|
|
- 확인:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt`
|
|
- `app/src/main/res/layout/fragment_creator_channel_fantalk.xml`
|
|
- 작업:
|
|
- `btnCreatorChannelFantalkWrite.setOnClickListener { }`와 `layoutCreatorChannelFantalkEmptyWriteButton.setOnClickListener { }`가 빈 동작임을 확인한다.
|
|
- Fragment 내부 `btn_creator_channel_fantalk_write`가 Fragment root bottom에 묶여 있어 긴 콘텐츠에서 화면 우측 하단 고정 요구사항을 만족하지 못하는지 확인한다.
|
|
- 검증:
|
|
- `rg -n "btnCreatorChannelFantalkWrite|layoutCreatorChannelFantalkEmptyWriteButton|btn_creator_channel_fantalk_write" app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk app/src/main/res/layout/fragment_creator_channel_fantalk.xml`
|
|
- 기대 결과: 빈 click listener와 Fragment 내부 floating view 위치가 확인된다.
|
|
|
|
- [x] **Task 1.2: Activity overlay fixed button 배치 지점 확인**
|
|
- 확인:
|
|
- `app/src/main/res/layout/activity_creator_channel.xml`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- 작업:
|
|
- donation floating button, owner FAB와 겹치지 않는 FanTalk fixed plus button 배치 지점을 정한다.
|
|
- current tab이 `CreatorChannelTab.FanTalk`이고 FanTalk content 상태일 때만 표시되도록 Activity 상태 변수를 둔다.
|
|
- 검증:
|
|
- `rg -n "btnCreatorChannelDonationWrite|owner_fab_button|updateDonationFloatingButtonVisibility|CreatorChannelTab.FanTalk" app/src/main/res/layout/activity_creator_channel.xml app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- 기대 결과: Activity overlay에 fixed button을 추가할 위치와 visibility 갱신 지점이 확인된다.
|
|
|
|
---
|
|
|
|
### Phase 2: FanTalk 등록 API wrapper와 ViewModel 계약
|
|
|
|
- [x] **Task 2.1: Repository wrapper 추가**
|
|
- 수정:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/data/CreatorChannelRepository.kt`
|
|
- 작업:
|
|
- `writeFanTalk(creatorId: Long, content: String, token: String)` method를 추가한다.
|
|
- 내부에서는 `explorerRepository.writeCheers(parentCheersId = null, creatorId = creatorId, content = content, token = token)`를 호출한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- 기존 레거시 파일 수정 없이 v2 repository에서 FanTalk 등록 API를 호출할 수 있다.
|
|
|
|
- [x] **Task 2.2: FanTalk 글 입력 ViewModel RED 테스트 작성**
|
|
- 생성:
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteViewModelTest.kt`
|
|
- 테스트 케이스:
|
|
- 초기 입력값은 빈 값이고 보내기 버튼은 disabled다.
|
|
- 공백과 줄바꿈만 입력하면 보내기 버튼은 disabled다.
|
|
- trim 결과가 비어 있지 않으면 보내기 버튼은 enabled다.
|
|
- disabled 상태에서 submit하면 repository를 호출하지 않는다.
|
|
- enabled 상태에서 submit하면 `CreatorChannelRepository.writeFanTalk(creatorId, trimmedContent, "Bearer $token")`을 호출한다.
|
|
- 등록 중에는 중복 submit을 무시한다.
|
|
- 등록 성공 시 완료 event를 emit한다.
|
|
- 등록 실패 시 입력값을 유지하고 toast message를 emit한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteViewModelTest"`
|
|
- 기대 결과:
|
|
- ViewModel 구현 전 실패한다.
|
|
|
|
- [x] **Task 2.3: FanTalk 글 입력 ViewModel 구현**
|
|
- 생성:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteViewModel.kt`
|
|
- 작업:
|
|
- `contentLiveData`, `isSendEnabledLiveData`, `isLoadingLiveData`, 완료 event, toast event를 제공한다.
|
|
- `onContentChanged(content)`에서 `content.trim().isNotEmpty()` 기준으로 보내기 enabled 상태를 갱신한다.
|
|
- `submit(creatorId)`에서 disabled/loading/invalid creatorId 상태를 방어한다.
|
|
- 성공 시 완료 event를 emit하고, 실패 시 toast event를 emit한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteViewModelTest"`
|
|
- 기대 결과:
|
|
- 신규 ViewModel 테스트가 PASS한다.
|
|
|
|
---
|
|
|
|
### Phase 3: 글 입력 Activity와 Figma UI 구현
|
|
|
|
- [x] **Task 3.1: 글 입력 Activity layout 추가**
|
|
- 생성:
|
|
- `app/src/main/res/layout/activity_creator_channel_fantalk_write.xml`
|
|
- 작업:
|
|
- Figma `290:9235`, `290:9282`, `290:9290` 기준으로 title/cancel/input/send 영역을 구성한다.
|
|
- 입력값 없음 상태의 보내기 버튼 disabled style과 입력값 있음 상태의 enabled style을 분리한다.
|
|
- keyboard가 내려간 상태에서도 입력 영역과 하단 action이 겹치지 않게 구성한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- 기대 결과:
|
|
- 신규 layout과 drawable/string resource가 resource merge를 통과한다.
|
|
|
|
- [x] **Task 3.2: 글 입력 Activity 구현**
|
|
- 생성:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt`
|
|
- 수정:
|
|
- `app/src/main/AndroidManifest.xml`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt`
|
|
- 작업:
|
|
- `newIntent(context, creatorId)`를 제공한다.
|
|
- 진입 시 `creatorId`가 없으면 `finish()`한다.
|
|
- `EditText`에 focus를 주고 keyboard를 표시한다.
|
|
- text change를 ViewModel에 전달한다.
|
|
- 보내기 버튼 enabled/loading 상태를 ViewModel state로 반영한다.
|
|
- 등록 성공 event 수신 시 `setResult(RESULT_OK)` 후 `finish()`한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- 신규 Activity, manifest, DI 구성이 컴파일된다.
|
|
|
|
- [x] **Task 3.3: 취소 popup과 back 동작 구현**
|
|
- 수정:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt`
|
|
- `app/src/main/res/values/strings.xml`
|
|
- `app/src/main/res/values-en/strings.xml`
|
|
- `app/src/main/res/values-ja/strings.xml`
|
|
- 작업:
|
|
- 입력값이 없으면 좌측 상단 `취소` 버튼 터치 시 즉시 종료한다.
|
|
- 입력값이 있으면 좌측 상단 `취소` 버튼 터치 시 Figma `308:19046` 기준 `SodaDialog`를 표시한다.
|
|
- popup title은 `입력 종료`로 추가한다.
|
|
- popup desc는 `입력을 종료할까요?\n지금 나가면 입력한 내용이 저장되지 않아요.`로 추가한다.
|
|
- popup cancel button label은 `계속 작성`으로 추가한다.
|
|
- popup confirm button label은 `종료`로 추가한다.
|
|
- popup 종료 확정 시 `RESULT_OK`를 설정하지 않고 `finish()`한다.
|
|
- popup 유지 선택 시 dialog만 닫는다.
|
|
- Android back 동작도 좌측 상단 `취소` 버튼과 동일한 입력값 기준을 따른다.
|
|
- 입력값이 있으면 Android back 실행 시 입력 종료 popup을 표시한다.
|
|
- 입력값이 없으면 Android back 실행 시 popup 없이 현재 화면을 종료한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- 취소 popup 문자열, 좌측 상단 `취소` 버튼 조건, Android back popup 조건이 resource merge 및 Kotlin compile을 통과한다.
|
|
|
|
---
|
|
|
|
### Phase 4: FanTalk 탭 진입점 연결과 fixed plus button 수정
|
|
|
|
- [x] **Task 4.1: Fragment Host callback 추가**
|
|
- 수정:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragment.kt`
|
|
- `app/src/main/res/layout/fragment_creator_channel_fantalk.xml`
|
|
- 작업:
|
|
- Host에 `onCreatorChannelFanTalkWriteClicked()`를 추가한다.
|
|
- Host에 `onCreatorChannelFanTalkFloatingButtonVisibilityChanged(isVisible: Boolean)`를 추가한다.
|
|
- empty `응원 남기기` 버튼 click은 `onCreatorChannelFanTalkWriteClicked()`를 호출한다.
|
|
- content 상태에서는 Activity fixed plus button이 보이도록 `true`를 전달한다.
|
|
- loading/empty/error 상태와 `onDestroyView()`에서는 `false`를 전달한다.
|
|
- Fragment 내부 `btn_creator_channel_fantalk_write`는 제거하거나 `gone`으로 고정한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- Fragment가 직접 floating button을 소유하지 않고 Host에 표시 상태와 진입 action을 위임한다.
|
|
|
|
- [x] **Task 4.2: Activity fixed plus button 추가**
|
|
- 수정:
|
|
- `app/src/main/res/layout/activity_creator_channel.xml`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- 작업:
|
|
- root overlay 우측 하단에 FanTalk fixed plus button을 추가한다.
|
|
- button icon은 `@drawable/ic_plus_no_bg`를 사용한다.
|
|
- icon tint는 `@color/white`로 지정한다.
|
|
- current tab이 FanTalk이고 Fragment가 content 상태 표시를 요청한 경우에만 visible 처리한다.
|
|
- bottom inset 또는 기존 bottom margin 정책을 적용해 navigation bar와 겹치지 않게 한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- plus button이 목록 스크롤과 분리된 Activity overlay에 존재하고, icon이 white로 표시되도록 resource가 구성된다.
|
|
|
|
- [x] **Task 4.3: ActivityResult 기반 글 입력 화면 연결**
|
|
- 수정:
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
|
- 작업:
|
|
- `CreatorChannelFanTalkWriteActivity` launcher를 추가한다.
|
|
- FanTalk fixed plus button click과 empty `응원 남기기` Host callback에서 동일한 `openFanTalkWrite()`를 호출한다.
|
|
- 로그인 토큰이 비어 있으면 기존 `showLoginActivity()`를 호출하고 글 입력 화면을 열지 않는다.
|
|
- 등록 성공 result 수신 시 `refreshCreatorChannelFanTalk()`와 `homeActionDelegate?.refreshHome()`를 호출한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- 두 진입점 모두 동일한 글 입력 화면으로 이동하고 성공 후 목록/홈 요약 refresh가 연결된다.
|
|
|
|
---
|
|
|
|
### Phase 5: Source/단위 테스트와 회귀 검증
|
|
|
|
- [x] **Task 5.1: fixed plus button/진입점 source test 추가**
|
|
- 수정:
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/CreatorChannelFanTalkFragmentLayoutTest.kt`
|
|
- 작업:
|
|
- `activity_creator_channel.xml`에 FanTalk fixed plus button과 `@drawable/ic_plus_no_bg`, `@color/white` tint가 있는지 확인한다.
|
|
- FanTalk fixed plus button이 `activity_creator_channel.xml` root overlay에 있고 `fragment_creator_channel_fantalk.xml`의 RecyclerView/content height에 묶이지 않는지 확인한다.
|
|
- `fragment_creator_channel_fantalk.xml`의 empty `응원 남기기` 버튼은 유지되고 Fragment 내부 floating button은 사용되지 않는지 확인한다.
|
|
- `CreatorChannelActivity`에 ActivityResult launcher와 등록 성공 refresh 호출이 있는지 확인한다.
|
|
- `CreatorChannelFanTalkFragment.Host`에 글쓰기 click/visibility callback이 있는지 확인한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"`
|
|
- 기대 결과:
|
|
- Activity overlay fixed plus button과 글 입력 진입점 연결 source test가 PASS한다.
|
|
|
|
- [x] **Task 5.2: 글 입력 Activity source test 추가**
|
|
- 생성:
|
|
- `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteSourceTest.kt`
|
|
- 작업:
|
|
- `CreatorChannelFanTalkWriteActivity`가 `newIntent(context, creatorId)`를 제공하는지 확인한다.
|
|
- 좌측 상단 `취소` 버튼은 입력값이 있을 때 popup을 표시하고, 입력값이 없을 때 즉시 종료하는 경로가 있는지 확인한다.
|
|
- Android back 동작도 입력값이 있을 때 popup을 표시하고, 입력값이 없을 때 즉시 종료하는 경로가 있는지 확인한다.
|
|
- 입력 종료 popup 문자열 resource가 title `입력 종료`, desc `입력을 종료할까요?\n지금 나가면 입력한 내용이 저장되지 않아요.`, cancel `계속 작성`, confirm `종료` 값을 갖는지 확인한다.
|
|
- 글 입력 layout에 입력 영역, 좌측 상단 `취소`, 보내기 버튼이 있고 ViewModel의 enabled/disabled 상태를 반영하는 binding 경로가 있는지 확인한다.
|
|
- 등록 성공 시 `setResult(RESULT_OK)` 후 종료하는지 확인한다.
|
|
- `AndroidManifest.xml`에 신규 Activity가 등록되어 있는지 확인한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"`
|
|
- 기대 결과:
|
|
- 글 입력 Activity의 핵심 연결 계약 source test가 PASS한다.
|
|
|
|
- [x] **Task 5.3: 전체 검증 실행**
|
|
- 실행:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"`
|
|
- `./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:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- `./gradlew :app:ktlintCheck`
|
|
- `git diff --check`
|
|
- Figma 기반 자동 확인:
|
|
- `CreatorChannelFanTalkWriteViewModelTest`에서 입력값 없음/공백 입력은 보내기 disabled, 입력값 있음은 보내기 enabled임을 확인한다.
|
|
- `CreatorChannelFanTalkWriteSourceTest`에서 입력 종료 popup 문구가 Figma `308:19046` 확정 문구와 일치하는지 확인한다.
|
|
- `CreatorChannelActivitySourceTest`에서 fixed plus button이 Activity overlay에 있고 `ic_plus_no_bg`와 white tint를 사용하는지 확인한다.
|
|
- `CreatorChannelFanTalkFragmentLayoutTest`에서 Fragment 내부 floating button이 스크롤 콘텐츠에 묶인 진입점으로 남아 있지 않은지 확인한다.
|
|
- Figma 수동 확인:
|
|
- 입력값 없음 + keyboard 있음 화면이 Figma `290:9235`의 주요 layout 구조와 버튼 disabled 상태를 반영하는지 확인한다.
|
|
- 입력값 있음 + keyboard 있음 화면이 Figma `290:9282`의 주요 layout 구조와 버튼 enabled 상태를 반영하는지 확인한다.
|
|
- 입력값 있음 + keyboard 없음 화면이 Figma `290:9290`의 주요 layout 구조와 버튼 enabled 상태를 반영하는지 확인한다.
|
|
- 입력 종료 popup이 Figma `308:19046`의 문구와 버튼 구성을 반영하는지 확인한다.
|
|
- FanTalk 탭 fixed plus button이 스크롤과 관계없이 viewport 우측 하단에 고정되고, `ic_plus_no_bg` icon이 white로 보이는지 확인한다.
|
|
- 기대 결과:
|
|
- 모든 명령이 PASS하고, Figma 기반 자동 확인과 Figma 4개 상태 화면 및 fixed plus button 수동 대조 결과가 PASS한다.
|
|
- 검증 기록:
|
|
- Figma 기반 자동 확인 결과와 수동 대조를 수행한 화면 상태, 기기/해상도, PASS/FAIL 결과를 `Verification Log`에 누적한다.
|
|
|
|
### Phase 6: Figma 290:9290 글 입력 UI 보정
|
|
|
|
- [x] **Task 6.1: title 제거와 하단 글자수 UI 추가**
|
|
- 수정:
|
|
- `app/src/main/res/layout/activity_creator_channel_fantalk_write.xml`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteActivity.kt`
|
|
- `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/fantalk/write/CreatorChannelFanTalkWriteViewModel.kt`
|
|
- 작업:
|
|
- Figma `290:9290` 기준으로 `tv_creator_channel_fantalk_write_title`을 제거한다.
|
|
- 보내기 버튼 enabled 상태는 `soda_400` 배경과 white 글자색을 사용한다.
|
|
- 하단 fixed info bar에 현재 글자수와 `/500자`를 표시한다.
|
|
- keyboard가 올라오면 info bar가 함께 올라오도록 Activity layout bottom에 고정한다.
|
|
- 입력값은 500자를 넘을 수 없게 제한한다.
|
|
- 검증 명령:
|
|
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"`
|
|
- `./gradlew :app:mergeDebugResources`
|
|
- `./gradlew :app:compileDebugKotlin`
|
|
- 기대 결과:
|
|
- title 없는 Figma 구조, 글자수 UI, 500자 제한 계약이 테스트/리소스/컴파일을 통과한다.
|
|
|
|
---
|
|
|
|
## Verification Log
|
|
|
|
- 구현 전 문서 작성 단계에서는 Gradle 검증을 실행하지 않았다.
|
|
|
|
- 2026-07-08 구현 중 TDD 기록: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.CreatorChannelFanTalkWriteViewModelTest"`를 RED 테스트 추가 직후 실행했으나 첫 실행은 120초 제한, 두 번째 실행은 300초 제한에서 `compileDebugKotlin` 단계 중 시간 초과되어 실패 출력을 끝까지 확보하지 못했다. 이후 구현 후 동일 계약을 포함한 write 테스트 묶음에서 GREEN을 확인했다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"` 최초 실행은 테스트 문자열 escape와 `ApiResponse` 제네릭 불일치로 실패했고, 수정 후 재실행 결과 PASS했다. 이 테스트에서 입력값 없음/공백 disabled, 입력값 있음 enabled, trim submit, 중복 submit 무시, 성공 finish, 실패 시 입력 유지 및 서버 message toast, popup 문구, 신규 Activity/Manifest 계약을 확인했다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.*"` PASS. FanTalk Fragment layout/source 계약과 기존 FanTalk 단위 테스트 회귀를 확인했다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*FanTalk*"` PASS. Activity overlay fixed plus, `ic_plus_no_bg`, white tint, ActivityResult refresh, Fragment Host callback source 계약을 확인했다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:mergeDebugResources` PASS. 신규 write layout/drawable/string/manifest resource 병합을 확인했다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:compileDebugKotlin` PASS. 기존 deprecation/unchecked warning만 출력되었고 신규 컴파일 오류는 없었다.
|
|
- 2026-07-08 구현 검증: `./gradlew :app:ktlintCheck` PASS. 기존 `.editorconfig`의 `disabled_rules` deprecation 경고만 출력되었다.
|
|
- 2026-07-08 구현 검증: `git diff --check` PASS. trailing whitespace 및 patch whitespace 오류가 없음을 확인했다.
|
|
- 2026-07-08 Figma 계약 대조: 자동 테스트와 소스 확인으로 fixed plus가 Activity root overlay에 있고 `ic_plus_no_bg` + `@color/white` tint를 사용함을 확인했다. write 화면은 입력값 없음/있음에 따른 send disabled/enabled와 입력 종료 popup의 PRD 확정 문구를 테스트로 확인했다. 실제 기기/에뮬레이터 수동 실행은 이번 환경에서 수행하지 못했다.
|
|
|
|
- 2026-07-08 후속 수정: Figma `290:9290` 대조 결과 상단 title이 없어야 하고 하단 `현재/500자` info bar가 필요함을 확인했다. 실제 기기/에뮬레이터 수동 실행은 환경 제약으로 별도 수행이 필요하다.
|
|
- 2026-07-08 후속 수정 검증: write 테스트 첫 실행은 source test 문자열 escape 오류로 `compileDebugUnitTestKotlin` 단계에서 실패했다. escape와 enabled 색상 계약을 수정 후 재검증한다.
|
|
- 2026-07-08 후속 수정 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.write.*"` PASS. title 제거, 하단 글자수 UI, 500자 제한, enabled 버튼 `soda_400` 배경/white 글자색 계약을 테스트로 확인했다.
|
|
- 2026-07-08 후속 수정 검증: `./gradlew :app:mergeDebugResources` PASS, `./gradlew :app:compileDebugKotlin` PASS, `git diff --check` PASS.
|