feat(creator): 차단 성공 후 채널에서 이탈한다

This commit is contained in:
2026-08-14 16:20:29 +09:00
parent cbce4af2f9
commit 7a21b37d8f
8 changed files with 582 additions and 1 deletions
@@ -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`로 통과했다.