feat(creator): 차단 성공 후 채널에서 이탈한다
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# 크리에이터 채널 차단 후 이탈 구현 계획/TASK
|
||||
|
||||
> **For agentic workers:** 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
|
||||
|
||||
**Goal:** 차단 성공 후 사용자가 현재 크리에이터 채널에 머물지 않고 이전 화면 또는 `MainV2Activity` 홈 fallback으로 이동한다.
|
||||
|
||||
**Architecture:** `CreatorChannelHomeViewModel`은 기존 차단 API 성공 흐름에서 one-shot 성공 이벤트를 추가로 emit한다. `CreatorChannelHomeFragment`는 이벤트를 consume해 `Host` callback으로 넘기고, `CreatorChannelActivity`는 `isTaskRoot` 여부에 따라 `finish()` 또는 `MainV2Activity` fallback 후 `finish()`를 수행한다.
|
||||
|
||||
**Tech Stack:** Kotlin, AndroidX LiveData, RxJava3, JUnit4 local unit test, source contract test.
|
||||
|
||||
---
|
||||
|
||||
## 전제와 성공 기준
|
||||
|
||||
- PRD: `docs/20260814_크리에이터_채널_차단_후_이탈/prd.md`
|
||||
- 구현은 차단 API 성공 후에만 화면 이탈을 수행한다.
|
||||
- 기존 차단 확인 모달과 성공/실패 toast는 유지한다.
|
||||
- 홈 fallback Activity는 `kr.co.vividnext.sodalive.v2.main.MainV2Activity`다.
|
||||
- 테스트 범위는 `app/src/test` local unit/source contract test로 한정한다.
|
||||
- 사용자가 명시적으로 요청하지 않았으므로 `app/src/androidTest`, `connectedDebugAndroidTest`, 기기·에뮬레이터 조작, 스크린샷 검증은 포함하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조
|
||||
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.kt`
|
||||
- 차단 성공 one-shot event를 제공한다.
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt`
|
||||
- 차단 성공 event를 observe/consume하고 host callback으로 전달한다.
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||
- host callback을 구현하고 `isTaskRoot` fallback을 처리한다.
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModelTest.kt`
|
||||
- 차단 성공/실패 event contract를 검증한다.
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||
- Fragment/Activity wiring과 `MainV2Activity` fallback source contract를 검증한다.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 차단 성공 후 채널 이탈
|
||||
|
||||
**Phase 결과:** 사용자가 크리에이터 채널에서 현재 크리에이터 차단에 성공하면 현재 채널에서 빠져나가고, 이전 화면이 없으면 `MainV2Activity`로 이동한다.
|
||||
|
||||
**선행조건:** `docs/20260814_크리에이터_채널_차단_후_이탈/prd.md` 확정.
|
||||
|
||||
**Phase 완료 조건:** `P1-T1`, `P1-T2`, `P1-GATE` 완료와 검증 기록 누적.
|
||||
|
||||
### 구현 항목
|
||||
|
||||
#### Task 1.1 차단 성공 이벤트 추가
|
||||
|
||||
**Goal 실행 `P1-T1`:** `CreatorChannelHomeViewModel.blockUser()`가 API 성공 시 기존 성공 toast와 함께 차단 성공 event를 emit한다.
|
||||
|
||||
- **시작 조건:** `CCB-001`, `CCB-005` 확정.
|
||||
- **완료 증거:** RED/GREEN 체크박스 전체 완료, focused `CreatorChannelHomeViewModelTest` 통과.
|
||||
- **범위 밖:** Fragment/Activity navigation wiring은 `P1-T2`에서 처리한다.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModelTest.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModel.kt`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: 기존 `CreatorChannelHomeUiState.Content`, `CreatorChannelRepository.blockUser(userId, token)`, `CreatorChannelEvent<T>`.
|
||||
- Produces: `blockSuccessLiveData: LiveData<CreatorChannelEvent<Unit>>`.
|
||||
|
||||
- [x] **RED:** 기존 `차단 성공은 block API를 호출하고 차단 완료 토스트를 emit한다` test에 `blockSuccessLiveData` event 검증을 추가한다. 실패 케이스 test에는 `ApiResponse.success == false`일 때 `blockSuccessLiveData.value == null`을 검증한다.
|
||||
- [x] **RED 확인:** 아래 focused test를 실행해 `blockSuccessLiveData` 미정의 또는 event 미발행 assertion 실패를 확인한다.
|
||||
|
||||
```bash
|
||||
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest" --no-parallel --no-daemon -Pkotlin.incremental=false
|
||||
```
|
||||
|
||||
- [x] **GREEN:** `CreatorChannelHomeViewModel`에 `MutableLiveData<CreatorChannelEvent<Unit>>` 기반 `blockSuccessLiveData`를 추가하고, `blockUser()` 성공 분기에서 기존 `creator_channel_block_success` toast emit 후 `CreatorChannelEvent(Unit)`을 emit한다. 실패·예외 분기에서는 emit하지 않는다.
|
||||
- [x] **GREEN 확인:** 같은 focused test를 다시 실행해 성공을 확인한다.
|
||||
- [x] **REFACTOR:** event naming과 observe 순서를 기존 `toastLiveData`, `chatRoomIdLiveData` 스타일과 맞추고 focused test를 재실행해 결과를 이 Task 아래 검증 기록에 남긴다.
|
||||
|
||||
**검증 기록:**
|
||||
|
||||
- 2026-08-14 RED: `CreatorChannelHomeViewModelTest` 실행 시 `blockSuccessLiveData` 미정의로 `compileDebugUnitTestKotlin`이 실패했다.
|
||||
- 2026-08-14 GREEN: `blockSuccessLiveData`와 성공 분기 emit을 최소 추가한 뒤 같은 focused test가 `BUILD SUCCESSFUL`로 통과했다. 성공 event는 한 번만 consume되고, API 실패·예외에서는 emit되지 않음을 확인했다.
|
||||
|
||||
#### Task 1.2 Fragment/Activity 이탈 연결
|
||||
|
||||
**Goal 실행 `P1-T2`:** 차단 성공 event를 `CreatorChannelActivity`까지 전달하고, 이전 화면 또는 `MainV2Activity` fallback으로 이동한다.
|
||||
|
||||
- **시작 조건:** `P1-T1` 완료.
|
||||
- **완료 증거:** RED/GREEN 체크박스 전체 완료, focused `CreatorChannelActivitySourceTest` 통과.
|
||||
- **범위 밖:** 차단 API 호출 방식, 기존 modal UI, `MainV2Activity` 내부 동작 변경.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt`
|
||||
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt`
|
||||
|
||||
**Interfaces:**
|
||||
|
||||
- Consumes: `CreatorChannelHomeViewModel.blockSuccessLiveData`.
|
||||
- Produces: `CreatorChannelHomeFragment.Host.onCreatorChannelBlockSucceeded()`.
|
||||
|
||||
- [x] **RED:** `CreatorChannelActivitySourceTest`에 source contract test를 추가한다. 검증 조건은 `CreatorChannelHomeFragment`가 `blockSuccessLiveData.observe`와 `event.consume()?.let { host.onCreatorChannelBlockSucceeded() }` 흐름을 갖는지, `CreatorChannelActivity`가 `onCreatorChannelBlockSucceeded()`를 구현하는지, 구현 내부에서 `isTaskRoot`, `MainV2Activity::class.java`, `finish()`를 사용하는지 확인한다. 또한 `showUserBlockDialog()` 구간에는 `homeActionDelegate?.blockUser()`만 있고 직접 `finish()`나 `MainV2Activity` 이동이 없음을 확인한다.
|
||||
- [x] **RED 확인:** 아래 focused test를 실행해 observer, host callback, fallback 구현 부재로 인한 assertion 실패를 확인한다.
|
||||
|
||||
```bash
|
||||
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --no-parallel --no-daemon -Pkotlin.incremental=false
|
||||
```
|
||||
|
||||
- [x] **GREEN:** `CreatorChannelHomeFragment.observeViewModel()`에서 `blockSuccessLiveData`를 observe하고 consume 후 host callback을 호출한다. `CreatorChannelHomeFragment.Host`에 `onCreatorChannelBlockSucceeded()`를 추가한다. `CreatorChannelActivity`는 `MainV2Activity` import를 추가하고, `onCreatorChannelBlockSucceeded()`에서 `isTaskRoot`이면 `Intent(this, MainV2Activity::class.java)`를 `startActivity`한 뒤 `finish()`를 호출한다. `isTaskRoot`가 아니면 `finish()`만 호출한다.
|
||||
- [x] **GREEN 확인:** 같은 focused source test를 다시 실행해 성공을 확인한다.
|
||||
- [x] **REFACTOR:** 추가한 source contract가 과도하게 넓지 않은지 확인하고, 기존 `CreatorChannelActivitySourceTest.sourceSection()` helper를 재사용한다. focused test를 재실행해 결과를 이 Task 아래 검증 기록에 남긴다.
|
||||
|
||||
**검증 기록:**
|
||||
|
||||
- 2026-08-14 RED: `CreatorChannelActivitySourceTest` 100개 중 신규 차단 이탈 contract만 observer 부재 assertion으로 실패했다.
|
||||
- 2026-08-14 GREEN: Fragment observer/Host callback과 Activity `isTaskRoot` 분기를 최소 추가한 뒤 같은 source test 100개가 모두 통과했다.
|
||||
|
||||
### 완료 조건
|
||||
|
||||
- [x] `P1-T1`, `P1-T2`의 체크박스와 완료 증거가 모두 충족됐다.
|
||||
- [x] `CCB-001~005`가 구현 또는 명시 검증으로 추적된다.
|
||||
- [x] API 성공 전 navigation이 발생하지 않는다는 source contract가 남아 있다.
|
||||
- [x] task root fallback 대상이 `MainV2Activity`임을 source contract가 검증한다.
|
||||
|
||||
### 검증 방법
|
||||
|
||||
#### Phase 1 Gate
|
||||
|
||||
**Goal 실행 `P1-GATE`:** 차단 성공 후 채널 이탈과 홈 fallback 동작을 최종 판정한다.
|
||||
|
||||
- **시작 조건:** `P1-T1`, `P1-T2` 완료.
|
||||
- **완료 증거:** 아래 자동 검증 통과와 Progress 기록.
|
||||
- **범위 밖:** Gate 통과를 위한 test 삭제·완화, unrelated refactor, instrumentation test 추가.
|
||||
|
||||
```bash
|
||||
./gradlew :app:testDebugUnitTest \
|
||||
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelHomeViewModelTest" \
|
||||
--tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" \
|
||||
--no-parallel --no-daemon -Pkotlin.incremental=false
|
||||
|
||||
./gradlew :app:compileDebugKotlin --no-parallel --no-daemon -Pkotlin.incremental=false
|
||||
|
||||
./gradlew :app:ktlintCheck --no-parallel --no-daemon
|
||||
|
||||
git diff --check
|
||||
```
|
||||
|
||||
**Expected:** focused tests와 compile은 `BUILD SUCCESSFUL`, `git diff --check`는 출력 없음. `ktlintCheck`가 기존 전역 위반으로 실패하면 이번 변경 파일 포함 여부를 확인해 Progress에 구분 기록한다.
|
||||
|
||||
- [x] ViewModel과 Activity source focused test 통합 실행이 통과했다.
|
||||
- [x] `compileDebugKotlin`과 `ktlintCheck`가 통과했다.
|
||||
- [x] `git diff --check`와 `git diff --cached --check` 결과 출력이 없었다.
|
||||
- [x] 전체 local unit suite의 범위 밖 실패를 분리하고 아래 Progress에 기록했다.
|
||||
|
||||
---
|
||||
|
||||
## 실행 순서와 의존성
|
||||
|
||||
| 순서 | Goal | 선행조건 | 병행 가능 | 차단 시 다음 행동 |
|
||||
|---:|---|---|---|---|
|
||||
| 1 | `P1-T1` | PRD 확정 | 아니요 | ViewModel event contract 재검토 |
|
||||
| 2 | `P1-T2` | `P1-T1` | 아니요 | Host callback 또는 Activity fallback contract 재검토 |
|
||||
| 3 | `P1-GATE` | `P1-T1`, `P1-T2` | 아니요 | 실패 소유 Task의 회귀 수정 Task 추가 |
|
||||
|
||||
```text
|
||||
P1-T1 -> P1-T2 -> P1-GATE
|
||||
```
|
||||
|
||||
## 변경 금지 항목
|
||||
|
||||
- 차단 API request/response 계약을 변경하지 않는다.
|
||||
- `MainV2Activity` 내부 구현을 변경하지 않는다.
|
||||
- API 성공 전 `CreatorChannelActivity`를 종료하거나 홈으로 이동하지 않는다.
|
||||
- 기존 modal UI와 string을 변경하지 않는다.
|
||||
- `app/src/androidTest`, 기기·에뮬레이터 UI 조작, 스크린샷 QA를 추가하지 않는다.
|
||||
- 관련 없는 creator channel tab, community, fan talk, live routing 코드를 리팩터링하지 않는다.
|
||||
|
||||
## 의사결정 및 중단 규칙
|
||||
|
||||
- 실패 event 또는 예외 event에서도 화면 이탈이 필요하다는 요구가 생기면 PRD `Decision Log`와 이 문서 범위를 먼저 갱신한다.
|
||||
- `MainV2Activity` fallback에 flag 정책이 필요하다는 증거가 나오면 source/test로 먼저 실패를 고정하고, 기존 홈 진입점의 intent flag 관례를 확인한 뒤 최소 변경한다.
|
||||
- 검증 중 기존 전역 test/ktlint 실패가 나오면 이번 변경 파일 포함 여부를 분리해 기록하고, 범위 밖 실패는 임의 수정하지 않는다.
|
||||
|
||||
## Progress
|
||||
|
||||
기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다.
|
||||
|
||||
### 문서 작성 — 2026-08-14
|
||||
|
||||
- 상태: 완료
|
||||
- 무엇을: PRD와 구현 계획/TASK 문서를 생성했다. production code와 test code는 변경하지 않았다.
|
||||
- 왜: 사용자가 “문서만 생성 또는 수정하고 아직 구현은 하지 않는다”고 지시했다.
|
||||
- 어떻게:
|
||||
- `docs/sample/sample-prd.md` — 샘플 구조 확인.
|
||||
- `docs/sample/sample-plan-task.md` — TDD Task 구조 확인.
|
||||
- `docs/agent-guides/work-plan-docs.md` — 신규 문서 배치와 검증 기록 규칙 확인.
|
||||
- `rg -n "차단|block|CreatorChannel|크리에이터 채널|MainV2Activity|뒤로가기" "docs"` — 기존 관련 문서 범위 확인.
|
||||
- 남은 항목: `P1-T1`, `P1-T2`, `P1-GATE` 구현과 검증.
|
||||
- 다음 행동: 사용자가 구현 진행을 승인하면 `P1-T1` RED 단계부터 시작한다.
|
||||
|
||||
### P1-GATE 1차 실행 — 2026-08-14
|
||||
|
||||
- 상태: 완료
|
||||
- 무엇을: `P1-T1`, `P1-T2`를 RED/GREEN으로 구현하고 focused test, compile, ktlint, diff 검증을 완료했다.
|
||||
- 왜: 차단 성공 후 현재 크리에이터 채널에서 이탈하고 task root에서는 `MainV2Activity`로 fallback해야 한다.
|
||||
- 어떻게:
|
||||
- ViewModel RED — `blockSuccessLiveData` 미정의 compile failure 확인.
|
||||
- ViewModel GREEN — focused `CreatorChannelHomeViewModelTest` `BUILD SUCCESSFUL`.
|
||||
- Activity RED — source test 100개 중 신규 차단 이탈 contract 1건 실패 확인.
|
||||
- Activity GREEN — source test 100개 전체 통과.
|
||||
- focused 통합 test — `BUILD SUCCESSFUL`.
|
||||
- `./gradlew :app:compileDebugKotlin --no-parallel --no-daemon -Pkotlin.incremental=false` — `BUILD SUCCESSFUL`.
|
||||
- `./gradlew :app:ktlintCheck --no-parallel --no-daemon` — 기존 `disabled_rules` deprecation warning만 출력하고 `BUILD SUCCESSFUL`.
|
||||
- `git diff --check`, `git diff --cached --check` — 출력 없음.
|
||||
- 전체 `testDebugUnitTest` — 1,221개 중 범위 밖 `CreatorChannelFanTalkFragmentLayoutTest` 1건 실패. 일본어 리소스는 팬레터 문구이나 테스트는 기존 응원 문구를 기대하며, 관련 파일은 현재 diff에 없다.
|
||||
- 남은 항목: Phase 1 리뷰 blocker 해소 확인.
|
||||
- 다음 행동: 문서 완료 상태를 반영한 diff를 재리뷰한다.
|
||||
|
||||
### P1-GATE 재리뷰 — 2026-08-14
|
||||
|
||||
- 상태: 완료
|
||||
- 무엇을: PRD와 계획 문서의 체크박스·검증 기록을 현재 구현 증거와 재대조했다.
|
||||
- 왜: 초기 리뷰의 유일한 blocker였던 문서 완료 상태 누락을 해소하기 위해서다.
|
||||
- 어떻게:
|
||||
- Oracle 재리뷰 — 이전 blocker 해소 및 코드 신규 blocker 없음, 최종 `APPROVE`.
|
||||
- `rg -n -- "- \\[ \\]" prd.md plan-task.md` — 실제 미완료 체크박스 없음. 템플릿 안내문에 포함된 리터럴 표기만 검색됨.
|
||||
- `git diff --check`, `git diff --cached --check` — 출력 없음.
|
||||
- 남은 항목: 없음.
|
||||
- 다음 행동: 완료 결과를 보고한다.
|
||||
|
||||
### 범위 밖 테스트 정정 — 2026-08-14
|
||||
|
||||
- 상태: 완료
|
||||
- 무엇을: `CreatorChannelFanTalkFragmentLayoutTest`의 일본어 empty message 기대값을 현재 `values-ja/strings.xml`과 맞췄다.
|
||||
- 왜: 전체 unit suite에서 남아 있던 유일한 실패가 테스트 기대값의 오래된 “응원” 문구 때문이었고, 사용자가 테스트 수정 지시를 했다.
|
||||
- 어떻게:
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.fantalk.CreatorChannelFanTalkFragmentLayoutTest" --no-parallel --no-daemon -Pkotlin.incremental=false` — `BUILD SUCCESSFUL`.
|
||||
- `./gradlew :app:testDebugUnitTest --no-parallel --no-daemon -Pkotlin.incremental=false` — `BUILD SUCCESSFUL`.
|
||||
- 남은 항목: 없음.
|
||||
- 다음 행동: 완료 결과를 보고한다.
|
||||
|
||||
## Decision Log
|
||||
|
||||
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|
||||
|---|---|---|---|---|---|
|
||||
| `2026-08-14` | `DEC-001` | 확정 | 차단 성공 후 현재 크리에이터 채널에서 이탈한다. | 사용자 요청과 후속 확인 | `P1-T1`, `P1-T2`, PRD `CCB-001~005` |
|
||||
| `2026-08-14` | `DEC-002` | 확정 | 이전 화면이 없으면 홈인 `MainV2Activity`로 fallback 이동한다. | 사용자 명시 | `P1-T2`, PRD `CCB-004` |
|
||||
| `2026-08-14` | `DEC-003` | 확정 | 이번 턴에서는 문서만 생성하고 구현하지 않는다. | 사용자 명시 | 이 문서 `Progress` |
|
||||
|
||||
## 발견된 문제
|
||||
|
||||
| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 |
|
||||
|---|---|---|---|---|---|
|
||||
| `ISSUE-001` | Medium | 확정 | 현재 차단 성공 흐름은 성공 토스트만 emit하고 채널 화면에 머문다. | `P1-T1`, `P1-T2` | 구현 승인 후 RED test부터 수정한다. |
|
||||
|
||||
## Verification Log
|
||||
|
||||
- 2026-08-14: 문서 작성만 수행했다. 구현 파일과 테스트 파일은 수정하지 않았으므로 Gradle 테스트·compile은 실행하지 않았다. 문서 샘플과 작업 문서 규칙, 기존 관련 문서 검색 결과를 기준으로 새 작업 디렉터리를 생성했다.
|
||||
- 2026-08-14: `P1-T1`, `P1-T2`를 RED/GREEN으로 완료했다. focused test 통합 실행, `compileDebugKotlin`, `ktlintCheck`, diff check는 통과했다. 전체 unit suite는 현재 diff 밖 일본어 팬레터 문구와 기존 테스트 기대값 불일치 1건으로 실패했으며 해당 실패를 범위 밖 blocker로 분리 기록했다.
|
||||
- 2026-08-14: 문서 완료 상태를 갱신한 뒤 Oracle 재리뷰에서 최종 `APPROVE`를 받았다. PRD와 계획 문서에 실제 미완료 체크박스가 없음을 확인했다.
|
||||
- 2026-08-14: 후속 테스트 정정으로 Fan Talk 일본어 empty message 기대값을 현재 리소스와 맞췄고, focused Fan Talk test와 전체 local unit suite가 통과했다.
|
||||
@@ -0,0 +1,150 @@
|
||||
# PRD: 크리에이터 채널 차단 후 이탈
|
||||
|
||||
## 문서 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서 상태 | 구현 완료 |
|
||||
| 작성일 | `2026-08-14` |
|
||||
| 최종 수정일 | `2026-08-14` |
|
||||
| 대상 제품 | 크리에이터 채널 사용자 차단 흐름 |
|
||||
| 작성자·결정권자 | 사용자, Sisyphus |
|
||||
| 관련 API Contract | 기존 `CreatorChannelRepository.blockUser(userId, token)` 계약 사용 |
|
||||
| 관련 구현 계획 | `docs/20260814_크리에이터_채널_차단_후_이탈/plan-task.md` |
|
||||
| 관련 review | 없음 |
|
||||
|
||||
## 1. Overview
|
||||
|
||||
사용자가 크리에이터 채널에서 현재 보고 있는 크리에이터를 차단하면, 차단 성공 후 더 이상 해당 크리에이터 채널 화면에 머물지 않는다. 일반 진입에서는 현재 채널 화면을 종료해 이전 화면으로 돌아가고, 푸시 또는 딥링크처럼 이전 화면이 없는 진입에서는 홈 화면인 `MainV2Activity`로 이동한다.
|
||||
|
||||
## 2. Problem Statement
|
||||
|
||||
- 현재 `CreatorChannelHomeViewModel.blockUser()`는 차단 성공 시 성공 토스트만 emit한다.
|
||||
- 그 결과 사용자가 차단한 크리에이터 채널 화면에 그대로 남아, 차단 이후에도 차단 대상의 채널 콘텐츠를 보는 부자연스러운 상태가 된다.
|
||||
- 딥링크·푸시 진입처럼 이전 화면이 없는 경우 단순 종료만 하면 앱 내 안전한 목적지가 부족할 수 있다.
|
||||
|
||||
문제를 해결했다는 판단은 차단 API 성공 후 현재 `CreatorChannelActivity`가 종료되고, task root 상태에서는 `MainV2Activity`가 fallback으로 시작되는 것으로 한다.
|
||||
|
||||
## 3. Goals
|
||||
|
||||
### 3.1 제품 목표
|
||||
|
||||
- 사용자는 차단 성공 후 현재 크리에이터 채널에서 즉시 빠져나간다.
|
||||
- 일반 진입에서는 Android 뒤로가기와 같은 의미로 이전 화면으로 돌아간다.
|
||||
- 이전 화면이 없는 푸시·딥링크 진입에서는 홈인 `MainV2Activity`로 이동한다.
|
||||
|
||||
### 3.2 UX 목표
|
||||
|
||||
- 기존 차단 확인 모달과 성공 토스트는 유지한다.
|
||||
- API 성공 전에는 화면을 이탈하지 않는다.
|
||||
- API 실패 시에는 현재처럼 오류 토스트만 표시하고 화면에 머문다.
|
||||
|
||||
## 4. Non-Goals
|
||||
|
||||
- 차단 API 계약, 차단 대상 식별자, 서버 응답 형식을 변경하지 않는다.
|
||||
- `MainV2Activity` 자체의 홈 탭 구조나 초기 탭 정책을 변경하지 않는다.
|
||||
- 차단 목록, 차단 해제, 차단된 크리에이터 콘텐츠 필터링 정책은 이번 범위에 포함하지 않는다.
|
||||
- `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷 기반 검증은 이번 계획에 포함하지 않는다.
|
||||
|
||||
## 5. Target Users and Permissions
|
||||
|
||||
| 사용자 | 목표 | 주요 작업 | 사용 환경 |
|
||||
|---|---|---|---|
|
||||
| 로그인 사용자 | 원하지 않는 크리에이터를 차단하고 해당 채널에서 벗어난다 | 크리에이터 채널 더보기 > 차단 확인 | Android 앱 |
|
||||
|
||||
- 인증 주체: 기존 `SharedPreferenceManager.token` 기반 로그인 사용자.
|
||||
- 허용 조건: 기존 크리에이터 채널 차단 액션을 수행할 수 있는 상태.
|
||||
- 거부 조건: API 실패, 네트워크 오류, `CreatorChannelHomeUiState.Content` 미준비 상태.
|
||||
|
||||
## 6. 핵심 사용자 흐름
|
||||
|
||||
1. 사용자가 `CreatorChannelActivity`에서 더보기 메뉴를 연다.
|
||||
2. 사용자가 차단을 선택하고 기존 확인 모달에서 확인한다.
|
||||
3. 앱은 기존 `homeActionDelegate?.blockUser()` 경로로 차단 API를 호출한다.
|
||||
4. API 성공 시 기존 차단 성공 토스트를 표시하고 현재 크리에이터 채널에서 이탈한다.
|
||||
5. `CreatorChannelActivity.isTaskRoot == false`이면 `finish()`로 이전 화면에 돌아간다.
|
||||
6. `CreatorChannelActivity.isTaskRoot == true`이면 `MainV2Activity`를 시작한 뒤 현재 `CreatorChannelActivity`를 종료한다.
|
||||
7. API 실패 또는 예외 발생 시 기존 unknown error toast를 표시하고 현재 화면에 머문다.
|
||||
|
||||
## 7. 정보 구조와 라우팅
|
||||
|
||||
```text
|
||||
CreatorChannelActivity
|
||||
block success, isTaskRoot=false -> finish() -> previous Activity
|
||||
block success, isTaskRoot=true -> MainV2Activity -> finish()
|
||||
```
|
||||
|
||||
- 홈 fallback 대상은 `kr.co.vividnext.sodalive.v2.main.MainV2Activity`다.
|
||||
- fallback은 차단 성공 후 현재 Activity가 task root일 때만 수행한다.
|
||||
|
||||
## 8. 기능 요구사항
|
||||
|
||||
| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 |
|
||||
|---|---|---|---|---|
|
||||
| `CCB-001` | 확정 | 차단 성공은 화면 이탈 이벤트를 발생시킨다. | `CreatorChannelHomeViewModel.blockUser()`가 `ApiResponse.success == true`일 때만 one-shot 차단 성공 이벤트를 emit한다. | `P1-T1` |
|
||||
| `CCB-002` | 확정 | API 성공 전에는 화면 이탈을 시작하지 않는다. | `showUserBlockDialog()`의 확인 콜백은 기존처럼 `homeActionDelegate?.blockUser()`만 호출하며 직접 `finish()` 또는 홈 이동을 하지 않는다. | `P1-T2` |
|
||||
| `CCB-003` | 확정 | 일반 진입에서는 이전 화면으로 돌아간다. | `CreatorChannelActivity.isTaskRoot == false`이면 `finish()`만 호출한다. | `P1-T2` |
|
||||
| `CCB-004` | 확정 | 이전 화면이 없는 진입에서는 홈으로 이동한다. | `CreatorChannelActivity.isTaskRoot == true`이면 `MainV2Activity` intent를 시작하고 현재 Activity를 종료한다. | `P1-T2` |
|
||||
| `CCB-005` | 확정 | 기존 성공/실패 feedback은 유지한다. | 성공 시 `creator_channel_block_success`, 실패·예외 시 `common_error_unknown` toast 흐름을 유지한다. | `P1-T1` |
|
||||
|
||||
## 9. UI/UX Expectations
|
||||
|
||||
- 신규 UI는 없다.
|
||||
- 기존 차단 확인 모달 문구와 버튼은 변경하지 않는다.
|
||||
- 성공 후 이동은 추가 확인 없이 즉시 수행한다.
|
||||
|
||||
## 10. API 계약
|
||||
|
||||
- 신규 API는 없다.
|
||||
- 기존 호출 경로: `CreatorChannelRepository.blockUser(userId, token)`.
|
||||
- 성공 판단은 기존 `ApiResponse.success`를 따른다.
|
||||
|
||||
## 11. 보안과 데이터 취급
|
||||
|
||||
- token, URL, 사용자 식별자 등 민감 정보는 로그·Toast·문서 fixture에 추가 노출하지 않는다.
|
||||
- 실패 시 서버 오류 상세를 사용자에게 직접 노출하지 않고 기존 unknown error toast를 유지한다.
|
||||
|
||||
## 12. 성공 기준
|
||||
|
||||
### 12.1 기능 수용 기준
|
||||
|
||||
- [x] 차단 성공 시 `CreatorChannelHomeViewModel`이 성공 토스트와 차단 성공 이벤트를 emit한다. (`CCB-001`, `CCB-005`)
|
||||
- [x] 차단 실패 또는 예외 시 차단 성공 이벤트가 emit되지 않는다. (`CCB-001`, `CCB-005`)
|
||||
- [x] `CreatorChannelHomeFragment`가 차단 성공 이벤트를 consume한 뒤 `Host`에 전달한다. (`CCB-001`)
|
||||
- [x] `CreatorChannelActivity`가 `isTaskRoot`에 따라 이전 화면 복귀 또는 `MainV2Activity` fallback을 수행한다. (`CCB-003`, `CCB-004`)
|
||||
- [x] 확인 모달의 confirm callback은 API 성공 전 직접 이동하지 않는다. (`CCB-002`)
|
||||
|
||||
### 12.2 검증 기준
|
||||
|
||||
- [x] `CreatorChannelHomeViewModelTest` focused unit test가 통과한다.
|
||||
- [x] `CreatorChannelActivitySourceTest` focused source contract test가 통과한다.
|
||||
- [x] 변경 파일 compile, ktlint 범위 검증이 통과하거나 기존 전역 blocker를 문서에 기록한다.
|
||||
|
||||
## 13. Open Questions
|
||||
|
||||
| ID | 상태 | 결정 필요 사항 | 현재 결정 | 결정 주체 | 영향 Goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `OQ-001` | 확정 | 뒤로가기 의미 | 현재 `CreatorChannelActivity`에서 빠져나가 이전 화면으로 돌아가는 의미 | 사용자 | `P1-T2` |
|
||||
| `OQ-002` | 확정 | 이전 화면이 없는 경우 fallback | `MainV2Activity`로 이동 | 사용자 | `P1-T2` |
|
||||
|
||||
## 14. 요구사항 추적표
|
||||
|
||||
| 요구사항 범위 | API Contract | 계획 Phase | Goal | 자동 검증 | 수동 검증 |
|
||||
|---|---|---:|---|---|---|
|
||||
| `CCB-001`, `CCB-005` | 기존 `blockUser` | 1 | `P1-T1` | `CreatorChannelHomeViewModelTest` | 없음 |
|
||||
| `CCB-002~004` | 없음 | 1 | `P1-T2` | `CreatorChannelActivitySourceTest` | 없음 |
|
||||
|
||||
## 15. Decision Log
|
||||
|
||||
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·계약·Goal |
|
||||
|---|---|---|---|---|---|
|
||||
| `2026-08-14` | `DEC-001` | 확정 | 차단 성공 후 현재 크리에이터 채널에서 이탈한다. | 사용자 요청 | `CCB-001~005`, `P1-T1`, `P1-T2` |
|
||||
| `2026-08-14` | `DEC-002` | 확정 | 일반 진입은 `finish()`로 이전 화면에 돌아가고, task root는 `MainV2Activity`로 fallback한다. | 사용자 확인 | `CCB-003`, `CCB-004`, `P1-T2` |
|
||||
|
||||
## Verification Log
|
||||
|
||||
- 2026-08-14: 현재 구현 맥락을 확인했다. `CreatorChannelHomeViewModel.blockUser()`는 성공 토스트만 emit하고, `CreatorChannelActivity.showUserBlockDialog()`는 확인 시 `homeActionDelegate?.blockUser()`를 호출한다. 이번 문서 작성 요청에 따라 production code와 test code는 변경하지 않았다.
|
||||
- 2026-08-14: 승인 후 `CreatorChannelHomeViewModel`의 차단 성공 one-shot event, `CreatorChannelHomeFragment.Host` 전달, `CreatorChannelActivity`의 `isTaskRoot` 기반 `MainV2Activity` fallback을 구현했다. ViewModel/source contract RED를 각각 확인한 뒤 최소 구현으로 GREEN 전환했다.
|
||||
- 2026-08-14: focused test 통합 실행, `compileDebugKotlin`, `ktlintCheck`, `git diff --check`가 성공했다. 전체 `testDebugUnitTest`는 1,221개 중 이번 변경과 무관한 `CreatorChannelFanTalkFragmentLayoutTest` 일본어 문구 기대값 불일치 1건으로 실패했으며, 실패 test와 locale string 파일은 현재 diff에 포함되지 않는다.
|
||||
- 2026-08-14: Phase 1 재리뷰에서 이전 문서 정합성 blocker 해소를 확인하고 최종 `APPROVE` 판정을 받았다.
|
||||
- 2026-08-14: 후속으로 `CreatorChannelFanTalkFragmentLayoutTest`의 일본어 empty message 기대값을 현재 `values-ja/strings.xml`의 팬레터 문구와 맞췄다. focused Fan Talk layout test와 전체 `testDebugUnitTest`가 `BUILD SUCCESSFUL`로 통과했다.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Phase 1 크리에이터 차단 후 이탈 코드 리뷰
|
||||
|
||||
## 1. 리뷰 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 리뷰 대상 | Phase 1 / `P1-T1`, `P1-T2`, `P1-GATE` |
|
||||
| 기준 commit 또는 working tree | 2026-08-14 working tree |
|
||||
| 리뷰 일자 | `2026-08-14` |
|
||||
| 리뷰어 | Oracle, Sisyphus |
|
||||
| 기준 문서 | `docs/20260814_크리에이터_채널_차단_후_이탈/prd.md`, `plan-task.md` |
|
||||
| 리뷰 상태 | 수정 검증 완료 |
|
||||
|
||||
## 2. 리뷰 목적과 범위
|
||||
|
||||
- 차단 성공 후에만 현재 채널에서 이탈하는지 확인한다.
|
||||
- 일반 진입은 이전 화면으로 돌아가고 task root는 `MainV2Activity`로 fallback하는지 확인한다.
|
||||
- ViewModel event, Fragment Host 전달, Activity navigation과 local test·문서 완료 증거를 검토한다.
|
||||
- `app/src/androidTest`, 기기·에뮬레이터 조작, 범위 밖 i18n 실패 수정은 제외한다.
|
||||
|
||||
## 3. 검토한 근거
|
||||
|
||||
- 요구사항: `CCB-001~005`.
|
||||
- 계획: `P1-T1`, `P1-T2`, `P1-GATE`.
|
||||
- 코드: `CreatorChannelHomeViewModel.kt`, `CreatorChannelHomeFragment.kt`, `CreatorChannelActivity.kt`.
|
||||
- 테스트: `CreatorChannelHomeViewModelTest`, `CreatorChannelActivitySourceTest`.
|
||||
|
||||
## 4. 실행한 검증
|
||||
|
||||
| 명령 | 결과 | 핵심 증거 |
|
||||
|---|---|---|
|
||||
| ViewModel focused RED/GREEN | 성공 | 미정의 compile failure 후 `BUILD SUCCESSFUL` |
|
||||
| Activity source RED/GREEN | 성공 | 신규 contract 1건 실패 후 source test 100개 통과 |
|
||||
| focused test 통합 실행 | 성공 | `BUILD SUCCESSFUL` |
|
||||
| `compileDebugKotlin` | 성공 | `BUILD SUCCESSFUL` |
|
||||
| `ktlintCheck` | 성공 | 기존 deprecation warning 외 오류 없음 |
|
||||
| `git diff --check`, `git diff --cached --check` | 성공 | 출력 없음 |
|
||||
| 전체 `testDebugUnitTest` | 범위 밖 실패 | 1,221개 중 기존 Fan Talk 일본어 문구 불일치 1건 |
|
||||
|
||||
## 5. 발견 사항
|
||||
|
||||
### REV-P1-001 — 계획 문서 완료 상태와 검증 기록 누락
|
||||
|
||||
- **심각도:** Blocker
|
||||
- **상태:** 수정 완료
|
||||
- **관련 요구사항:** Phase 완료 조건
|
||||
- **소유 Task:** `P1-GATE`
|
||||
|
||||
초기 리뷰에서 구현과 테스트는 `CCB-001~005`를 충족했지만, `plan-task.md` 체크박스와 검증 기록이 미구현 상태로 남아 완료 판정을 막았다. 실제 RED/GREEN/focused/compile/ktlint/diff/전체 suite 결과를 기존 기록을 삭제하지 않고 누적하고, 증거가 충족된 체크박스를 완료 처리했다.
|
||||
|
||||
## 6. plan·goal 전환
|
||||
|
||||
별도 코드 회귀 수정 Task는 없다. `REV-P1-001`은 코드 결함이 아닌 현재 Phase 문서 정합성 문제로, 기존 `P1-GATE` 기록 갱신으로 처리했다.
|
||||
|
||||
## 7. 리뷰 종료 판정
|
||||
|
||||
| 판정 항목 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 리뷰 범위 전체 확인 | 충족 | `CCB-001~005` 코드·test 대조 |
|
||||
| 후보 항목 판정 완료 | 충족 | `REV-P1-001` 수정 완료 |
|
||||
| 확정 항목 plan 반영 | 충족 | `P1-GATE` 체크박스와 Progress 갱신 |
|
||||
| 검증 명령과 결과 기록 | 충족 | Task별 검증 기록과 Verification Log |
|
||||
|
||||
**최종 결론:** 수정 검증 완료
|
||||
|
||||
**남은 항목:** 없음.
|
||||
|
||||
## 8. 수정 후 검증 기록
|
||||
|
||||
### 1차 수정 검증 — 2026-08-14
|
||||
|
||||
- 무엇을: `REV-P1-001`의 문서 체크박스와 RED/GREEN/Gate 검증 기록을 현재 증거에 맞게 갱신했다.
|
||||
- 왜: 구현과 테스트가 완료됐지만 계획 문서가 미구현 상태로 남아 Phase 완료 판정을 막았기 때문이다.
|
||||
- 어떻게:
|
||||
- Oracle 재리뷰 — 이전 blocker 해소, 코드 신규 blocker 없음, 최종 `APPROVE`.
|
||||
- PRD/plan 미완료 체크박스 검색 — 실제 미완료 항목 없음.
|
||||
- `git diff --check`, `git diff --cached --check` — 출력 없음.
|
||||
- 남은 항목: 없음.
|
||||
|
||||
### 2차 수정 검증 — 2026-08-14
|
||||
|
||||
- 무엇을: 이전 전체 suite 실패 원인이던 Fan Talk 일본어 empty message 테스트 기대값을 현재 리소스와 맞췄다.
|
||||
- 왜: 테스트가 오래된 “응원” 문구를 기대했고, 현재 일본어 리소스는 “팬레터” 문구가 맞기 때문이다.
|
||||
- 어떻게:
|
||||
- focused `CreatorChannelFanTalkFragmentLayoutTest` — `BUILD SUCCESSFUL`.
|
||||
- 전체 `:app:testDebugUnitTest` — `BUILD SUCCESSFUL`.
|
||||
- 남은 항목: 없음.
|
||||
Reference in New Issue
Block a user