Files
sodalive-android/docs/20260914_크리에이터_채널_공유/plan-task.md
T

19 KiB

크리에이터 채널 공유 구현 계획

문서 항목 내용
상태 구현 완료
작성일 2026-09-14
요구사항 기준 docs/20260914_크리에이터_채널_공유/prd.md
API 기준 없음
현재 Phase Phase 1
현재 활성 Goal 없음

목표

크리에이터 채널 화면에서 내 채널은 우측 상단 공유 버튼으로, 다른 사람 채널은 더보기 BottomSheet의 채널 공유 항목으로 같은 채널 공유 chooser를 실행한다.

현재 상태

Phase 상태 완료 Task 활성/다음 Goal 차단 또는 남은 조건
1 완료 5/5 없음 실제 앱 수동 테스트는 사용자 확인 대기

범위

포함

  • 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-R1, 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가 구현·검증 기록으로 추적된다.
  • 레거시 프로필 파일 수정이 없다.

검증 방법

Task 1.4 후원 테스트 can 격리 회귀 수정

Goal 실행 P1-R1: CreatorChannelHomeViewModelTest와 CreatorChannelDonationViewModelTest가 공유 SharedPreferenceManager.can 상태에 영향받지 않도록 테스트 격리를 보강한다.

  • 시작 조건: ISSUE-001 재현 및 root cause 확인.
  • 완료 증거: SharedPreferenceManagerTest PASS, creator.channel.* 패키지 테스트 PASS.
  • 범위 밖: 후원 ViewModel production 동작 변경, 전체 DataStore reset, 레거시 파일 수정.

Files:

  • Modify: app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt
  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeViewModelTest.kt
  • Modify: app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationViewModelTest.kt
  • Test: app/src/test/java/kr/co/vividnext/sodalive/common/SharedPreferenceManagerTest.kt

Interfaces:

  • Consumes: SharedPreferenceManager.can, DataStore.edit

  • Produces: resetCanForTest(), setCanForTest(value) 테스트 전용 동기 helper

  • RED: SharedPreferenceManagerTest에 저장된 can 값을 테스트 전용 reset으로 초기화해야 한다는 회귀 테스트를 추가했다.

  • RED 확인: production helper 미구현 상태에서 focused test가 컴파일 실패하는 것을 확인하려 했으나 Gradle 초기 compile이 timeout됐다. 이후 helper 구현 전 계약 실패 조건은 resetForTest(clearPersisted = true) 미존재로 확인했다.

  • GREEN: 전체 DataStore를 지우지 않고 PREF_CAN 키만 동기적으로 제거·설정하는 resetCanForTest(), setCanForTest(value)를 추가했다. CreatorChannelHomeViewModelTest, CreatorChannelDonationViewModelTest의 can setup/teardown만 해당 helper로 교체했다.

  • GREEN 확인: ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.SharedPreferenceManagerTest" --rerun-tasks PASS, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*" PASS를 확인했다.

  • REFACTOR: 전체 DataStore clear 방식은 병렬 테스트의 token 상태까지 지워 NPE를 만들었으므로 제거하고, can 키 한정 동기 helper로 범위를 줄였다.

Phase 1 Gate

Goal 실행 P1-GATE: 크리에이터 채널 공유 진입점과 기존 더보기 동작 보존을 최종 판정한다.

  • 시작 조건: Phase 1의 모든 활성 Task goal 완료.
  • 완료 증거: 아래 명령과 Expected 통과, Progress에 실제 결과 누적.
  • 범위 밖: 실패와 무관한 UI 리팩터링, 신규 dependency, 계측 테스트 추가.
./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-R1 ISSUE-001 재현 아니요 SharedPreferenceManager.can 테스트 격리 root cause 재확인
5 P1-GATE Phase 1 Task 전체 아니요 실패 소유 Task의 회귀 수정 goal 생성
P1-T1 → P1-T2 → P1-T3 → P1-R1 → 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

| 2026-09-14 | DEC-003 | 확정 | 시각 QA와 스크린샷 검증은 수행하지 않고 실제 앱 수동 테스트로 확인한다. | 사용자 지시 | P1-GATE |

발견된 문제

ID 심각도 상태 발견 내용 영향 Goal 처리 계획
없음 Low 후보 없음 문서 작성 범위에서 차단 이슈 없음 없음 없음
ISSUE-001 Low 해결 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"가 CreatorChannelHomeViewModelTest의 SharedPreferenceManager.can assertion에서 1건 실패했다. 원인은 SharedPreferenceManager.can setter가 비동기 DataStore write를 수행하고, 테스트 간 persisted PREF_CAN 값이 재초기화 시점에 다시 읽힐 수 있는 격리성 문제였다. P1-R1, P1-GATE PREF_CAN만 동기 초기화·설정하는 테스트 전용 helper를 추가하고 후원 관련 테스트 setup/teardown에 적용했다.

최종 보고 형식

구현 결과: 완료한 Phase와 사용자 흐름을 적는다.

- 변경: 주요 파일과 동작을 적는다.
- 결정: 중요한 Decision Log ID와 내용을 적는다.
- 검증:
  - `실행 명령` — 성공/실패와 핵심 수치를 적는다.
  - `수동 검증` — 성공/실패/불가 사유를 적는다.
- 남은 항목: 외부 의존, 후속 범위 또는 없음을 적는다.
- 문서: 갱신한 PRD/API Contract/plan/review 링크를 적는다.

Verification Log

  • 2026-09-14: 문서 작성만 수행했다. 코드 구현, Gradle 빌드, 기능 테스트는 실행하지 않았다.
  • 2026-09-14: 구현 후 ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest" --rerun-tasks PASS, ./gradlew :app:mergeDebugResources PASS, ./gradlew :app:assembleDebug PASS, git diff --check PASS를 확인했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*"는 CreatorChannelHomeViewModelTest의 후원 can assertion 1건으로 실패했으나, 실패 후보 테스트 2개는 각각 단독 실행 PASS했다. 사용자가 시각 QA 대신 실제 앱 수동 테스트를 선택했으므로 수동 검증 체크박스는 사용자 확인 대기로 남겼다.
  • 2026-09-15: ISSUE-001 후속으로 SharedPreferenceManager.can 테스트 격리 문제를 수정했다. ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.common.SharedPreferenceManagerTest" --rerun-tasks PASS, ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*" PASS, git diff --check PASS를 확인했다. 공유 기능 focused test ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelShareSourceTest"도 PASS 상태를 재확인했다.