diff --git a/docs/20260914_크리에이터_채널_공유/plan-task.md b/docs/20260914_크리에이터_채널_공유/plan-task.md new file mode 100644 index 00000000..b8ff29e6 --- /dev/null +++ b/docs/20260914_크리에이터_채널_공유/plan-task.md @@ -0,0 +1,251 @@ +# 크리에이터 채널 공유 구현 계획 + +| 문서 항목 | 내용 | +|---|---| +| 상태 | 구현 대기 | +| 작성일 | `2026-09-14` | +| 요구사항 기준 | `docs/20260914_크리에이터_채널_공유/prd.md` | +| API 기준 | 없음 | +| 현재 Phase | Phase 1 | +| 현재 활성 Goal | 없음 | + +## 목표 + +크리에이터 채널 화면에서 내 채널은 우측 상단 공유 버튼으로, 다른 사람 채널은 더보기 BottomSheet의 `채널 공유` 항목으로 같은 채널 공유 chooser를 실행한다. + +## 현재 상태 + +| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 | +|---:|---|---:|---|---| +| 1 | 대기 | `0/4` | `P1-T1` | 없음 | + +## 범위 + +### 포함 + +- `CreatorChannelActivity` title bar에 owner 전용 공유 버튼을 추가한다. +- `CreatorChannelMoreBottomSheet`에 non-owner 전용 `채널 공유` 항목 callback을 추가한다. +- 기존 `UserProfileActivity` 887~902줄과 같은 `Intent.ACTION_SEND` chooser 흐름을 사용한다. +- 기존 `UserProfileViewModel.shareChannel()`과 같은 OneLink params/message 형식을 사용한다. + +### 제외 + +- 서버 API, analytics, 신규 dependency 추가. +- 레거시 `UserProfileActivity`, `UserProfileViewModel` 수정. +- `app/src/androidTest`, 기기·에뮬레이터 조작, 스크린샷 QA. + +## 기술적 제약 + +- 기술 스택: Android Kotlin, XML layout, ViewBinding, Material BottomSheet. +- 아키텍처: `CreatorChannelActivity`가 header owner 상태를 기준으로 title action을 bind하고 공유 chooser를 실행한다. +- 데이터·보안: 공유 텍스트에는 공개 닉네임과 OneLink URL만 포함하며 token/BuildConfig 값은 노출하지 않는다. +- 의존성: 신규 dependency를 추가하지 않는다. +- 테스트 범위: `app/src/test` source/local unit test만 계획한다. +- 구현: 각 구현 Task는 RED → GREEN → REFACTOR 순서로 진행한다. + +## 구현 파일 맵 + +- Modify: `app/src/main/res/layout/activity_creator_channel.xml` + - `title_action_container` 안에 `iv_share` ImageView를 추가하고 `@drawable/ic_audio_content_share`를 지정한다. +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt` + - `iv_share` click listener, owner/non-owner visibility, BottomSheet 공유 callback, chooser 실행 함수를 추가한다. +- Modify: `app/src/main/res/layout/dialog_creator_channel_more.xml` + - 기존 메뉴 항목과 같은 스타일로 `tv_channel_share`를 추가한다. +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelMoreBottomSheet.kt` + - `onClickChannelShare` callback을 추가하고 항목 클릭 시 dismiss 후 호출한다. +- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelShareSourceTest.kt` + - layout/source 계약을 검증한다. + +## Phase 1 + +**Phase 결과:** 크리에이터 채널의 owner/non-owner 공유 진입점과 공유 chooser 실행 경로가 구현된다. + +**선행조건:** `docs/20260914_크리에이터_채널_공유/prd.md` 확정. + +**Phase 완료 조건:** `P1-T1`~`P1-T3`과 `P1-GATE` 완료, 검증 기록 누적. + +### 구현 항목 + +#### Task 1.1 owner title bar 공유 버튼 추가 + +**Goal 실행 `P1-T1`:** 내 채널 우측 상단에 `ic_audio_content_share` 공유 버튼을 표시한다. + +- **시작 조건:** `SHARE-001`, `SHARE-002` 확정. +- **완료 증거:** source test PASS, `activity_creator_channel.xml`에 공유 버튼 추가, `bindTitleBar()` owner 분기 갱신. +- **범위 밖:** BottomSheet 공유 항목과 chooser 실행 구현. + +**Files:** + +- Modify: `app/src/main/res/layout/activity_creator_channel.xml` +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt` +- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelShareSourceTest.kt` + +**Interfaces:** + +- Consumes: `CreatorChannelHeaderUiModel.isOwner` +- Produces: `iv_share` owner 전용 title action + +- [ ] **RED:** `CreatorChannelShareSourceTest`에 `activity_creator_channel.xml`이 `@+id/iv_share`와 `@drawable/ic_audio_content_share`를 포함해야 한다는 source test를 추가한다. `CreatorChannelActivity.kt`가 owner 분기에서 `binding.ivShare.visibility = View.VISIBLE`를 포함해야 한다는 source test를 추가한다. +- [ ] **RED 확인:** `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest"`를 실행해 `iv_share` 또는 owner visibility 계약 미구현 실패를 확인한다. +- [ ] **GREEN:** `activity_creator_channel.xml`의 `title_action_container`에 `ImageView` `@+id/iv_share`를 추가하고 `android:src="@drawable/ic_audio_content_share"`, `android:visibility="gone"`을 지정한다. `CreatorChannelActivity.bindTitleBar()` owner 분기에서 follow, bell, more를 숨기고 share를 보이게 하며, non-owner 분기에서는 share를 숨긴다. +- [ ] **GREEN 확인:** 같은 focused test를 다시 실행해 PASS를 확인한다. +- [ ] **REFACTOR:** 새 동작을 바꾸지 않는 범위에서 이번 Task가 만든 중복만 정리하고 focused test 결과를 Progress에 기록한다. + +#### Task 1.2 non-owner BottomSheet 공유 항목 추가 + +**Goal 실행 `P1-T2`:** 다른 사람 채널 더보기 BottomSheet에 `채널 공유` 항목을 추가한다. + +- **시작 조건:** `P1-T1` 완료, `SHARE-003` 확정. +- **완료 증거:** source test PASS, 기존 block/report/profile report callback 유지. +- **범위 밖:** 실제 chooser payload 생성. + +**Files:** + +- Modify: `app/src/main/res/layout/dialog_creator_channel_more.xml` +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelMoreBottomSheet.kt` +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt` +- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelShareSourceTest.kt` + +**Interfaces:** + +- Consumes: `CreatorChannelMoreBottomSheet.newInstance()` +- Produces: `CreatorChannelMoreBottomSheet.onClickChannelShare` + +- [ ] **RED:** source test에 `dialog_creator_channel_more.xml`의 `@+id/tv_channel_share`와 `@string/screen_user_profile_share_channel` 존재를 검증한다. `CreatorChannelMoreBottomSheet.kt`가 `onClickChannelShare` callback과 `tvChannelShare.setOnClickListener`를 포함해야 한다는 source test를 추가한다. +- [ ] **RED 확인:** `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest"`를 실행해 BottomSheet 공유 항목 미구현 실패를 확인한다. +- [ ] **GREEN:** `dialog_creator_channel_more.xml` 상단 메뉴 영역에 기존 TextView와 같은 style/padding/textColor/textSize로 `tv_channel_share`를 추가한다. `CreatorChannelMoreBottomSheet`에 `var onClickChannelShare: (() -> Unit)? = null`을 추가하고 클릭 시 `dismiss()` 후 callback을 호출한다. `CreatorChannelActivity.onMoreClicked()`에서 `onClickChannelShare = { shareCurrentCreatorChannel() }`를 연결한다. +- [ ] **GREEN 확인:** 같은 focused test를 다시 실행해 PASS를 확인한다. +- [ ] **REFACTOR:** 기존 block/report/profile report callback이 삭제되지 않았는지 source test와 diff로 확인하고 Progress에 기록한다. + +#### Task 1.3 공유 메시지와 chooser 실행 연결 + +**Goal 실행 `P1-T3`:** 두 공유 진입점이 기존 프로필 공유와 같은 텍스트 payload로 Android chooser를 실행한다. + +- **시작 조건:** `P1-T1`, `P1-T2` 완료, `SHARE-004` 확정. +- **완료 증거:** source test PASS, 기존 프로필 공유와 같은 ACTION_SEND/EXTRA_TEXT/chooser title 확인. +- **범위 밖:** 공유 메시지 문구 변경, 신규 API, analytics. + +**Files:** + +- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt` +- Test: `app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelShareSourceTest.kt` + +**Interfaces:** + +- Consumes: `currentHeader.creatorId`, `currentHeader.nickname`, `Utils.createOneLinkUrl` +- Produces: `shareCurrentCreatorChannel()` + +- [ ] **RED:** source test에 `CreatorChannelActivity.kt`가 `Intent(Intent.ACTION_SEND)`, `intent.type = "text/plain"`, `Intent.EXTRA_TEXT`, `Intent.createChooser`, `R.string.screen_user_profile_share_channel`, `deep_link_value` = `channel`, `deep_link_sub5`를 포함해야 한다는 계약을 추가한다. +- [ ] **RED 확인:** `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest"`를 실행해 공유 payload 미구현 실패를 확인한다. +- [ ] **GREEN:** `CreatorChannelActivity`에 `shareCurrentCreatorChannel()` private 함수를 추가한다. `currentHeader`가 없거나 `creatorId <= 0L`이면 return한다. params는 `af_dp=voiceon://`, `deep_link_value=channel`, `deep_link_sub5=$creatorId`를 사용한다. 메시지는 `소다라이브 ${header.nickname}님의 채널입니다.\n$shareUrl`로 만들고 기존 Activity 공유 코드와 같은 chooser를 실행한다. `setupClickListeners()`에서 `binding.ivShare.setOnClickListener { shareCurrentCreatorChannel() }`를 연결한다. +- [ ] **GREEN 확인:** 같은 focused test를 다시 실행해 PASS를 확인한다. +- [ ] **REFACTOR:** `UserProfileActivity`, `UserProfileViewModel`이 수정되지 않았는지 `git diff -- app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileViewModel.kt`로 확인하고 Progress에 기록한다. + +### 완료 조건 + +- [ ] `P1-T1`, `P1-T2`, `P1-T3`의 체크박스와 완료 증거가 모두 충족됐다. +- [ ] `SHARE-001~004`, `REG-001~002`가 구현·검증 기록으로 추적된다. +- [ ] 레거시 프로필 파일 수정이 없다. + +### 검증 방법 + +#### Phase 1 Gate + +**Goal 실행 `P1-GATE`:** 크리에이터 채널 공유 진입점과 기존 더보기 동작 보존을 최종 판정한다. + +- **시작 조건:** Phase 1의 모든 활성 Task goal 완료. +- **완료 증거:** 아래 명령과 Expected 통과, Progress에 실제 결과 누적. +- **범위 밖:** 실패와 무관한 UI 리팩터링, 신규 dependency, 계측 테스트 추가. + +```bash +./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest" +./gradlew :app:mergeDebugResources +git diff --check +``` + +**Expected:** focused source test PASS, resource merge PASS, whitespace error 0건. + +수동 검증: + +- [ ] 내 채널 진입 시 우측 상단 `ic_audio_content_share` 버튼이 표시되고 더보기는 숨겨진다. +- [ ] 내 채널 공유 버튼 탭 시 Android 공유 chooser 제목이 `채널 공유`로 표시된다. +- [ ] 다른 사람 채널 진입 시 기존 follow, bell, more가 표시되고 공유 버튼은 숨겨진다. +- [ ] 다른 사람 채널 더보기 BottomSheet에서 `채널 공유` 선택 시 같은 Android 공유 chooser가 표시된다. +- [ ] 기존 차단, 유저 신고, 프로필 신고 항목이 계속 동작한다. + +## 실행 순서와 의존성 + +| 순서 | Goal | 선행조건 | 병행 가능 | 차단 시 다음 행동 | +|---:|---|---|---|---| +| 1 | `P1-T1` | PRD 확정 | 아니요 | title bar layout/source 계약 재확인 | +| 2 | `P1-T2` | `P1-T1` | 아니요 | BottomSheet binding 이름 재확인 | +| 3 | `P1-T3` | `P1-T1`, `P1-T2` | 아니요 | 기존 프로필 공유 코드 재대조 | +| 4 | `P1-GATE` | Phase 1 Task 전체 | 아니요 | 실패 소유 Task의 회귀 수정 goal 생성 | + +```text +P1-T1 → P1-T2 → P1-T3 → P1-GATE +``` + +## 변경 금지 항목 + +- `UserProfileActivity`, `UserProfileViewModel`을 수정하지 않는다. +- 공유 메시지, OneLink key, chooser title을 임의 변경하지 않는다. +- 신규 dependency, 신규 서버 API, analytics event를 추가하지 않는다. +- test를 삭제·skip·완화하거나 타입 오류를 우회하지 않는다. +- 요청 범위 밖의 layout 리팩터링을 하지 않는다. + +## 의사결정 및 중단 규칙 + +- `currentHeader`가 없는 상태에서 공유 요청을 처리할 수 없으면 no-op으로 둔다. +- PRD와 구현 계획이 충돌하면 구현 전에 문서를 먼저 정정한다. +- 공유 링크 정책 변경 요청이 나오면 PRD Decision Log를 정정하고 `P1-T3` 범위를 갱신한다. +- 완료 증거와 Progress 기록까지 충족한 뒤에만 goal을 complete로 갱신한다. + +## Progress + +기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다. + +### 문서 작성 — 2026-09-14 + +- 상태: 완료 +- 무엇을: `prd.md`, `plan-task.md` 신규 작성. +- 왜: 사용자 요청이 문서 생성까지만 허용하고 코드 구현을 금지했기 때문. +- 어떻게: + - `UserProfileActivity.kt:887~902` — 기존 Android chooser 공유 흐름 확인. + - `UserProfileViewModel.kt:351~362` — 기존 채널 OneLink params와 메시지 형식 확인. + - `CreatorChannelActivity.kt:230~441` — title action, owner 분기, 더보기 BottomSheet 진입 확인. + - `activity_creator_channel.xml:179~238` — title action container와 `iv_more` 구조 확인. + - `dialog_creator_channel_more.xml:19~47`, `CreatorChannelMoreBottomSheet.kt:12~43` — 기존 BottomSheet 메뉴/callback 구조 확인. +- 남은 항목: 코드 구현 전체. +- 다음 행동: 사용자가 구현을 요청하면 `P1-T1`부터 진행. + +## Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 | +|---|---|---|---|---|---| +| `2026-09-14` | `DEC-001` | 확정 | 내 채널은 title bar 직접 공유 버튼, 타인 채널은 더보기 BottomSheet 공유 항목으로 구현한다. | 사용자 요청 | `P1-T1`, `P1-T2` | +| `2026-09-14` | `DEC-002` | 확정 | 기존 프로필 공유의 chooser와 OneLink 형식을 크리에이터 채널 v2 안에서 동일하게 사용한다. | `UserProfileActivity.kt:887~902`, `UserProfileViewModel.kt:351~362` 확인 | `P1-T3` | + +## 발견된 문제 + +| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 | +|---|---|---|---|---|---| +| 없음 | Low | 후보 없음 | 문서 작성 범위에서 차단 이슈 없음 | 없음 | 없음 | + +## 최종 보고 형식 + +```markdown +구현 결과: 완료한 Phase와 사용자 흐름을 적는다. + +- 변경: 주요 파일과 동작을 적는다. +- 결정: 중요한 Decision Log ID와 내용을 적는다. +- 검증: + - `실행 명령` — 성공/실패와 핵심 수치를 적는다. + - `수동 검증` — 성공/실패/불가 사유를 적는다. +- 남은 항목: 외부 의존, 후속 범위 또는 없음을 적는다. +- 문서: 갱신한 PRD/API Contract/plan/review 링크를 적는다. +``` + +## Verification Log + +- 2026-09-14: 문서 작성만 수행했다. 코드 구현, Gradle 빌드, 기능 테스트는 실행하지 않았다. diff --git a/docs/20260914_크리에이터_채널_공유/prd.md b/docs/20260914_크리에이터_채널_공유/prd.md new file mode 100644 index 00000000..3b1ffa93 --- /dev/null +++ b/docs/20260914_크리에이터_채널_공유/prd.md @@ -0,0 +1,176 @@ +# 크리에이터 채널 공유 PRD + +## 문서 정보 + +| 항목 | 내용 | +|---|---| +| 문서 상태 | 구현 기준 확정 | +| 작성일 | `2026-09-14` | +| 최종 수정일 | `2026-09-14` | +| 대상 제품 | 크리에이터 채널 공유 | +| 작성자·결정권자 | 사용자 요청 기준 | +| 관련 API Contract | 없음 | +| 관련 구현 계획 | `docs/20260914_크리에이터_채널_공유/plan-task.md` | +| 관련 review | 없음 | + +### 요구사항 상태 + +| 상태 | 의미 | 구현 처리 | +|---|---|---| +| 확정 | 제품·기술 결정이 완료되어 구현 기준으로 사용 | `plan-task.md`의 Task와 완료 증거로 추적 | +| 제외 | 현재 릴리스에서 구현하지 않기로 결정 | Non-Goals와 Decision Log에 기록 | + +## 1. Overview + +크리에이터 채널 화면에서 사용자가 현재 보고 있는 채널을 Android 공유 시트로 공유할 수 있게 한다. 내 채널은 우측 상단에 공유 버튼을 바로 노출하고, 다른 사람 채널은 우측 상단 더보기 BottomSheet 안에 `채널 공유` 항목을 추가한다. + +## 2. Problem Statement + +현재 크리에이터 채널 화면에는 채널 공유 진입점이 없다. 사용자는 크리에이터 채널을 외부 앱으로 공유하려면 기존 프로필 화면 같은 다른 경로를 찾아야 한다. 이 기능이 완료되면 크리에이터 채널 화면에서 소유자와 방문자 모두 같은 채널 공유 메시지를 Android 공유 시트로 보낼 수 있다. + +## 3. Goals + +### 3.1 제품 목표 + +- 내 채널 사용자는 크리에이터 채널 우측 상단 공유 버튼을 눌러 즉시 채널을 공유한다. +- 다른 사람 채널 사용자는 우측 상단 더보기 버튼을 누른 뒤 `채널 공유`를 선택해 채널을 공유한다. +- 공유 메시지와 링크 생성 방식은 `UserProfileActivity`의 기존 채널 공유 흐름과 동일하게 유지한다. + +### 3.2 UX 목표 + +- 내 채널에서는 기존에 숨겨지던 우측 상단 영역에 공유 버튼만 표시한다. +- 다른 사람 채널에서는 기존 follow, 알림, 더보기 동작을 유지하고 더보기 BottomSheet에 공유 항목을 추가한다. +- 우측 상단 공유 버튼 아이콘은 `@drawable/ic_audio_content_share`를 사용한다. + +## 4. Non-Goals + +- 신규 공유 API, 서버 계약, analytics event를 추가하지 않는다. +- `UserProfileActivity`, `UserProfileViewModel` 같은 레거시 파일은 수정하지 않는다. +- 공유 메시지 문구, OneLink 파라미터, chooser 제목을 새로 정의하지 않는다. +- `app/src/androidTest`, 기기·에뮬레이터 UI 조작, 스크린샷 QA는 이번 범위에서 제외한다. + +## 5. Target Users and Permissions + +| 사용자 | 목표 | 주요 작업 | 사용 환경 | +|---|---|---|---| +| 내 채널 소유자 | 내 채널 링크를 빠르게 외부 앱으로 공유 | 우측 상단 공유 버튼 탭 | Android 앱 | +| 다른 사람 채널 방문자 | 방문 중인 채널 링크를 외부 앱으로 공유 | 더보기 BottomSheet에서 `채널 공유` 탭 | Android 앱 | + +### 권한 + +- 인증 주체: 현재 앱 사용자. +- 허용 역할: 내 채널 소유자와 다른 사람 채널 방문자 모두 허용. +- 리소스 소유권: `CreatorChannelHeaderUiModel.isOwner`로 UI 진입점을 분기한다. +- 거부 조건: `currentHeader`가 없거나 `creatorId <= 0`인 상태에서는 공유 액션을 실행하지 않는다. + +## 6. 핵심 사용자 흐름 + +1. 사용자가 `CreatorChannelActivity`에 진입한다. +2. Activity가 채널 header를 로드하고 `currentHeader`를 갱신한다. +3. 내 채널이면 우측 상단 `ic_audio_content_share` 버튼이 표시되고, 기존 더보기 버튼은 계속 숨긴다. +4. 다른 사람 채널이면 기존 더보기 버튼이 표시되고, BottomSheet에 `채널 공유` 항목이 추가된다. +5. 공유 액션은 `Intent.ACTION_SEND`, `text/plain`, `Intent.EXTRA_TEXT`를 사용하고 chooser 제목은 `R.string.screen_user_profile_share_channel`을 사용한다. + +## 7. 정보 구조와 라우팅 + +```text +CreatorChannelActivity + 내 채널: title_action_container > iv_share + 다른 사람 채널: iv_more > CreatorChannelMoreBottomSheet > tv_channel_share + 공유 실행: Android ACTION_SEND chooser +``` + +- 딥링크 payload는 기존 프로필 공유와 동일하게 `deep_link_value=channel`, `deep_link_sub5={creatorId}`를 사용한다. +- 공유 메시지는 기존 `소다라이브 ${nickname}님의 채널입니다.\n{shareUrl}` 형식을 유지한다. + +## 8. 기능 요구사항 + +### 8.1 채널 공유 진입점 + +| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 | +|---|---|---|---|---| +| `SHARE-001` | 확정 | 내 채널은 우측 상단에 공유 버튼을 바로 표시한다. | `header.isOwner == true`일 때 `iv_share`가 보이고 `iv_more`, follow, bell은 숨겨진다. | `P1-T1` | +| `SHARE-002` | 확정 | 내 채널 우측 상단 공유 버튼은 `ic_audio_content_share`를 사용한다. | `activity_creator_channel.xml`의 공유 버튼 source가 `@drawable/ic_audio_content_share`다. | `P1-T1` | +| `SHARE-003` | 확정 | 다른 사람 채널은 더보기 BottomSheet에 `채널 공유` 항목을 표시한다. | `header.isOwner == false`일 때 기존 더보기 진입 후 `tv_channel_share` 항목이 표시된다. | `P1-T2` | +| `SHARE-004` | 확정 | 공유 실행은 기존 프로필 공유와 같은 Android chooser 흐름을 사용한다. | `Intent.ACTION_SEND`, `text/plain`, `Intent.EXTRA_TEXT`, `Intent.createChooser(..., screen_user_profile_share_channel)`가 사용된다. | `P1-T3` | + +### 8.2 기존 동작 보존 + +| ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 | +|---|---|---|---|---| +| `REG-001` | 확정 | 다른 사람 채널의 follow, bell, 더보기 표시 정책은 유지한다. | `bindTitleBar()`의 non-owner follow/bell/more 조건이 기존과 동일하다. | `P1-GATE` | +| `REG-002` | 확정 | 더보기 BottomSheet의 차단, 유저 신고, 프로필 신고 동작은 유지한다. | 기존 callback 3개가 dismiss 후 동일하게 호출된다. | `P1-GATE` | + +## 9. 반응형 기능 범위 + +| 기능 | Android Phone | 비고 | +|---|---:|---| +| 내 채널 공유 버튼 | 전체 | 기존 title bar 안에 24dp 아이콘으로 배치 | +| 다른 사람 채널 공유 항목 | 전체 | 기존 BottomSheet vertical menu에 항목 추가 | + +## 10. UI/UX Expectations + +- 내 채널 공유 버튼은 기존 우측 title action container 안에서 `iv_more`와 같은 크기·정렬 정책을 따른다. +- 다른 사람 채널 BottomSheet의 `채널 공유` 항목은 기존 block/report 항목과 같은 typography, padding, 색상을 따른다. +- contentDescription은 기존 title action button 관례가 없으므로 기능 label을 연결할 수 있으면 `screen_user_profile_share_channel`을 사용한다. + +## 11. API 계약 + +- 신규 API 없음. +- OneLink 생성은 기존 `Utils.createOneLinkUrl(params = mapOf("af_dp" to "voiceon://", "deep_link_value" to "channel", "deep_link_sub5" to "$creatorId"))` 흐름과 동일하다. + +## 12. 보안과 데이터 취급 + +- 공유 텍스트에는 채널 닉네임과 공개 채널 링크만 포함한다. +- `BuildConfig` 값, token, signed URL, 개인정보성 내부 ID를 log, Toast, crash message에 노출하지 않는다. +- 내부 `creatorId`는 기존 공유 딥링크의 `deep_link_sub5` 값으로만 사용한다. + +## 13. 성능과 품질 요구사항 + +- 네트워크 요청 없이 로컬 OneLink URL 생성과 Android chooser 실행만 수행한다. +- 신규 dependency를 추가하지 않는다. +- 자동 테스트는 `app/src/test`의 source/local unit test만 계획한다. +- 전체 UI 계측 테스트와 스크린샷 QA는 사용자 명시 요청 전까지 실행하지 않는다. + +## 14. 성공 기준 + +### 14.1 기능 수용 기준 + +- [ ] 내 채널에서 우측 상단 공유 버튼이 표시된다. (`SHARE-001`, `P1-T1`) +- [ ] 내 채널 공유 버튼이 `ic_audio_content_share`를 사용한다. (`SHARE-002`, `P1-T1`) +- [ ] 다른 사람 채널 더보기 BottomSheet에 `채널 공유`가 표시된다. (`SHARE-003`, `P1-T2`) +- [ ] 두 진입점 모두 기존 프로필 공유와 같은 chooser payload를 사용한다. (`SHARE-004`, `P1-T3`) + +### 14.2 회귀 수용 기준 + +- [ ] non-owner follow, bell, more 표시 정책이 유지된다. +- [ ] 기존 block/report/profile report BottomSheet callback이 유지된다. +- [ ] `UserProfileActivity`, `UserProfileViewModel`은 수정되지 않는다. + +## 15. Open Questions + +| ID | 상태 | 결정 필요 사항 | 현재 권고 | 결정 주체 | 결정 기한/시점 | 영향 Goal | +|---|---|---|---|---|---|---| +| 없음 | 확정 | 추가 인터뷰 필요 항목 없음 | 현재 요구사항 그대로 구현 | 사용자 | 문서 작성 시점 | 없음 | + +## 16. 요구사항 추적표 + +| 요구사항 범위 | API Contract | 계획 Phase | Goal | 자동 검증 | 수동 검증 | +|---|---|---:|---|---|---| +| `SHARE-001~002` | 없음 | 1 | `P1-T1` | `CreatorChannelShareSourceTest` | 내 채널 title bar 확인 | +| `SHARE-003` | 없음 | 1 | `P1-T2` | `CreatorChannelShareSourceTest` | 타인 채널 BottomSheet 확인 | +| `SHARE-004`, `REG-001~002` | 없음 | 1 | `P1-T3`, `P1-GATE` | `CreatorChannelShareSourceTest` | Android chooser 실행 확인 | + +## 17. Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·계약·Goal | +|---|---|---|---|---|---| +| `2026-09-14` | `DEC-001` | 확정 | 내 채널은 우측 상단 직접 공유 버튼, 타인 채널은 더보기 BottomSheet 공유 항목으로 분기한다. | 사용자 요청 | `SHARE-001~003`, `P1-T1~T2` | +| `2026-09-14` | `DEC-002` | 확정 | 공유 실행은 `UserProfileActivity` 887~902줄의 Android chooser 흐름과 동일하게 사용한다. | 사용자 요청 및 기존 코드 확인 | `SHARE-004`, `P1-T3` | +| `2026-09-14` | `DEC-003` | 확정 | 레거시 프로필 파일은 수정하지 않고, 크리에이터 채널 v2 범위에서 기존 공유 방식만 재현한다. | 저장소 레거시 코드 사용 원칙 | `REG-002`, `P1-GATE` | + +## 18. 변경 관리 + +- 요구사항 변경 시 Decision Log에 정정 행을 추가한다. +- 구현 범위가 바뀌면 `plan-task.md` 체크리스트를 먼저 갱신한다. +- 기존 완료 체크박스와 검증 기록은 삭제하거나 덮어쓰지 않는다.