100 KiB
V2 공통 접근 가드와 도메인 액션 Plan/Task
Assumptions
- 작업 범위는
kr.co.vividnext.sodalive.v2하위 신규/기존 코드와 이를 조립하는AppDI.kt로 제한한다. - 화면별 최초 집계 API와 Response는 feature가 계속 소유할 수 있다.
- 공통화 대상은 최소 두 호출부에서 반복되거나 모든 화면에서 동일해야 하는 정책과 사용자 행동이다.
- 기존 UX, API, Intent extra, Activity result, 화면 새로고침 동작을 보존한다.
- 직접적인 하위 Activity 결과는
ActivityResult -> 명시적 도메인 결과 -> feature 단일 handler를 기본 전달 방식으로 사용한다. - 현재 화면 안에서 Action/Repository가 직접 반환하는 결과에는 불필요하게
ActivityResult를 도입하지 않고 같은 handler를 호출한다. SharedFlow/observer는 Activity 밖의 독립 소비자가 다수 존재하거나 백그라운드 변경 전파 요구가 확인될 때만 사용한다.- 레거시 기능은 수정하지 않고
v2adapter/wrapper에서 호출한다. - 구현은 Access부터 도메인별 Phase로 진행하며 각 Phase를 독립적으로 검증한다.
- 모든 도메인에 동일한 형식의 클래스를 강제하지 않고 실제 중복과 정책이 확인된 최소 단위만 추출한다.
Success Criteria
v2로그인/성인 접근 판단의 단일 소유자가 존재한다.- Activity와 Fragment가 동일한 Access 공개 진입점을 사용한다.
- 합의된 도메인별 반복 동작이 단일 Action 공개 진입점을 사용한다.
- Action 입력은 화면 전용 UI model/DTO가 아닌 ID, 값 또는 명시적 command다.
- 화면별 최초 query와 UI composition은 유지된다.
- Community mutation은 명시적 변경 결과를 반환하고 Creator Channel의 단일 composition handler가 Home/Community projection 갱신을 결정한다.
- Activity result를 사용하는 도메인 흐름은 raw result를 화면마다 해석하지 않고 명시적 결과와 단일 handler를 사용한다.
- feature 간 직접 Activity/Coordinator 의존은 공통 Action 또는 navigation 계약으로 대체된다.
- 각 Phase의 테스트, 컴파일, ktlint, whitespace 검증 결과가 문서에 누적된다.
Phase 0: 기준선과 Action 카탈로그 확정
-
Task 0.1:
v2구조와 중복 접근 구현 기준선 조사- 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/app/src/test/java/kr/co/vividnext/sodalive/v2/
- 확인 내용:
- 운영 Kotlin 258개, 약 25,294줄
- 테스트 Kotlin 127개, 약 23,809줄
SharedPreferenceManager직접 참조 29개 파일, 92곳- API 11개, Repository 11개, ViewModel 22개, Activity/Fragment 20개
- 검증 기준: 전체 재배치가 아닌 반복 동작별 점진 전환이 필요한 근거를 기록한다.
- 검증 기록:
- 2026-07-14:
find,wc,rg로v2운영/테스트 코드 규모와 직접 의존을 확인했다.widget64개는 UI 재사용 컴포넌트이므로 초기 도메인 Action 전환 대상에서 제외한다.
- 2026-07-14:
- 파일:
-
Task 0.2: 초기 Action 카탈로그 작성
- 파일:
docs/20260714_V2_공통_접근_가드와_도메인_액션/prd.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업: Access, Content, Live, Creator, Community, Chat의 초기 공통 동작과 호출 화면을 기록한다.
- 검증 기준: 화면별 집계 query와 공통 action을 구분하고, FanTalk/Donation/Schedule은 실제 재사용 수요 확인 전 추출 대상에서 제외한다.
- 검증 기록:
- 2026-07-14: 기존 호출부 조사 결과 Access, 오디오/시리즈 진입, Live 상세/입장, Creator Channel 진입, Community 상세, Chat/DM 진입을 초기 후보로 확정했다.
- 파일:
-
Task 0.3: 구현 직전 호출부 및 동작 차이 상세 목록 확정
- 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/app/src/main/java/kr/co/vividnext/sodalive/v2/live/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt(호출부 확인만 수행하고 수정하지 않음)docs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업: 각 Action별 호출 함수, 접근 조건, Intent extra, Activity result, callback/refresh 차이를 기록한다.
- 검증 기준:
- 공통 계약으로 보존할 동작과 화면에 남길 후처리가 구분된다.
- Activity result 흐름은 adapter에서 만들 명시적 결과와 feature 단일 handler 후보가 기록된다.
- observer가 필요한 흐름은 Activity 밖의 소비자 수, lifecycle, replay 요구사항이 함께 기록된다.
- 확인 결과:
- Access:
HomeMainFragment,ContentMainFragment,ChatMainFragment는 기존ensureMainV2NavigationAllowed를 사용했고,CreatorChannelActivity,HomeOnAirLiveActivity는 자체ensureLoginAndAdultAuth를 중복 구현하고 있었다. - Access:
MainV2Activity는 chat tab, audio notification route처럼 로그인 차단 UX가 필요한 navigation guard와 push token/member/deeplink 초기화처럼 부수효과 없이 허용 여부만 확인하는 흐름이 섞여 있었다. Phase 1에서는 두 용도를ensureV2Access(AccessRequirement.Login)와isV2AccessAllowed(AccessRequirement.Login)로 분리해 기존 route 처리 순서와 invalid audio content id 반환 계약을 유지한다. - Content:
ContentMainFragment의 오디오/시리즈 상세 진입은 ID 검사 후 판단 가능한 성인 여부를 guard에 전달하며, banner/overview/전체 탭 이동은 login guard만 필요하다. - Live:
HomeMainFragment와HomeOnAirLiveActivity는 로그인 후 room detail을 조회하고, detail의isAdult로 성인 Access를 다시 검사한다. Creator Channel 현재 라이브도 동일 입장 흐름을 사용한다. - Creator/Community:
CreatorChannelActivity는 Activity 자체 진입 로그인 guard와 Community/FanTalk/Live ActivityResult refresh를 소유한다. Phase 1에서는 result adapter를 만들지 않고 기존 launcher/refresh를 유지한다. - Chat: title bar 및 Home following chat은 login guard 후 기존 AI/DM intent helper를 사용한다. DM room result adapter나 observer 변경은 Phase 5 대상으로 유지한다.
- Legacy MyPage:
MyPageFragment.showLoginActivity는 실행 Activity가MainV2Activity이면 공개showLoginActivity()를 호출하므로 해당 호환 진입점을 유지해야 한다.
- Access:
- 파일:
상세 호출부 및 계약
| Action 후보 | 호출부 | Access 조건 | Intent extra / 입력 | Result / callback 차이 | Phase 1 처리 |
|---|---|---|---|---|---|
| Login/Adult Access | HomeMainFragment.openWithLoginGuard, openFollowingTab, title bar cash/search/bell, ContentMainFragment title/overview/banner/detail, ChatMainFragment title bar, CreatorChannelActivity community/FanTalk/live, HomeOnAirLiveActivity live entry |
Login 또는 forAdultContent(isAdult) |
화면별 기존 Activity/Intent helper 그대로 사용 | 화면별 후처리는 호출부에 유지 | ensureV2Access로 전환하고 정책 판단만 공통화 |
| MainV2 chat tab | MainV2Activity.selectChatTabWithLoginGuard, openChatTab, initial chat filter |
Login | EXTRA_CHAT_FILTER는 기존 consume/open 흐름 유지 |
탭 전환은 허용 시에만 viewModel.clickTab(CHAT) |
ensureV2Access(Login) 사용, legacy showLoginActivity()는 guard 위임만 유지 |
| Legacy MyPage login bridge | MyPageFragment.showLoginActivity -> MainV2Activity.showLoginActivity |
Login | MainV2Activity.intent.extras |
레거시 Activity 타입 분기와 로그인 화면 이동 계약 유지 | MainV2Activity.showLoginActivity()를 유지하고 ensureV2Access(Login)에 위임 |
| MainV2 deeplink/audio notification | onCreate, onNewIntent, handleAudioNotificationRoute, executeDeeplink |
Login 허용 여부 확인 또는 Login guard | EXTRA_DATA, deep_link, EXTRA_AUDIO_NOTIFICATION_ROUTE, EXTRA_AUDIO_CONTENT_ID |
audio route가 먼저 소비되고 invalid audio content id는 false 반환해 기존 후속 처리 가능 |
route 순서와 반환 계약 보존, side-effect 없는 분기는 isV2AccessAllowed(Login) 사용 |
| Content 상세/전체/overview | ContentMainFragment.openAudioContentDetail, openSeriesDetail, openContentOverview, openContentAllTab |
Login 또는 콘텐츠 성인 여부에 따른 AdultContent | Constants.EXTRA_AUDIO_CONTENT_ID, series id, ContentOverviewType, MainContentAllType, ContentSort |
Activity result 없음, 전체 탭 이동은 MainV2Activity.openContentAllTab 호출 |
Phase 1에서는 guard만 공통화, Content Action 계약은 Phase 2에서 구현 |
| Live entry | HomeMainFragment.onLiveClick, HomeOnAirLiveActivity.enterLiveRoom, CreatorChannelActivity.onCreatorChannelCurrentLiveClicked |
Login 후 room detail의 isAdult로 AdultContent |
room id/live id, room detail | 기존 liveCoordinator.enterLiveRoom 및 on-air 목록 refresh/상태는 호출부에 유지 |
Phase 1에서는 중복 adult guard 제거, Live Action은 Phase 3 |
| Creator / Community | CreatorChannelActivity.onCreatorChannelCommunityPostClicked, Home creator/profile/community 진입 |
Login | creator id, post id, existing newIntent |
Creator Channel의 Home/Community/FanTalk refresh 조합과 launcher는 Activity 소유 유지 | Phase 1에서는 login guard만 공통화, result adapter는 Phase 4 |
| Chat / DM | HomeMainFragment.openFollowingChat, title bar search/cash, MainV2Activity chat route |
Login | AI room id, DM room id, existing chat intent helpers | DM room result adapter/observer 없음, 기존 Activity 전환만 수행 | Phase 1에서는 login guard만 공통화, Chat Action은 Phase 5 |
observer 판단
- Phase 1 Access는 one-shot UI navigation/action 차단이므로 전역 observer나 EventBus를 추가하지 않는다.
- ActivityResult/refresh가 있는 Creator Channel Community/FanTalk는 기존 launcher와 Activity 내부 composition handler 후보를 Phase 4에서 정리한다.
- Live room entry는 detail 조회 후 접근 판단이 필요하므로 repository/result observer가 아니라 호출부 callback 조합을 유지하고 Phase 3에서 Action 계약을 분리한다.
검증 기록
- 2026-07-14:
rg와explore조사로 Access, Content, Live, Creator, Community, Chat 후보의 호출부와 차이를 확정했다. Phase 1 구현 범위는 Access로 제한하고, Content 이후 도메인 Action은 카탈로그 상태로 유지한다. - 2026-07-14: 리뷰 보완으로
MainV2Activity의 직접isLoggedIn()/showLoginActivity()소유 여부를 재확인했고, 로그인 UX 실행이 필요한 경로와 단순 허용 여부 확인 경로를 분리해 공통 Access 정책으로 이관하기로 확정했다. - 2026-07-14: 리뷰 게이트 보완으로 Action 후보별 호출 함수, Access 조건, Intent 입력, Result/callback 차이, observer 판단을 표로 추가해 Task 0.3 검증 기준을 충족하도록 문서를 보강했다.
- 2026-07-14: Phase 0/1 리뷰 답변에 따라 레거시
MyPageFragment에서MainV2Activity.showLoginActivity()를 호출하는 호환 경로를 상세 호출부 표에 추가했다. 레거시 파일은 수정하지 않았다.
Phase 1: V2 공통 Access 정책과 실행기
-
Task 1.1: Access 판단 단위 테스트 작성
- 생성 예정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/access/AccessPolicyTest.kt
- 검증 대상:
- 로그인 필요 요구사항의 로그인/미로그인 결과
- 성인 콘텐츠 요구사항의 미로그인 결과
- 국내 미인증 사용자의 본인인증 필요 결과
- 국외 사용자의 본인인증 생략
- 성인 콘텐츠 표시 설정 비활성 결과
- 모든 조건 충족 시 허용 결과
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.AccessPolicyTest" - 기대 결과: 구현 전 RED, 구현 후 GREEN.
- 검증 기록:
- 2026-07-14: 테스트를 먼저 추가한 뒤
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.AccessPolicyTest"를 실행해AccessPolicy,AccessRequirement,AccessDecision,AccessState미정의 컴파일 오류로 RED를 확인했다.
- 2026-07-14: 테스트를 먼저 추가한 뒤
- 생성 예정 파일:
-
Task 1.2: Access 입력·출력 계약과 순수 정책 구현
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessRequirement.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessDecision.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessState.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/access/AccessPolicy.kt
- 작업: 로그인, 국가/본인인증, 성인 콘텐츠 표시 설정을 명시적 입력과 결과로 판단한다.
- 검증 기준: Android, Activity, Fragment, Dialog, Retrofit 의존 없이 Task 1.1 테스트가 통과한다.
- 검증 기록:
- 2026-07-14:
AccessRequirement,AccessDecision,AccessState,AccessPolicy를 추가하고 같은 단일 테스트 명령을 재실행해 GREEN을 확인했다.
- 2026-07-14:
- 생성 예정 파일:
-
Task 1.3: Activity/Fragment 공용 Access 실행기 구현
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/access/V2AccessGuard.kt
- 수정 가능 파일:
app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
- 작업:
AccessDecision을 로그인, 본인인증, 콘텐츠 설정 UX로 연결한다.- 기존
LoginActivity,V2ModalDialog,Auth.auth,MyPageViewModel.authVerify,ContentSettingsActivity를 재사용한다. - Activity와 Fragment에서 같은 공개 함수를 호출할 수 있게 한다.
- 검증 기준:
Allowed에서만 원래 action을 한 번 실행한다.- 거부 결과에서 원래 action을 실행하지 않는다.
- 기존 Intent flag/extra와 인증 완료 후 흐름을 유지한다.
- 검증 기록:
- 2026-07-14:
V2AccessGuard를 추가해 Activity/Fragment 공용ensureV2Access를 제공하고, 기존LoginActivityextra/flag,V2ModalDialog,Auth.auth,MyPageViewModel.authVerify,SplashActivity,ContentSettingsActivityextra 계약을 source test로 고정했다. - 2026-07-14:
MainV2Activity의 초기화/조건부 실행처럼 로그인 UX를 띄우지 않아야 하는 경로를 위해 같은AccessPolicy와AccessStateProvider를 사용하는isV2AccessAllowed를 추가했다. - 2026-07-14: 리뷰 보완으로 기존
MainV2Activity.isLoggedIn()의token.isNotBlank() && token.length > 10기준을AccessPolicy에 보존하고, 10자/11자/공백 토큰 경계 테스트를 추가했다. - 2026-07-14: 레거시
MyPageFragment본문을 수정하지 않고 기존MainV2Activity.showLoginActivity()호출 계약을 유지하기 위해MainV2Activity에 얇은 호환 메서드를 남기고 내부에서ensureV2Access(AccessRequirement.Login)을 호출하게 했다. - 2026-07-14: 사용자 확인 결과 현재
token.length > 10기준은 실제 토큰 계약에서 동작 변경을 만들지 않으므로 추가 코드 변경 없이 유지한다.
- 2026-07-14:
- 생성 예정 파일:
-
Task 1.4: 기존 Access 구현을 공통 실행기로 전환
- 수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
- 작업:
- 기존 호출부를
V2AccessGuard로 전환한다. - 화면 진입 guard와 클릭 action guard의 기존 종료/후처리 차이를 보존한다.
- 모든 호출부 전환 후 중복
ensureLoginAndAdultAuth구현을 제거한다.
- 기존 호출부를
- 검증 기준:
CreatorChannelActivity,HomeOnAirLiveActivity에 별도 로그인/성인 판단 함수가 남지 않는다.- 대상 클릭 handler에 직접 token 조건문이 남지 않는다.
- 검증 기록:
- 2026-07-14:
HomeMainFragment,ContentMainFragment,ChatMainFragment,CreatorChannelActivity,HomeOnAirLiveActivity호출부를ensureV2Access와AccessRequirement로 전환했다.MainV2LoginGuard.kt와 Activity별ensureLoginAndAdultAuth구현을 제거했다. - 2026-07-14:
MainV2Activity의 chat tab, audio notification route는ensureV2Access(AccessRequirement.Login)로 전환하고, deeplink 실행 전 조건, push token update, member info 조회 등은isV2AccessAllowed(AccessRequirement.Login)로 전환했다. Activity 내부 직접isLoggedIn()구현은 제거했고, 기존 외부 호출 호환용showLoginActivity()는 공통 guard 위임만 수행한다. - 2026-07-14:
rg -n "ensureMainV2NavigationAllowed|ensureLoginAndAdultAuth" app/src/main/java/kr/co/vividnext/sodalive/v2 app/src/test/java/kr/co/vividnext/sodalive/v2 --glob '*.kt'결과 production 잔존은 없고, source test의 제거 확인 assertion만 남았다. - 2026-07-14: 대상 UI 파일의
SharedPreferenceManager.(token|countryCode|isAuth|isAdultContentVisible)검색 결과 Creator Channel의 API auth header 생성 함수만 남았고, 접근 정책 직접 판단은 제거됐다.
- 2026-07-14:
- 수정 예정 파일:
-
Task 1.5: 기존 Access source test를 새 계약에 맞게 전환
- 수정 예정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2ActivitySourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2AudioNotificationRouteSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainTitleBarActionSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentLoginGuardSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainTitleBarActionSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainTitleBarActionSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.kt
- 작업: 이전 함수명 고정 assertion을 새 공통 Access 사용 및 기존 UX 보존 검증으로 최소 수정한다.
- 검증 기준: 정책은
AccessPolicyTest, 호출 연결은 필요한 source test가 담당한다. - 검증 기록:
- 2026-07-14:
MainV2LoginGuardSourceTest, Home/Content/Chat title bar source test, Home/Content login guard source test,CreatorChannelActivitySourceTest의 Access 관련 assertion을 새V2AccessGuard계약 기준으로 전환했다. - 2026-07-14:
MainV2ActivitySourceTest와MainV2AudioNotificationRouteSourceTest를 새ensureV2Access/isV2AccessAllowed경계 기준으로 갱신하고,MainV2Activity에 직접isLoggedIn()판단 함수가 남지 않음을 고정했다. - 2026-07-14:
MainV2LoginGuardSourceTest에MainV2Activity.showLoginActivity()가 기존 호출명은 유지하되ensureV2Access(AccessRequirement.Login)으로 위임하는지 확인하는 assertion을 추가했다.
- 2026-07-14:
- 수정 예정 파일:
-
Task 1.6: Access Phase 검증 및 문서 반영
- 파일:
docs/agent-guides/build-test-style.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck --rerun-tasks git diff --check HEAD - 기대 결과: 신규 Access 테스트와 영향받은 기존 테스트, 컴파일, whitespace 검증이 통과한다.
ktlintCheck는 전체 기준선을 재실행하고, 저장소 기존 위반으로 실패하면 Phase 1 변경 파일에 신규 위반이 없음을 완료 gate로 확인한다. - 검증 기록:
- 2026-07-14: Access 관련 좁은 회귀 명령
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.*" --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.현재 라이브 카드는 동일한 enterLiveRoom 플로우로 입장한다" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.커뮤니티 게시물 클릭 source는 상세 Activity newIntent로 진입하고 invalid postId를 무시한다" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.FanTalk fixed plus source는 Activity overlay와 write result refresh 계약을 연결한다" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.FanTalk item click source는 owner gate와 상세 ActivityResult refresh를 연결한다"가BUILD SUCCESSFUL로 통과했다. - 2026-07-14: 더 넓은 관련 테스트 묶음은 497개 중
CreatorChannelActivitySourceTest > 팬Talk 섹션은 Figma ListComment layout과 bind로 렌더링한다1개가@drawable/ic_chevron_down_whitesource assertion으로 실패했다. Access 변경 파일과 무관한 기존 FanTalk layout assertion으로 확인해 별도 관찰로 남긴다. - 2026-07-14:
./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL로 통과했다. - 2026-07-14:
./gradlew :app:ktlintCheck는 이번 변경과 무관한app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt:118-120기존 max line length/argument wrapping 위반으로 실패했다. 해당 파일에는 이번 diff가 없음을git diff -- app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.kt로 확인했다. - 2026-07-14:
git diff --check는 whitespace 오류 없이 통과했다. - 2026-07-14:
MainV2LoginGuardSourceTest의 신규 adapter assertion line length를 수정한 뒤./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.*" && ./gradlew :app:compileDebugKotlin && ./gradlew :app:ktlintCheck && git diff --check를 재실행해 모두BUILD SUCCESSFUL또는 무출력 통과를 확인했다. - 2026-07-14: 리뷰 게이트에서
token.isNotBlank() && token.length > 10보존과mypageadapter 제거가 차단 이슈로 확인되어,AccessPolicy로그인 기준과MainV2Activity.showLoginActivity()호환 메서드 위임 방식으로 보완했다. - 2026-07-14: 보완 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.*" && ./gradlew :app:compileDebugKotlin && ./gradlew :app:ktlintCheck && git diff --check를 재실행해 모두BUILD SUCCESSFUL또는 무출력 통과를 확인했다. - 2026-07-14: 추가 리뷰에서 11자 이상 공백 토큰이 허용되는 회귀를 확인해
AccessPolicy에isNotBlank()조건을 복원하고 공백 토큰 회귀 테스트와 PRD 문구를 보강했다. - 2026-07-14: Kotlin LSP 진단은 환경에
kotlin-lsp가 설치되어 있지 않아 실행할 수 없었고, Gradle compile/test로 대체 검증했다.
- 2026-07-14: Access 관련 좁은 회귀 명령
- 파일:
-
Task 1.7: 리뷰 보완으로 Access 실행 계약과 source test 범위 강화
- 생성 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/access/V2AccessGuardTest.kt
- 수정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/access/V2AccessGuard.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktdocs/agent-guides/build-test-style.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
AccessDecision실행 분기를 Android 비의존 함수로 분리해Allowedcallback과 거부 UX callback 실행 횟수를 실제 입력·출력으로 검증한다.- Creator Channel 커뮤니티 게시물 클릭 source test가 Activity 전체가 아닌 해당 handler 본문에서 guard와 launcher 순서를 확인하게 한다.
- Task 1.4와 Task 1.5의 실제 변경 파일 목록을 현재 diff와 일치시킨다.
- 검증 기준:
Allowed는 원래 action을 정확히 한 번 실행하고 거부 UX callback은 실행하지 않는다.- 모든 거부 결과는 원래 action을 실행하지 않고 대응 UX callback만 정확히 한 번 실행한다.
- 커뮤니티 게시물 handler 밖의 다른 로그인 guard로 source test가 통과하지 않는다.
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.V2AccessGuardTest" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.커뮤니티 게시물 클릭 source는 상세 Activity newIntent로 진입하고 invalid postId를 무시한다" - 기대 결과: 실행 계약 테스트는 구현 전 RED, 구현 후 GREEN이고 강화된 source test가 통과한다.
- 검증 기록:
- 2026-07-14:
V2AccessGuardTest를 먼저 추가하고 단일 테스트를 실행해executeAccessDecision미정의 컴파일 오류로 RED를 확인했다. - 2026-07-14: 기존
V2AccessGuard.handleDecision의 분기를 Android 비의존executeAccessDecision으로 추출한 뒤 같은 단일 테스트가BUILD SUCCESSFUL로 전환되는 것을 확인했다. 네 가지AccessDecision별 반환값과 실행 callback이 정확히 하나인지 검증한다. - 2026-07-14: Creator Channel 커뮤니티 handler source에서 guard 문자열을 일시 제거한 변이 입력으로 대상 테스트가 실패하는 것을 확인한 뒤, 실제 handler source 검사로 복원해
BUILD SUCCESSFUL을 확인했다. - 2026-07-14: Access, Main, On-air 전체와 변경 영향 Creator Channel 4개 테스트를 묶어 재실행해
BUILD SUCCESSFUL을 확인했다. - 2026-07-14:
./gradlew :app:compileDebugKotlin,./gradlew :app:ktlintCheck,./gradlew tasks --all을 실행해 모두BUILD SUCCESSFUL을 확인했다. ktlint에는 기존.editorconfig의disabled_rules속성 사용에 대한 deprecation warning이 남아 있다. - 2026-07-14:
git diff --check HEAD를 실행해 staged와 unstaged를 합친 전체 변경에 whitespace 오류가 없음을 확인했다. - 2026-07-14: 사용자 확인에 따라 현재 layout에 존재하지 않아 기존 테스트를 실패시키던
ic_chevron_down_whitesource assertion은 수동 삭제한 상태를 유지한다. Phase 1 운영 코드 변경과는 무관한 기존 테스트 정리다.
- 2026-07-14:
- 생성 파일:
-
Task 1.8: 코드 리뷰 후 ktlint 재현성 보완
- 수정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeFollowingFragmentSourceTest.ktdocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
- 캐시를 배제한
ktlintCheck에서 재현된 기존 source test 3줄의 max line length와 argument wrapping 위반만 수정한다. - 기존 인증 문자열과 테스트 의미는 변경하지 않는다.
- 캐시를 배제한
- 검증 기준:
./gradlew :app:ktlintCheck --rerun-tasks로 전체 기준선을 재검사하고, Phase 1 변경 파일에 신규 위반이 없음을 보고서에서 확인한다.- 수정한 source test와 Access 관련 회귀 테스트가 통과한다.
git diff --check HEAD가 통과한다.
- RED 검증 기록:
- 2026-07-14:
./gradlew :app:ktlintCheck --rerun-tasks를 실행해HomeFollowingFragmentSourceTest.kt:118-120의 max line length와 argument wrapping 위반으로BUILD FAILED를 재현했다. 해당 파일은 기존 Phase 1 diff에 없었으므로, 이전 통과 기록은 Gradle up-to-date 캐시에 의해 실제 전체 린트를 재실행하지 않은 결과였다.
- 2026-07-14:
- GREEN 검증 기록:
- 2026-07-14: 세 문자열 assertion만 ktlint 규칙에 맞게 줄바꿈하고
HomeFollowingFragmentSourceTest를--rerun-tasks로 실행해BUILD SUCCESSFUL을 확인했다. - 2026-07-14: 수정 후
./gradlew :app:ktlintCheck --rerun-tasks를 재실행한 결과ktlintTestSourceSetCheck보고서는 0건이 되었고 Phase 1 변경 파일에서 위반이 검출되지 않았다. 전체 명령은 Phase 1 범위 밖 파일의 기존 ktlint 위반으로 실패했으며, 레거시 운영 코드 위반과 함께app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ui/ContentCommentedAudioAdapter.kt:29의 기존 V2 위반 1건도 포함되므로 전체 통과가 아닌 기준선 제약으로 기록한다. - 2026-07-14:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.home.HomeFollowingFragmentSourceTest" --rerun-tasks를 실행해 Access 회귀와 수정한 source test가BUILD SUCCESSFUL로 통과함을 확인했다. - 2026-07-14:
git diff --check HEAD는 whitespace 오류 없이 통과했다.
- 2026-07-14: 세 문자열 assertion만 ktlint 규칙에 맞게 줄바꿈하고
- 수정 파일:
-
Task 1.9: 코드 리뷰 후 FanTalk source test와 ktlint gate 정정
- 수정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktdocs/20260714_V2_공통_접근_가드와_도메인_액션/prd.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
- FanTalk write와 item click source test가 Activity 전체가 아닌 해당 함수 본문에서 login guard와 launcher 순서를 확인하게 한다.
- PRD의 ktlint 완료 기준을 현재 저장소 기준선에 맞춰
ktlintCheck전체 재실행과 Phase 변경 파일 신규 위반 없음으로 명확히 한다. - 기존 ktlint 실패 기록에서 V2 파일 위반 1건을 레거시 위반으로 잘못 분류한 표현을 정정한다.
- 검증 기준:
- FanTalk write/item click handler 밖의 다른
ensureV2Access문자열로 source test가 통과하지 않는다. - Phase 1 완료 판정은 전체 ktlint 통과로 오인되지 않고 기준선 제약과 변경 파일 신규 위반 여부를 함께 기록한다.
- 관련 source test와 whitespace 검증이 통과한다.
- FanTalk write/item click handler 밖의 다른
- 검증 기록:
- 2026-07-14:
openFanTalkWrite와onCreatorChannelFanTalkItemClicked본문만sourceSection으로 추출해 guard, launcher, 실행 순서를 검증하도록 source test를 강화했다. - 2026-07-14:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.FanTalk fixed plus source는 Activity overlay와 write result refresh 계약을 연결한다" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest.FanTalk item click source는 owner gate와 상세 ActivityResult refresh를 연결한다" --rerun-tasks를 실행해BUILD SUCCESSFUL을 확인했다. - 2026-07-14:
./gradlew :app:ktlintCheck --rerun-tasks는ktlintMainSourceSetCheck기준선 위반으로 실패했다. 보고서에는 기존 main source 위반 551줄이 남아 있고, 그중app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ui/ContentCommentedAudioAdapter.kt:29의 V2 max line length 위반 1건이 포함된다. - 2026-07-14:
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL로 통과해 이번에 수정한 source test 파일에는 신규 ktlint 위반이 없음을 확인했다. - 2026-07-14:
git diff --check HEAD는 whitespace 오류 없이 통과했다. - 2026-07-14: 리뷰어 게이트에서 P2/P3 반영, assertion 약화 없음, 문서 모순 없음에 대해 조건부 없는
PASS를 확인했다.
- 2026-07-14:
- 수정 파일:
-
Task 1.10: Phase 0/1 재리뷰 결과 반영
- 수정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.ktdocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
V2AccessGuard.handleDecision이 각 거부 결과를 로그인, 본인인증, 콘텐츠 설정 UX callback에 연결하는 실제 결선을 source assertion으로 고정한다.- Task 1.6의 ktlint 기대 결과를 PRD의 최종 gate와 일치시킨다.
- 검증 기준:
- 로그인, 본인인증, 콘텐츠 설정 callback 결선이 서로 바뀌면 source test가 실패한다.
ktlintCheck는 전체 기준선을 재실행하고, 기존 위반으로 실패하면 Phase 1 변경 파일의 신규 위반이 없음을 확인한다.- 관련 회귀 테스트, 컴파일, test source ktlint, whitespace 검증이 통과한다.
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest" --rerun-tasks ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck --rerun-tasks ./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks git diff --check HEAD - 기대 결과: 결선 source test, 컴파일, test source ktlint, whitespace 검증이 통과하고 전체 ktlint 기준선 제약이 기존 기록과 일치한다.
- 검증 기록:
- 2026-07-14:
handleDecision본문만 추출해 로그인, 본인인증, 콘텐츠 설정AccessDecision이 각각startLogin,showAuthDialog,startContentSettingscallback에 연결되는지 검증하도록 source test를 강화했다. - 2026-07-14:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest" --rerun-tasks를 실행해 테스트 1건이 실패 없이 통과하고BUILD SUCCESSFUL임을 확인했다. - 2026-07-14: 위 회귀 테스트에서 production/test source를 새로 컴파일했으며, 별도
./gradlew :app:compileDebugKotlin도BUILD SUCCESSFUL로 통과했다. - 2026-07-14:
./gradlew :app:ktlintCheck --rerun-tasks는 기존 main source 기준선 위반 551줄로 실패했다. Phase 1 변경 main source의 신규 위반은 0건이고, V2 위반 1건은 변경하지 않은ContentCommentedAudioAdapter.kt:29의 기존 max line length 위반이다. - 2026-07-14:
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL로 통과했으며 test source 보고서 위반은 0건이다. - 2026-07-14:
./gradlew tasks --all은BUILD SUCCESSFUL로 통과해 문서 변경 후 Gradle task 구성을 확인했다. - 2026-07-14:
git diff --check HEAD는 whitespace 오류 없이 통과했다.
- 2026-07-14:
- 수정 파일:
-
Task 1.11: Phase 0/1 리뷰의 Activity별 Access 결선 테스트 보강
- 수정 파일:
app/src/test/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveEntryPolicyTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivitySourceTest.ktdocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
- On-air 라이브 입장이 로그인 가드, room detail 조회, 성인 콘텐츠 가드, 기존 입장 함수 순서로 실행되는지 함수 본문 source로 검증한다.
- Creator Channel 화면 진입이 유효한 creator ID 확인 후 로그인 가드를 거치며, 거부 시 화면 조립 전에 종료되는지 검증한다.
- Creator Channel AI 채팅이 성인 콘텐츠 가드 통과 후에만 채팅방 생성을 요청하는지 클릭 listener 범위에서 검증한다.
- 검증 기준:
- 각 함수 밖의 다른
ensureV2Access문자열로 테스트가 통과하지 않는다. - 각 가드 문자열을 제거한 변이 source에서는 대응 테스트가 실패한다.
- production 코드는 변경하지 않고 관련 source test와 기존 Access 회귀 테스트가 통과한다.
- 각 함수 밖의 다른
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.onair.HomeOnAirLiveEntryPolicyTest" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --rerun-tasks ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.*" --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --rerun-tasks ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks git diff --check HEAD - 기대 결과: 변이 source RED 확인 후 실제 source 기준 대상 테스트와 관련 회귀 테스트, 컴파일, test source ktlint, whitespace 검증이 통과한다.
- RED 검증 기록:
- 2026-07-14: 세 결선 테스트에서 각 대상 가드 문자열을 제거한 변이 source를 사용해 실행한 결과, On-air 로그인/성인 결선 1건과 Creator Channel 진입/AI 채팅 결선 2건이 각각 실패하고
BUILD FAILED가 발생함을 확인했다.
- 2026-07-14: 세 결선 테스트에서 각 대상 가드 문자열을 제거한 변이 source를 사용해 실행한 결과, On-air 로그인/성인 결선 1건과 Creator Channel 진입/AI 채팅 결선 2건이 각각 실패하고
- GREEN 검증 기록:
- 2026-07-14: 변이를 제거하고 실제 production source를 대상으로 신규 결선 테스트 3건을
--rerun-tasks로 실행해 모두 통과하고BUILD SUCCESSFUL임을 확인했다. - 2026-07-14:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.access.*" --tests "kr.co.vividnext.sodalive.v2.main.*" --tests "kr.co.vividnext.sodalive.v2.live.onair.*" --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest" --rerun-tasks를 실행해 관련 회귀 테스트 503건이 실패, 오류, 스킵 없이 통과했다. - 2026-07-14:
./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL로 통과했다. - 2026-07-14:
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL로 통과해 test source 신규 위반이 없음을 확인했다. - 2026-07-14:
./gradlew tasks --all은BUILD SUCCESSFUL로 통과해 문서 변경 후 Gradle task 구성을 확인했다. - 2026-07-14:
git diff --check HEAD는 whitespace 오류 없이 통과했다. - 2026-07-14: Task 1.11에서는 production 코드를 추가 변경하지 않고 기존 source test 클래스 두 곳과 계획 문서만 보강했다.
- 2026-07-14: 변이를 제거하고 실제 production source를 대상으로 신규 결선 테스트 3건을
- 수정 파일:
Phase 2: Content 공통 Action
-
Task 2.1: 오디오/시리즈 진입 호출부와 계약 확정
- 확인 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업: ID, 성인 여부, Activity result, route source, 기존 extra 차이를 목록화하고 Action 계약을 확정한다.
- 검증 기준:
- 화면 전용 UI model을 받지 않는 최소 command/result 계약이 정해진다.
- Activity result 또는 호출 화면 후처리가 있는 경로는 Content result adapter와 단일 handler 필요 여부가 결정된다.
- 확인 결과:
- 공개 입력은
audioContentId또는seriesId와 호출부가 이미 알고 있는 성인 콘텐츠 여부만 받는AudioDetail/SeriesDetailcommand로 제한한다. Home/Content/Creator 전용 UI model과 Response는 Action에 전달하지 않는다. - Action 결과는 유효하지 않은 ID의
Ignored, Access가 거부된Blocked, 허용된 오디오/시리즈 navigation 결과로 구분한다.ContentActionHandler는 허용 결과만 기존Constants.EXTRA_AUDIO_CONTENT_ID또는Constants.EXTRA_SERIES_ID로 변환한다. - Home banner의 series와 최근 활동의 audio는 성인 여부를 제공하지 않으므로 기존처럼
AccessRequirement.Login을 사용한다. Home의 event/creator/link와 community route는 Content Action 범위에 포함하지 않는다. - Content 추천 카드와 전체 탭은
showAdultBadge를 알고 있는 경우 이를 전달한다. 댓글/랭킹/원작 series/banner처럼 성인 여부가 없는 경로는 로그인만 확인한다. - Content Overview audio, Creator Channel Home audio, Audio tab, Live replay, Series tab은 성인 여부를 보유하므로 command에 전달한다. Creator Channel schedule audio/live replay와 Home series는 성인 여부가 없어 로그인만 확인한다.
- 상세 진입은 모두 일반
startActivity이며ActivityResultLauncher, route source extra, 호출 화면 후처리가 없다. 따라서 별도 Content result adapter나 feature composition handler를 추가하지 않는다. MainV2Activity의 audio notification/deeplink/mini player와 series deeplink는 Phase 1에서 route 소비 순서와 boolean 반환 계약을 별도로 고정한 시스템 route이므로, PRD에 합의된 Home/Content/Content Overview/Creator Channel 전환 범위에는 포함하지 않는다.
- 공개 입력은
- 검증 기록:
- 2026-07-14:
rg와 대상 source 확인으로 V2 상세 Activity 직접 참조, ID/extra, 성인 여부 보유 여부, Activity result 사용 여부를 대조해 위 command/result 계약과 전환 범위를 확정했다. - 2026-07-14: 격리 worktree에서 변경 전
./gradlew :app:testDebugUnitTest를 실행해 전체 debug 단위 테스트가BUILD SUCCESSFUL로 통과하는 기준선을 확인했다. 기존 dependency/manifest/Kotlin deprecation 경고는 있었지만 실패는 없었다.
- 2026-07-14:
- 확인 예정 파일:
-
Task 2.2: Content Action 순수 계약 테스트 및 구현
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/content/action/ContentAction.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/content/action/ContentActionCommand.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/content/action/ContentActionTest.kt
- 검증 대상: invalid ID, audio/series route, 성인 Access 요구사항 전달, 허용/차단 결과.
- 검증 기준: feature UI model과 DTO 의존 없이 입력·출력이 테스트된다.
- RED 검증 기록:
- 2026-07-14:
ContentActionTest를 먼저 추가하고./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.content.action.ContentActionTest"를 실행해ContentAction,ContentActionCommand,ContentActionResult,ContentActionHandler미정의 컴파일 오류와BUILD FAILED를 확인했다.
- 2026-07-14:
- GREEN 검증 기록:
- 2026-07-14:
ContentActionCommand,ContentAction,ContentActionResult,ContentActionHandler를 최소 구현하고 같은 단일 테스트 명령을 재실행해 7개 테스트와 빌드가 통과했다. Action은 ID/성인 여부와 Access 함수만 입력받고 feature UI model, DTO, Android UI에 의존하지 않는다.
- 2026-07-14:
- 생성 예정 파일:
-
Task 2.3: Content navigation adapter 구현 및 전체 호출부 전환
- 생성/수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/content/action/ContentActionHandler.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/model/HomeRecommendationUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/model/AudioRecommendationsUiModels.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ContentOverviewActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/content/overview/ui/ContentOverviewAdapter.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/audio/CreatorChannelAudioFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/CreatorChannelSeriesFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/series/ui/CreatorChannelSeriesAdapter.kt- 위 호출부 계약을 검증하는 기존 source/layout 테스트
- 작업: 기존 extra와 Access UX를 보존하면서 오디오/시리즈 상세 진입을 한 곳으로 통합한다.
- 검증 기준: 대상 UI에서 상세 Activity Intent 직접 생성이 제거되고 Content Action을 사용한다.
- RED 검증 기록:
- 2026-07-14: 기존 source/layout 테스트를 새 Content Action 결선 계약으로 먼저 변경하고 관련 6개 테스트 클래스 207건을
--rerun-tasks로 실행했다. 대상 화면의 직접 상세 Intent, series banner Intent, ID-only callback이 남아 있어 예상한 13건이 실패하고BUILD FAILED가 발생함을 확인했다.
- 2026-07-14: 기존 source/layout 테스트를 새 Content Action 결선 계약으로 먼저 변경하고 관련 6개 테스트 클래스 207건을
- GREEN 검증 기록:
- 2026-07-14: Home, Content, Content Overview, Creator Channel 호출부를
handleContentAction으로 전환하고 대상 feature/model source의AudioContentDetailActivity/SeriesDetailActivity직접 참조를 제거했다. 상세 Activity와 기존 ID extra 생성은ContentActionHandler만 소유한다. - 2026-07-14: Content Overview와 Creator Channel Audio/Live replay/Series tab callback이 기존 UI model의
showAdultBadge를 command까지 전달하도록 연결했다. 성인 정보를 제공하지 않는 Home, schedule, Creator Home series 경로는 기존 Login 요구사항을 유지했다. - 2026-07-14: 같은 6개 테스트 클래스 207건을
--rerun-tasks로 재실행해 실패, 오류, 스킵 없이BUILD SUCCESSFUL로 통과했다. - 2026-07-14: 결선 GREEN 중 발견한 기존 전체 탭 source assertion은 다른 helper의
openSeriesDetail(seriesId)문자열에 우연히 의존하고 있었다. 실제 계약인openSeriesDetail(seriesId, findAllTabSeriesAdultAccess(seriesId))를 검증하도록 강화한 뒤 단일 테스트와 207건 결선을 다시 통과시켰다.
- 2026-07-14: Home, Content, Content Overview, Creator Channel 호출부를
- 생성/수정 예정 파일:
-
Task 2.4: Content Phase 회귀 검증
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.content.action.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck git diff --check
- 수동 확인: Home, Content, Content Overview, Creator Channel에서 audio/series 상세 진입과 성인 차단 UX가 기존과 동일하다. - 검증 기록: - 2026-07-14: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.content.action.*"`, `--tests "kr.co.vividnext.sodalive.v2.main.content.*"`, `--tests "kr.co.vividnext.sodalive.v2.creator.channel.*"`, 추가 Home 회귀 `--tests "kr.co.vividnext.sodalive.v2.main.home.*"`를 각각 실행해 모두 `BUILD SUCCESSFUL`을 확인했다. Content 묶음은 99개 테스트가 통과했다. - 2026-07-14: Phase 1에서 작성된 `ContentMainFragmentLoginGuardSourceTest`가 이전 화면 내부 `ensureV2Access` 구현을 고정해 Content 묶음 3건이 실패했다. 실제 Access 계약은 `ContentMainFragment -> ContentAction -> ContentActionHandler` 순으로 이동했으므로 invalid ID, 성인 요구사항, Access-before-navigation을 새 소유자에서 검증하도록 테스트를 갱신한 뒤 단일 클래스와 Content 99건을 다시 통과시켰다. - 2026-07-14: `./gradlew :app:testDebugUnitTest`를 실행해 전체 debug 단위 테스트가 44초에 `BUILD SUCCESSFUL`로 통과했고, `./gradlew :app:compileDebugKotlin`도 `BUILD SUCCESSFUL`로 통과했다. - 2026-07-14: `./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks`는 `BUILD SUCCESSFUL`로 통과했고 test source 리포트는 0건이다. `./gradlew :app:ktlintCheck`는 이번 변경과 무관한 기존 main source 기준선 위반 550줄로 실패했으며, 리포트에서 Phase 2 변경 Kotlin 파일의 위반은 0건임을 확인했다. 레거시 파일은 수정하지 않았다. - 2026-07-14: 대상 Home, Content, Content Overview, Creator Channel 운영 source에서 `AudioContentDetailActivity`, `SeriesDetailActivity` 및 상세 ID extra를 검색한 결과 잔존이 없고, `ContentActionHandler`만 기존 Activity/extra 생성을 소유함을 확인했다. Phase 1에서 계약을 고정한 `MainV2Activity` 시스템 route는 범위대로 유지했다. - 2026-07-14: `./gradlew tasks --all`은 `BUILD SUCCESSFUL`로 통과했다. `adb devices`는 연결된 기기가 없음을 반환해 실기기 수동 탭 검증은 수행하지 못했고, 대신 Robolectric handler 테스트로 허용/차단과 기존 extra를 검증했다. - 2026-07-14: `git diff --check HEAD`는 whitespace 오류 없이 통과했다. - 실행 명령:
Phase 3: Live 공통 Action
-
Task 3.1: 기존 Live 흐름 차이와 레거시 의존 목록 확정
- 확인 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
- 작업: 상세, 입장, 예약, 결제, 비밀번호, 오디오 중단, Activity result, refresh callback, navigation 차이를 기록한다.
- 검증 기준:
- Live 공통 정책과 화면별 후처리가 구분된다.
- 라이브 생성 결과를 포함한 raw Activity result의 Live 결과 변환과 feature 단일 handler가 확정된다.
- 확인 결과:
- Home은 로그인 후 상세를 조회해
isAdult를 확인하고 기존 coordinator에 상세를 전달하며, Creator Channel 현재 라이브는 응답의isAdult를 먼저 확인한 뒤 coordinator가 상세를 다시 조회한다. On-air는 로그인, 상세 조회, channel 유효성, 성인 접근을 확인한 뒤 Activity 안에 복제된 입장 분기를 실행한다. - 공통 입장 결정 순서는 channel 없음의 상세 표시, 관리자 직접 입장, 무료/결제 완료 방의 비밀번호 또는 직접 입장, 미결제 유료 비밀번호 또는 결제 Dialog다. 오디오 서비스 중단과 입장 성공 후
LiveRoomActivity이동도 세 흐름에서 동일하다. - 일정 상세의 예약, 수정, 시작, 취소는 기존 coordinator만 소유하며 레거시
LiveViewModel, Dialog, Activity를 호출한다. 이 동작은 Live 소유 coordinator로 함께 이동하되 정책 판단과 화면 adapter를 추가로 분리하지 않는다. - On-air는 channel이 없으면 공통 오류 Toast를 표시하고, Home/Creator Channel은 라이브 상세를 표시한다. 이 차이는 공통 입장 정책이 아닌 호출 화면의 unavailable callback으로 유지한다.
- Creator Channel의 라이브 생성 raw Activity result는 성공 여부, room ID, channel name을
LiveCreationResult.Enter,Created,RefreshOnly,Ignored로 변환한다. Activity의handleLiveCreationResult단일 handler가 성공 결과의 Home refresh 후 즉시 입장 또는 생성 안내를 결정한다. 실패/cancel은Ignored, channel은 있지만 room ID가 유효하지 않은 성공 결과는RefreshOnly로 변환한다. - 별도 observer/SharedFlow는 추가하지 않는다. Live 생성 결과의 소비자는 Creator Channel Activity 하나이고 Home/On-air refresh 요구도 각각 현재 화면 callback으로 충분하다.
- Home은 로그인 후 상세를 조회해
- 검증 기록:
- 2026-07-14: 대상 운영 코드와 source test를 대조해 위 공통 정책, 레거시 의존, Activity result 및 화면별 후처리 차이를 확정했다.
- 2026-07-14: 구현 전
./gradlew :app:testDebugUnitTest를 실행해 전체 debug 단위 테스트 기준선이BUILD SUCCESSFUL임을 확인했다.
- 확인 예정 파일:
-
Task 3.2: Live 입장 결정 계약과 단위 테스트 작성
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveEntryDecision.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveEntryPolicy.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveEntryPolicyTest.kt
- 검증 대상: 관리자, 무료/결제 완료, 비밀번호, 유료, 상세 표시 분기.
- 검증 기준: Activity/Dialog 없이 기존 분기 순서를 순수 입력·출력으로 검증한다.
- RED 검증 기록:
- 2026-07-14:
LiveEntryPolicyTest를 먼저 추가하고 단일 테스트를 실행해LiveEntryDecision,LiveEntryContext,LiveEntryPolicy미정의 컴파일 오류와BUILD FAILED를 확인했다.
- 2026-07-14:
- GREEN 검증 기록:
- 2026-07-14: channel 없음, 관리자, 무료/결제 완료 공개·비밀번호 방, 미결제 유료 공개·비밀번호 방만 표현하는 순수 정책을 최소 구현하고 같은 단일 테스트를 재실행해
BUILD SUCCESSFUL을 확인했다.
- 2026-07-14: channel 없음, 관리자, 무료/결제 완료 공개·비밀번호 방, 미결제 유료 공개·비밀번호 방만 표현하는 순수 정책을 최소 구현하고 같은 단일 테스트를 재실행해
- 생성 예정 파일:
-
Task 3.3: Live Action/Coordinator 단일 구현과 호환 adapter 구성
- 생성/이동/수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveCreationResult.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinator.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelLiveCoordinator.kt
- 작업:
- 기존 Coordinator 동작을 Live 소유 진입점으로 옮기고 필요한 동안 기존 이름은 얇은 adapter로 유지한다.
- 하위 Activity가 반환하는 Live 결과는 명시적 result로 변환하고 호출 feature의 단일 handler로 전달한다.
- 검증 기준: 새 구현이 Creator Channel feature를 import하지 않고 레거시 Live 기능만 adapter로 호출한다.
- RED 검증 기록:
- 2026-07-14:
LiveActionsTest와LiveActionCoordinatorSourceTest를 먼저 추가했다. 결과 타입/변환 함수 미정의 컴파일 실패를 확인한 뒤 최소 결과 계약을 추가했고, 새 coordinator 파일과 세 화면 결선 및 Creator Channel handler가 없어 source 결선 테스트 3건이 실패함을 확인했다. - 2026-07-14: diff 검토에서 channel은 있지만 room ID가 유효하지 않은 기존 무동작 edge를 발견해
Ignored기대 테스트로 변경하고 실제Created반환 실패를 재현했다. 이 당시 계약은 후속 Task 3.6에서 성공 결과의 Home refresh를 보장하도록RefreshOnly로 변경했다.
- 2026-07-14:
- GREEN 검증 기록:
- 2026-07-14: 기존 coordinator를
v2.live.action.LiveActionCoordinator로 이동하고LiveEntryPolicy결과에 따라 기존LiveViewModel, Dialog, Activity를 호출하도록 전환했다. 모든 호출부를 함께 전환해 이전 이름의 호환 adapter는 남길 필요가 없었으며 feature 역의존도 제거했다. - 2026-07-14:
LiveCreationResult와resolveLiveCreationResult를 구현하고 Creator Channel의handleLiveCreationResult단일 handler가Ignored,Created,Enter후처리를 소유하도록 연결했다. invalid room ID는 기존처럼Ignored로 보존한 뒤 결과 테스트를 통과시켰다. 이 동작은 후속 Task 3.6에서RefreshOnly로 변경했다.
- 2026-07-14: 기존 coordinator를
- 생성/이동/수정 예정 파일:
-
Task 3.4: Home/Creator Channel/On-air 호출부 전환과 중복 제거
- 수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
- 검증 기준: 세 화면이 동일 Live Action을 사용하고 On-air의 중복 입장 조건이 제거된다.
- 검증 기록:
- 2026-07-14: Home은 lifecycle 확인 callback과 함께 공통 coordinator를 사용하고, Creator Channel은 현재 라이브의 알려진 성인 여부를 전달하며 일정 상세도 같은 coordinator를 사용하도록 전환했다.
- 2026-07-14: On-air Activity의 상세 이후 관리자/무료/결제/비밀번호 분기와 Dialog/navigation 구현을 제거하고 공통 coordinator에 unavailable Toast callback만 전달했다. 이에 따라 단일 boolean helper와 파일도 제거했다.
- 2026-07-14: 신규 결선 테스트와 기존 Home login guard, Creator Channel, On-air source 테스트를 함께 실행해
BUILD SUCCESSFUL을 확인했다.
- 수정 예정 파일:
-
Task 3.5: Live Phase 회귀 검증
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck git diff --check
- 수동 확인: 무료, 유료, 비밀번호, 관리자, 상세 표시, 예약/취소 흐름과 화면별 refresh 동작이 기존과 동일하다. - 검증 기록: - 2026-07-14: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.*" --tests "kr.co.vividnext.sodalive.v2.creator.channel.live.*" --rerun-tasks`를 실행해 Live 관련 테스트가 `BUILD SUCCESSFUL`로 통과했다. - 2026-07-14: 최종 변경 후 `./gradlew :app:testDebugUnitTest --rerun-tasks`를 실행해 전체 debug 단위 테스트와 production/test 컴파일이 1분 43초에 `BUILD SUCCESSFUL`로 통과했다. 기존 deprecation 및 manifest namespace 경고는 있었지만 실패는 없었다. - 2026-07-14: `./gradlew :app:compileDebugKotlin`은 별도 실행에서도 `BUILD SUCCESSFUL`로 통과했다. - 2026-07-14: `./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks`는 `BUILD SUCCESSFUL`로 통과했다. `./gradlew :app:ktlintCheck --rerun-tasks`는 기존 main source 기준선 위반으로 실패했으며, filename 위반을 수정한 뒤 main source 보고서 550줄에서 Phase 3 변경 Kotlin 파일 위반이 0건임을 확인했다. - 2026-07-14: `./gradlew tasks --all`은 `BUILD SUCCESSFUL`로 통과했고 `git diff --check HEAD`도 whitespace 오류 없이 통과했다. - 2026-07-14: `adb devices`에 연결된 기기/에뮬레이터가 없어 무료, 유료, 비밀번호, 관리자, 상세, 예약/취소의 실제 Dialog/Activity 수동 탭 검증은 수행하지 못했다. 해당 분기는 순수 정책 테스트와 기존 레거시 결선 source 테스트로 회귀 검증했다. - 실행 명령:
-
Task 3.6: Live 진입·생성 결과 회귀와 리뷰 발견사항 보완
- 수정 대상 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinator.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveCreationResult.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinatorSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionsTest.ktdocs/20260714_V2_공통_접근_가드와_도메인_액션/prd.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 정리 대상 파일:
docs/20260715_라이브_진입과_생성_결과_회귀_수정/prd.mddocs/20260715_라이브_진입과_생성_결과_회귀_수정/plan-task.md
- 작업:
- unavailable을 성인 접근 확인과 오디오 중단보다 먼저 처리하되, 상세 fallback은 호출자 성인 힌트와 상세 응답을 OR로 합성한 접근 조건을 정상 진입과 함께 사용한다.
- 라이브 생성 성공 후 즉시 입장할 room ID가 유효하지 않아도
RefreshOnly로 Home refresh를 보장한다. - 신규 source test의 ktlint 위반 3건을 고치고 test source 검증 게이트를 복구한다.
- 연속 작업으로 별도 생성된 2026-07-15 문서의 요구사항과 검증 기록을 본 PRD와 Phase 3에 이관한 뒤 중복 문서를 제거한다.
- 검증 기준:
- 합성 성인 접근 조건이 정상 진입과 unavailable 상세 fallback에서 동일하게 사용된다.
LiveActionCoordinatorSourceTest와 Live action 관련 테스트가 통과한다.:app:ktlintTestSourceSetCheck가 통과하고 Phase 3 변경 Kotlin 파일의 신규 main source 위반이 없다.- 별도 2026-07-15 문서가 남지 않고 현행 생성 결과 계약이 구현과 일치한다.
- 이관한 구현·검증 기록:
- 2026-07-15: 성공했지만 channel 정보가 있고 room ID가 유효하지 않은 결과를
RefreshOnly로 기대하는 테스트를 먼저 추가해 타입 미정의 컴파일 오류로 RED를 확인했다. - 2026-07-15:
LiveCreationResult.RefreshOnly를 추가하고 Creator Channel의 단일 handler가 해당 결과에서homeActionDelegate?.refreshHome()만 실행하도록 구현했다. - 2026-07-15:
ShowDetail을 성인 접근 확인과 오디오 중단보다 먼저 처리하도록 source test와 구현을 보완했다. source 문자열을 실제 구현 호출 형태로 조정한 뒤 테스트가 통과했다. - 2026-07-15:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.*"는BUILD SUCCESSFUL로 통과했다. - 2026-07-15:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivitySourceTest"는BUILD SUCCESSFUL로 통과했다. - 2026-07-15:
./gradlew :app:assembleDebug는BUILD SUCCESSFUL로 통과했다. - 2026-07-15: 리뷰에서 상세 조회 전 성인 접근 확인이 실행되는 순서 문제와 callback 없는
ShowDetailfallback이 성인 접근 확인 없이 열리는 문제가 확인되었다. 로그인만 먼저 확인하고, fallback은roomDetail.isAdult로 성인 접근을 확인하도록 보완한 뒤 Live action 테스트, Creator Channel source test, assembleDebug가 통과했다. - 2026-07-15: 보안 재리뷰에서 호출자 힌트가
false여도 상세 응답이true이면 성인 접근을 요구해야 하는 문제가 확인되었다. 정상 진입 조건을requiresAdultContentAccess == true || roomDetail.isAdult로 바꾸는 테스트에서 기존 구현의 assertion 2건 실패로 RED를 확인한 뒤 보완했고, Live action 테스트, Creator Channel source test, assembleDebug가 통과했다.
- 2026-07-15: 성공했지만 channel 정보가 있고 room ID가 유효하지 않은 결과를
- 리뷰 발견 및 RED 검증 기록:
- 2026-07-15: Creator Channel 목록의
isAdult == true와 unavailable 상세 응답의isAdult == false가 다를 때 fallback이roomDetail.isAdult만 사용해 호출자 힌트를 잃는 P1 회귀를 코드 경로 대조로 확인했다. - 2026-07-15:
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks를 실행해LiveActionCoordinatorSourceTest.kt:79의max-line-length1건과argument-list-wrapping2건, 총 3건으로BUILD FAILED를 재현했다. - 2026-07-15: 합성 성인 접근 조건의 단일 계산과 정상 진입·fallback 재사용을 요구하도록
LiveActionCoordinatorSourceTest를 먼저 변경했다. 대상 테스트를 실행해 5개 중 기존 구현과 불일치한 3개가 실패하고BUILD FAILED가 되는 RED를 확인했다.
- 2026-07-15: Creator Channel 목록의
- GREEN 및 회귀 검증 기록:
- 2026-07-15:
requiresAdultContentAccess == true || roomDetail.isAdult를requiresAdultAccess로 한 번 계산하고 정상 진입 guard와 unavailable 상세 fallback에 함께 전달하도록 수정했다. - 2026-07-15: source test가 정상 진입과 fallback 함수 구간을 분리해 동일 성인 조건을 검증하고, unavailable 처리 후
return@handleRoomDetail, callback의return, 성인 guard, 상세 표시 순서를 고정하도록 보강했다.RefreshOnly분기의 Home refresh와 invalid room IDnull,0L, 음수도 함께 검증했다. - 2026-07-15:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.*"와 최종LiveActionCoordinatorSourceTest단일 실행은 각각BUILD SUCCESSFUL로 통과했다. - 2026-07-15: 최종
./gradlew :app:testDebugUnitTest --rerun-tasks는 39개 작업을 모두 실행해 3분 20초에BUILD SUCCESSFUL로 통과했다. - 2026-07-15: 최종
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL로 통과했고 test source 리포트는 0건이다. - 2026-07-15:
./gradlew :app:assembleDebug는BUILD SUCCESSFUL로 통과했다. - 2026-07-15:
./gradlew :app:ktlintMainSourceSetCheck --rerun-tasks는 기존 main source 기준선 550건으로 실패했으며, 갱신된 리포트에서 Phase 3 변경 Kotlin 파일의 위반은 0건임을 확인했다. - 2026-07-15:
./gradlew tasks --all은BUILD SUCCESSFUL로 통과했다. - 2026-07-15: 별도
docs/20260715_라이브_진입과_생성_결과_회귀_수정/의 두 문서 내용을 본 PRD와 Task 3.6으로 이관한 뒤 중복 문서 파일을 제거했다. - 2026-07-15: 독립 코드 재리뷰에서 정상·fallback 성인 guard, 두 unavailable short-circuit,
RefreshOnlyrefresh 계약을 재확인했고 Critical/Important 이슈 없이 Ready 판정을 받았다. - 2026-07-15: 최종
git diff --check HEAD는 whitespace 오류 없이 통과했다.
- 2026-07-15:
- 수정 대상 파일:
-
Task 3.7: Live coordinator lifecycle·중복 표시·계약 리뷰 반영
- 수정 대상 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinator.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinatorSourceTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinatorUiGateTest.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/live/action/LiveEntryPolicyTest.ktdocs/agent-guides/build-test-style.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/prd.mddocs/20260714_V2_공통_접근_가드와_도메인_액션/plan-task.md
- 작업:
- 비동기
enterRoom성공 callback에서LiveRoomActivity를 열기 직전에 lifecycle predicate를 다시 검사한다. - 라이브 상세 bottom sheet는 Activity/호출 화면 predicate,
FragmentManager.isStateSaved, 안정적인 tag의 기존 fragment 존재 여부를 모두 통과할 때만 표시한다. channelName없음과 본인 manager 조건이 동시에 있을 때ShowDetail이 manager 직접 입장보다 우선함을 실행형 정책 테스트로 고정한다.LiveActionCoordinator가 presentation adapter로서refreshHomecallback을 주입받는 예외와 순수 도메인 계약의 callback 금지를 문서에서 명확히 구분한다.
- 비동기
- 검증 기준:
canHandleResult기본값은 Activity 종료/파괴 상태를 반영하고, Fragment 호출부의 더 엄격한 predicate override는 유지된다.- 모든 비동기 UI 후처리 직전에는
canHandleResult가 재검사된다. - 상세 sheet 중복 방지는 새 fragment 인스턴스의
isAdded가 아니라 안정적인 tag와 표시 가능 상태를 기준으로 동작한다. - 정책 테스트와 coordinator gate/source 테스트, 컴파일, test source ktlint, whitespace 검증이 통과한다.
- RED 검증 기록:
- 2026-07-15: lifecycle/detail gate 실행형 테스트와 source test를 먼저 추가한 뒤
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.LiveEntryPolicyTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorSourceTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorUiGateTest" --rerun-tasks를 실행해runLiveActionUiSideEffect,canShowLiveRoomDetail미정의 컴파일 오류로BUILD FAILED를 확인했다.
- 2026-07-15: lifecycle/detail gate 실행형 테스트와 source test를 먼저 추가한 뒤
- GREEN 및 회귀 검증 기록:
- 2026-07-15:
LiveActionCoordinator의 기본canHandleResult를 Activity 종료/파괴 상태 기반으로 바꾸고,canHandleUiResult()에서 Activity 상태,FragmentManager.isStateSaved, 호출 화면 predicate를 합성하도록 보완했다. - 2026-07-15: 상세 sheet는
LIVE_ROOM_DETAIL_TAG로findFragmentByTag를 먼저 확인하고, 같은 tag로show하도록 수정했다. 새LiveRoomDetailFragment인스턴스의isAdded와 nullabletag기반 중복 방지는 제거했다. - 2026-07-15:
enterRoom성공 callback과startLive성공 callback은runOnUiThread내부에서runLiveActionUiSideEffect(::canHandleUiResult)를 통과한 뒤에만openLiveRoom(roomId)를 호출하도록 보강했다. 비밀번호/결제/예약/수정/시작/취소 UI side effect도 표시 또는 실행 직전canHandleUiResult()를 검사한다. - 2026-07-15:
LiveEntryPolicyTest에channelName == null과 본인 manager 조건이 동시에 있을 때LiveEntryDecision.ShowDetail을 반환하는 실행형 회귀 테스트를 추가했다. - 2026-07-15:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.LiveEntryPolicyTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorSourceTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorUiGateTest" --rerun-tasks를 실행해BUILD SUCCESSFUL을 확인했다. - 2026-07-15: 최종
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.*" --rerun-tasks를 실행해 Live action 테스트 묶음이BUILD SUCCESSFUL로 통과했다. - 2026-07-15:
./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL로 통과했다. - 2026-07-15:
./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL로 통과했다../gradlew :app:ktlintMainSourceSetCheck --rerun-tasks는 기존 main source 기준선 위반으로 실패했으며, ktlint main/test 리포트에서 이번 변경 Kotlin 파일명 검색 결과는 0건이다. - 2026-07-15:
./gradlew tasks --all은BUILD SUCCESSFUL로 통과했고git diff --check HEAD는 whitespace 오류 없이 통과했다. - 2026-07-15:
adb devices는 연결된 기기/에뮬레이터가 없음을 반환해 실제 탭 기반 Dialog/Activity 수동 검증은 수행하지 못했다. 대신 lifecycle/detail gate 실행형 단위 테스트와 source order 테스트로 회귀를 고정했다.
- 2026-07-15:
- 리뷰 게이트 후속 보완 기록:
- 2026-07-15: 리뷰 게이트에서
DialogFragment.show()가 비동기commit()을 사용해 첫 transaction 반영 전 연속 호출 시findFragmentByTag가 중복을 막지 못할 수 있다는 차단 이슈와, unavailable/adult guard/audio 순서가 source test에만 의존한다는 차단 이슈를 확인했다. - 2026-07-15:
LiveActionCoordinatorSourceTest를 먼저showNow(fragmentManager, LIVE_ROOM_DETAIL_TAG)기대값으로 바꾸고,LiveActionCoordinatorUiGateTest에runLiveEntryDecisionFlow실행형 순서 테스트를 추가한 뒤./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorSourceTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorUiGateTest" --rerun-tasks를 실행해runLiveEntryDecisionFlow미정의 컴파일 오류로BUILD FAILED를 확인했다. - 2026-07-15: 상세 sheet 표시를
showNow(fragmentManager, LIVE_ROOM_DETAIL_TAG)로 바꿔 pending transaction 경쟁 구간을 제거하고, 입장 분기 순서를 Android 비의존runLiveEntryDecisionFlow로 분리해ShowDetail -> unavailable,Enter -> adult guard -> audio/entry, adult guard 거부 시 audio/entry 미실행을 실행형 테스트로 고정했다. - 2026-07-15: 후속 보완 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorSourceTest" --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorUiGateTest" --rerun-tasks를 재실행해BUILD SUCCESSFUL을 확인했다. - 2026-07-15: 후속 보완 최종 확인으로
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.*" --rerun-tasks를 재실행해 39개 작업이 모두 실행되고BUILD SUCCESSFUL로 종료됨을 확인했다. - 2026-07-15:
./gradlew :app:testDebugUnitTest --rerun-tasks전체 debug 단위 테스트를 재실행해 39개 작업이 모두 실행되고BUILD SUCCESSFUL로 종료됨을 확인했다. - 2026-07-15:
./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL,./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL,./gradlew tasks --all은BUILD SUCCESSFUL,git diff --check HEAD는 무출력으로 통과했다. - 2026-07-15:
./gradlew :app:ktlintMainSourceSetCheck --rerun-tasks는 기존 main source 기준선 위반으로 실패했으며, 갱신된 ktlint main 리포트에서 이번 변경 파일명 검색 결과는 0건이다. - 2026-07-15:
adb devices는 연결된 기기/에뮬레이터가 없음을 반환해 실제 탭 기반 수동 QA는 수행하지 못했다. 대신 Live action 대상 실행형 테스트, 전체 debug 단위 테스트, 컴파일, ktlint test source, whitespace 검증으로 대체 확인했다.
- 2026-07-15: 리뷰 게이트에서
- 보안 리뷰 후속 보완 기록:
- 2026-07-15: 재리뷰 게이트에서 Creator Channel 일정 경로가
showLiveRoomDetail(roomId)를 직접 호출할 때 공개 상세 진입점 자체에는 로그인·성인 접근 확인이 없어 성인 라이브 상세를 열 수 있다는 보안 차단 이슈를 확인했다. - 2026-07-15:
LiveActionCoordinatorSourceTest에 공개showLiveRoomDetail(roomId)가AccessRequirement.Login,liveViewModel.getRoomDetail(roomId),AccessRequirement.forAdultContent(roomDetail.isAdult)를 거친 뒤 내부 표시 함수로 이동해야 한다는 source test를 먼저 추가했다. 생산 코드 변경 전 단일 source test 실행에서 해당 테스트가 실패해 RED를 확인했다. - 2026-07-15: 공개
showLiveRoomDetail(roomId)는 로그인 확인 후 room detail을 조회하고roomDetail.isAdult기준 성인 접근을 통과한 뒤showLiveRoomDetailContent(roomId)를 호출하도록 분리했다. 이미 성인 가드를 통과한 unavailable fallback은 공개 진입점 재호출 대신 내부 표시 함수만 호출하도록 수정했다. - 2026-07-15: 보안 보완 후
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinatorSourceTest" --rerun-tasks와./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.live.action.*" --rerun-tasks가 모두BUILD SUCCESSFUL로 통과했다. - 2026-07-15: 전체
./gradlew :app:testDebugUnitTest --rerun-tasks는 최초 재실행에서 이번 변경과 직접 관련 없는CreatorChannelHomeViewModelTest1건이 실패했으나, 해당 단일 테스트 재실행과 전체 재실행이 모두BUILD SUCCESSFUL로 통과해 일시적 격리 실패로 기록한다. - 2026-07-15: 보안 보완 후
./gradlew :app:compileDebugKotlin은BUILD SUCCESSFUL,./gradlew :app:ktlintTestSourceSetCheck --rerun-tasks는BUILD SUCCESSFUL,git diff --check HEAD는 무출력으로 통과했다../gradlew :app:ktlintMainSourceSetCheck --rerun-tasks는 기존 main source 기준선 위반으로 실패했으며 변경 파일명 검색 결과는 0건이다.
- 2026-07-15: 재리뷰 게이트에서 Creator Channel 일정 경로가
- 수정 대상 파일:
-
Task 3.8: Phase 3 실기기 수동 회귀 테스트
- 확인 대상 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/live/action/LiveActionCoordinator.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/live/onair/HomeOnAirLiveActivity.kt
- 대상 화면:
HomeMainFragment추천 라이브CreatorChannelActivity현재 라이브, 일정 상세, owner 라이브 생성HomeOnAirLiveActivity라이브 목록
- 완료 기준:
- 아래 필수 시나리오가 모두 기대 결과와 일치하면 각 항목을
[x]로 변경한다. - 재현 데이터나 외부 환경이 없어 실행하지 못한 항목은 체크하지 않고 검증 기록에
BLOCKED와 사유를 남긴다. - 최종 수동 검증에서 사용자가 테스트 불가능 또는 의미 없음으로 판단한 항목은 미체크 상태를 유지하고
SKIPPED로 기록하며 완료 판정 대상에서 제외한다. - 기대 결과와 다른 항목은 체크하지 않고 화면, 계정 조건, room ID, 실제 결과, 로그 또는 캡처 위치를 검증 기록에 남긴다.
- 아래 필수 시나리오가 모두 기대 결과와 일치하면 각 항목을
사전 준비
-
M3-P01: 테스트 환경 준비
- 절차: 최신 debug 빌드를 실기기 또는 에뮬레이터에 설치하고 네트워크 연결, 알림/오디오 재생, 로그인 화면 이동이 가능한지 확인한다.
- 기대 결과: 앱을 실행해 Home, Creator Channel, On-air 화면에 진입할 수 있고 라이브 상세/입장 API 응답을 받을 수 있다.
-
M3-P02: 접근 상태별 계정 준비
- 절차: 로그아웃 상태, 국내 미인증 계정, 본인인증 완료·성인 콘텐츠 표시 비활성 계정, 성인 콘텐츠 접근 허용 일반 계정, 라이브 관리자 계정을 준비한다.
- 기대 결과: 각 계정의 로그인,
isAuth, 성인 콘텐츠 표시 설정, 관리자 여부를 테스트 전에 구분할 수 있다.
-
M3-P03: 라이브 분기별 데이터 준비
- 절차: 아래 조건을 각각 만족하는 현재 라이브 또는 예약 라이브의 room ID와 노출 화면을 기록한다.
데이터 조건 주요 검증 R1Home 추천, Creator Channel, On-air에 노출되는 무료 공개 현재 라이브 세 화면 공통 진입 R2무료 비밀번호 현재 라이브 결제액 없는 비밀번호 Dialog R3결제 완료 유료 공개 현재 라이브 결제 없는 직접 입장 R4결제 완료 유료 비밀번호 현재 라이브 결제액 없는 비밀번호 Dialog R5미결제 유료 공개 현재 라이브 결제 Dialog R6미결제 유료 비밀번호 현재 라이브 가격이 표시되는 비밀번호 Dialog R7관리자 본인의 유료·비밀번호 현재 라이브 관리자 직접 입장 우선순위 R8각 대상 화면에 노출 가능한 channelNamenull 또는 blank 라이브화면별 unavailable 처리 R9성인 현재 라이브 본인인증/표시 설정 가드 R10일반 사용자가 예약 가능한 무료·유료·비밀번호 예약 라이브 예약 분기 - 기대 결과: 각 데이터의 가격, 결제 여부, 비밀번호 여부, 성인 여부, 관리자 ID,
channelName조건을 QA 환경에서 확인할 수 있다.
화면 결선과 접근 가드
- M3-01: 세 화면의 무료 공개 라이브 진입
- 절차: 접근 허용 일반 계정으로
R1을 Home 추천, Creator Channel 현재 라이브, On-air 목록에서 각각 한 번 선택한다. - 기대 결과: 세 경로 모두 기존 로딩 표시 후 같은 room ID의
LiveRoomActivity로 이동하며 중복 상세 조회 화면이나 추가 Dialog가 나타나지 않는다.
- 절차: 접근 허용 일반 계정으로
- M3-02: 로그아웃 상태의 라이브 선택
- 절차: 로그아웃 상태에서 Home 추천 라이브를 선택하고, Home의 On-air 더보기 진입도 시도한다.
- 기대 결과: 기존
LoginActivity가 열리고 라이브 상세 조회, 오디오 중단,LiveRoomActivity이동은 실행되지 않는다.
- M3-03: 국내 미인증 계정의 성인 라이브 선택
- 절차: 국내 미인증 계정으로
R9를 Home 추천 또는 Creator Channel 현재 라이브에서 선택한다. - 기대 결과: 로그인 확인과 상세 조회 뒤 기존 V2 본인인증 안내가 표시되고 오디오 중단, 비밀번호/결제 Dialog, 라이브 입장은 실행되지 않는다.
- 절차: 국내 미인증 계정으로
- M3-04: 성인 콘텐츠 표시가 비활성인 계정의 성인 라이브 선택
- 절차: 본인인증 완료·성인 콘텐츠 표시 비활성 계정으로
R9를 선택한다. - 기대 결과:
ContentSettingsActivity가 성인 콘텐츠 안내 상태로 열리고 라이브 입장과 오디오 중단은 실행되지 않는다.
- 절차: 본인인증 완료·성인 콘텐츠 표시 비활성 계정으로
- M3-05: 성인 콘텐츠 접근 허용 계정의 성인 라이브 선택
- 절차: 성인 콘텐츠 접근 허용 계정으로
R9를 선택한다. - 기대 결과: 추가 접근 안내 없이
R9의 입장 정책에 맞는 직접 입장, 비밀번호 Dialog 또는 결제 Dialog가 실행된다.
- 절차: 성인 콘텐츠 접근 허용 계정으로
- M3-06: 호출자 성인 힌트와 상세 응답 불일치
- 절차: QA fixture에서 Creator Channel 목록
isAdult=true·상세isAdult=false인 방과 목록isAdult=false·상세isAdult=true인 방을 각각 선택한다. - 기대 결과: 두 경우 모두 성인 콘텐츠 접근 조건을 요구하며, 접근이 거부되면 상세 표시와 라이브 입장이 실행되지 않는다.
- 절차: QA fixture에서 Creator Channel 목록
Live 입장 분기
- 공통 절차: 각 항목 실행 전에 오디오 콘텐츠를 재생하고, 접근 허용 일반 계정 또는 명시된 관리자 계정으로 대상 라이브를 선택한다.
완료 시나리오 기대 결과 [x] M3-07: 관리자 본인의 유료·비밀번호 현재 라이브 R7선택 시 비밀번호/결제 Dialog 없이 오디오가 중단되고LiveRoomActivity로 이동한다.[x] M3-08: 무료 공개 현재 라이브 R1선택 시 오디오가 중단되고LiveRoomActivity로 이동한다.[x] M3-09: 무료 또는 결제 완료 비밀번호 현재 라이브 R2와R4선택 시 결제액 없는 비밀번호 Dialog가 표시되고 올바른 비밀번호 입력 성공 후 입장한다.[x] M3-10: 결제 완료 유료 공개 현재 라이브 R3선택 시 결제 Dialog 없이 오디오가 중단되고 바로 입장한다.[x] M3-11: 미결제 유료 공개 현재 라이브 R5선택 시 오디오가 중단되고 기존 금액·시간 문구의 결제 Dialog가 표시되며, 취소 시 현재 화면을 유지하고 확인 성공 시 입장한다.[x] M3-12: 미결제 유료 비밀번호 현재 라이브 R6선택 시 결제 Dialog 대신 가격이 표시되는 비밀번호 Dialog가 나타나고 올바른 비밀번호 입력 성공 후 입장한다.[x] M3-13: 비밀번호 또는 입장 API 실패 잘못된 비밀번호 또는 QA 실패 응답에서 기존 오류 안내가 표시되고 LiveRoomActivity는 열리지 않으며 로딩 표시가 종료된다.unavailable과 예약 라이브 상세
- M3-14: Home과 Creator Channel의 unavailable 상세 fallback
- 절차: 오디오를 재생한 상태에서
R8을 Home 추천과 Creator Channel 현재 라이브에서 각각 선택한다. - 기대 결과: 성인 접근 조건을 통과한 뒤 라이브 상세 bottom sheet가 표시되고 오디오는 계속 재생된다.
- 절차: 오디오를 재생한 상태에서
- M3-15: On-air의 unavailable 처리
- 절차: 오디오를 재생한 상태에서 On-air 목록에 노출된
R8을 선택한다. - 기대 결과: 기존 공통 오류 Toast만 표시되고 성인 안내, 라이브 상세, 비밀번호/결제 Dialog, 오디오 중단, 라이브 입장은 실행되지 않는다.
- 절차: 오디오를 재생한 상태에서 On-air 목록에 노출된
- M3-16: 예약 라이브 상세의 로그인·성인 접근 확인
- 절차: Creator Channel 일정에서 일반 예약 라이브와 성인 예약 라이브를 각각 선택하고, 성인 예약 라이브는 접근 거부 계정과 허용 계정으로 나누어 확인한다.
- 기대 결과: 일반 예약 라이브는 로그인 후 상세가 표시되고, 성인 예약 라이브는 성인 접근 허용 시에만 상세가 표시된다.
- M3-17: 라이브 상세 중복 표시 방지
- 절차: Creator Channel 일정의 같은 예약 라이브를 빠르게 연속 선택한다.
- 기대 결과: 같은
LiveRoomDetailFragment가 한 번만 표시되며 중복 bottom sheet나FragmentManager예외가 발생하지 않는다.
- M3-18: 일반 사용자의 무료·유료·비밀번호 예약
- 절차: 일반 계정으로
R10의 무료 공개, 미결제 유료 공개, 비밀번호 예약 라이브를 각각 열어 예약을 진행한다. - 기대 결과: 무료 공개는 바로 예약되고, 유료 공개는 기존 결제 Dialog를 거치며, 비밀번호 방은 기존 비밀번호 Dialog를 거친다. 성공 후
LiveReservationCompleteActivity가 열리고 Creator Channel Home이 새로고침된다.
- 절차: 일반 계정으로
- M3-19: 관리자 예약 라이브 수정
- 절차: 관리자 계정으로 본인 예약 라이브 상세에서 수정을 선택한다.
- 기대 결과: 기존 room detail extra를 가진
LiveRoomEditActivity가 열린다.
- M3-20: 관리자 예약 라이브 시작
- 절차: 관리자 계정으로 본인 예약 라이브 상세에서 시작을 선택하고 API 성공까지 기다린다.
- 기대 결과: Creator Channel Home이 새로고침되고 해당 room ID의
LiveRoomActivity로 이동한다.
- M3-21: 관리자 예약 라이브 취소
- 절차: 관리자 계정으로 본인 예약 라이브 상세에서 취소를 선택해 사유를 입력하고 완료한다.
- 기대 결과: 기존 취소 성공 Toast가 표시되고 Creator Channel Home이 새로고침되며 취소된 일정 상태가 반영된다.
라이브 생성 결과
- M3-22: 예약 라이브 생성 완료
- 절차: Creator Channel owner FAB에서 즉시 시작되지 않는 예약 라이브를 생성한다.
- 기대 결과: Creator Channel Home이 새로고침되고 기존 생성 완료 Toast가 표시되며
LiveRoomActivity는 열리지 않는다.
- M3-23: 즉시 시작 라이브 생성 완료
- 절차: Creator Channel owner FAB에서 channel과 유효한 room ID가 반환되는 즉시 시작 라이브를 생성한다.
- 기대 결과: Creator Channel Home이 새로고침되고 생성된 room ID의 입장 흐름을 거쳐
LiveRoomActivity로 이동한다.
- M3-24: 유효한 room ID가 없는 생성 성공 결과
- 절차: QA backend에서 channel은 반환하지만 room ID가 null,
0L또는 음수인 성공 응답으로 라이브 생성을 완료한다. - 기대 결과: Creator Channel Home만 새로고침되고 생성 Toast, 비밀번호/결제 Dialog,
LiveRoomActivity이동은 실행되지 않는다.
- 절차: QA backend에서 channel은 반환하지만 room ID가 null,
- M3-25: 라이브 생성 취소
- 절차: 라이브 생성 화면을 결과 없이 닫는다.
- 기대 결과: Creator Channel Home 새로고침, 생성 Toast, 라이브 입장이 실행되지 않는다.
- M3-26: 라이브 생성 실패 응답
- 절차: QA backend에서 라이브 생성 실패 응답을 반환하도록 설정하고 생성 요청을 완료한다.
- 기대 결과: 기존 생성 실패 안내만 표시되고 Creator Channel Home 새로고침, 생성 완료 Toast, 라이브 입장은 실행되지 않는다.
lifecycle과 오류 회귀
- M3-27: 상세 조회 중 호출 화면 이탈
- 절차: 느린 네트워크에서 라이브를 선택한 직후 Home Fragment를 벗어나는 경우와 Activity를 종료하는 경우를 각각 실행하고 상세 응답을 기다린다.
- 기대 결과: 이탈한 화면 위에 Toast, Dialog, bottom sheet,
LiveRoomActivity가 늦게 표시되지 않고 앱이 종료되지 않는다.
- M3-28: 입장 또는 라이브 시작 요청 중 화면 이탈
- 절차: 비밀번호/결제 확인 또는 관리자 라이브 시작 직후 요청이 완료되기 전에 Activity 종료와 앱 백그라운드 전환을 각각 실행한다.
- 기대 결과: lifecycle이 유효하지 않은 동안
LiveRoomActivity가 늦게 열리지 않고FragmentManager/Activity state 예외가 발생하지 않는다.
- M3-29: 상세 조회 API 실패
- 절차: QA 실패 응답 또는 네트워크 차단 상태에서 Home, Creator Channel, On-air 중 한 경로의 라이브를 선택한다.
- 기대 결과: 기존 라이브 상세 조회 오류 안내가 표시되고 오디오 중단, Dialog, 상세 bottom sheet, 라이브 입장은 실행되지 않으며 로딩 표시가 종료된다.
- M3-30: 수동 테스트 종료 후 화면 상태 확인
- 절차: 네트워크를 복구하고 Home, Creator Channel, On-air를 다시 열어 목록 로딩과 라이브 선택을 한 번씩 수행한다.
- 기대 결과: 이전 실패/취소/화면 이탈로 인한 고정 loading, 중복 Dialog, 중복 상세, 잘못된 Home 상태가 남지 않고 정상 동작한다.
수동 검증 기록 형식
- 각 실행 결과는 다음 형식으로 본 Task 아래에 누적한다.
YYYY-MM-DD / M3-번호 / 기기·OS / 계정 조건 / room ID / PASS·FAILED·BLOCKED·SKIPPED / 실제 결과와 증빙 위치
- 전체 항목 완료 후 여러 화면에 걸친 최종 결과는 문서 최하단
Verification Log에도 요약한다. - 체크리스트 작성 검증 기록:
- 2026-07-15: Phase 3 요구사항과 구현 분기를 대조해 사전 준비 3개와 수동 시나리오
M3-01~M3-30을 작성했다. 시나리오 번호 중복과 placeholder가 없음을 확인했고,./gradlew tasks --all은BUILD SUCCESSFUL,git diff --check HEAD는 무출력으로 통과했다.
- 2026-07-15: Phase 3 요구사항과 구현 분기를 대조해 사전 준비 3개와 수동 시나리오
- 사용자 수동 검증 기록:
- 2026-07-15 /
M3-P01~M3-P03/ 기기·OS·계정·room ID 상세 미제공 / PASS / 사용자가 사전 준비 3개 항목을 모두 완료로 확인했다. - 2026-07-15 /
M3-01,M3-02,M3-05,M3-07M3-14,M3-16M3-23,M3-25,M3-27,M3-28/ 기기·OS·계정·room ID 상세 미제공 / PASS / 사용자가 체크한 22개 시나리오를 기대 결과와 일치한 것으로 확인했다. - 2026-07-15 /
M3-03,M3-04,M3-06,M3-15,M3-24,M3-26,M3-29,M3-30/ 기기·OS·계정·room ID 상세 미제공 / SKIPPED / 사용자가 테스트 불가능 또는 의미 없음으로 판단했으며, 항목은 미체크 상태로 유지했다. - 2026-07-15: 사전 준비 3개와 시나리오 22개는
PASS, 시나리오 8개는SKIPPED,FAILED는 0개로 판정되어 Task 3.8을 완료 처리했다.
- 2026-07-15 /
- 확인 대상 파일:
Phase 4: Creator와 Community 공통 Action
-
Task 4.1: Creator Channel 진입 호출부와 계약 확정
- 확인 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/home/app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/
- 검증 기준:
creatorId, 로그인 요구 여부, route source, 기존 extra 차이가 정리된다.
- 확인 예정 파일:
-
Task 4.2: Creator Action 구현과 호출부 전환
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/action/CreatorActions.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/creator/action/CreatorActionsTest.kt
- 검증 기준: 유효 ID와 Access를 한 곳에서 처리하고 feature UI model을 입력받지 않는다.
- 생성 예정 파일:
-
Task 4.3: Community 게시글 진입 계약 구현과 호출부 전환
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityActions.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/community/action/CommunityActionsTest.kt
- 수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 검증 기준: 로그인, ID 검사, Activity result 차이를 보존하고 공통 게시글 진입점을 사용한다.
- 생성 예정 파일:
-
Task 4.4: Community 변경 결과와 레거시 ActivityResult adapter 구현
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChange.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/community/action/CommunityActivityResultMapper.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/community/action/CommunityChangeTest.kt
- 작업:
- 작성, 수정, 삭제, 고정 변경을
Created,Updated,Deleted,PinChanged로 표현한다. - 레거시 작성/수정 Activity의
RESULT_OK를 명시적인 Community 변경 결과로 변환한다. - 결과 계약에는 Home/Community 탭 refresh callback 또는 Fragment 참조를 포함하지 않는다.
- 작성, 수정, 삭제, 고정 변경을
- 검증 기준: result code, 유효/누락 post ID, 변경 종류가 화면 의존 없이 테스트된다.
- 생성 예정 파일:
-
Task 4.5: Creator Channel Community 변경 composition handler 통합
- 수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업:
handleCommunityChange(change)단일 함수에서 Home/Community projection 갱신을 조합한다.- Community 작성, 수정, 삭제, 고정 변경 성공 경로가 위 함수를 사용하도록 전환한다.
- 기존
ActivityResultLauncher, Activity result 수신 시점, Fragment initial load 동작은 유지한다.
- 검증 기준:
- Community Action/결과가 Creator Channel Home 또는 Community Fragment를 알지 않는다.
- Activity 내부의 개별 성공 경로에
refreshHome()과refreshCreatorChannelCommunity()조합이 반복되지 않는다. - 현재 Activity 내부 전파를 위해 전역 EventBus/observer singleton을 추가하지 않는다.
- 수정 예정 파일:
-
Task 4.6: Creator/Community Phase 회귀 검증
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.creator.action.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.community.action.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck git diff --check
- 실행 명령:
Phase 5: Chat 공통 Action
-
Task 5.1: Chat/DM 진입 유형과 결과 계약 확정
- 확인 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 작업: 기존 room 진입, creator 기반 DM 생성, AI/DM 타입, owner/non-owner, Activity result 차이를 구분한다.
- 검증 기준:
- 서로 다른 동작을 하나의 nullable/boolean 다목적 함수로 합치지 않는다.
- 호출 화면 후처리가 필요한 Activity result는 명시적 Chat 결과와 feature 단일 handler로 연결한다.
- 확인 예정 파일:
-
Task 5.2: Chat Action 계약과 단위 테스트 작성
- 생성 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/chat/action/ChatActions.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/chat/action/ChatActionCommand.ktapp/src/test/java/kr/co/vividnext/sodalive/v2/chat/action/ChatActionsTest.kt
- 검증 기준: room/creator 식별자와 진입 유형이 명시적이고 Home/Creator UI model에 의존하지 않는다.
- 생성 예정 파일:
-
Task 5.3: Home/Chat/Creator Channel 호출부 전환
- 수정 예정 파일:
app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/main/chat/ChatMainFragment.ktapp/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt
- 검증 기준: 기존 login guard, room 생성 API, 성공 후 이동, owner/non-owner 분기가 유지된다.
- 수정 예정 파일:
-
Task 5.4: Chat Phase 회귀 검증
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.chat.action.*" ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.chat.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck git diff --check
- 실행 명령:
Phase 6: 의존 방향과 이전 구현 정리
-
Task 6.1: feature 간 직접 navigation/Coordinator 의존 재검사
- 확인 범위:
app/src/main/java/kr/co/vividnext/sodalive/v2/**/*.kt - 실행 명령:
rg -n '^import kr\.co\.vividnext\.sodalive\.v2\.(main|creator|live)\..*(Activity|Fragment|Coordinator)' app/src/main/java/kr/co/vividnext/sodalive/v2 --glob '*.kt' - 검증 기준: composition에 필요한 부모-자식 Fragment 연결을 제외하고 도메인 Action으로 대체할 직접 의존이 남지 않는다.
- 확인 범위:
-
Task 6.2: 이전 helper와 중복 함수 제거
- 삭제/수정 대상: 각 Phase에서 호환을 위해 남긴 adapter, 이전
ensure..., 중복open..., 직접 route helper. - 검증 기준: 모든 호출부가 새 공개 진입점을 사용하는 것이 확인된 구현만 제거한다.
- 삭제/수정 대상: 각 Phase에서 호환을 위해 남긴 adapter, 이전
-
Task 6.3: 미추출 도메인 재평가
- 확인 대상: FanTalk, Donation, Schedule, Ranking, Discovery.
- 작업: 실제 반복 호출과 동일 정책이 생겼는지 확인하고 추출 또는 현행 유지 근거를 기록한다.
- 검증 기준: 형식적 Action 추가 없이 단일 사용 기능은 기존 feature에 유지한다.
-
Task 6.4: 결과 전달 방식과 observer 필요성 재검사
- 확인 대상: Content, Live, Community, Chat 및 후속 mutation 흐름.
- 작업:
- 하위 Activity 결과가
ActivityResult -> 명시적 결과 -> feature 단일 handler로 연결되는지 확인한다. - 동일한 raw result 해석과 refresh 조합이 여러 호출부에 남아 있는지 확인한다.
SharedFlow/observer 도입 지점이 있다면 Activity 밖의 독립 소비자, lifecycle, replay, 중복 처리 근거를 기록한다.
- 하위 Activity 결과가
- 검증 기준: 직접 결과 전달로 충분한 흐름에 전역 EventBus 또는 singleton observer가 추가되지 않는다.
-
Task 6.5: 통합 회귀 검증
- 실행 명령:
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.*" ./gradlew :app:compileDebugKotlin ./gradlew :app:ktlintCheck git diff --check - 수동 확인:
- 로그인/본인인증/성인 설정 차단과 허용 흐름
- Home, Content, Creator Channel, On-air의 오디오/시리즈/라이브 진입
- Creator Channel 및 Community 상세 진입
- Chat/DM 생성과 기존 room 진입
- 기존 Activity result와 새로고침 동작
- 기대 결과:
v2전체 관련 테스트와 컴파일, ktlint, whitespace 검증 통과.
- 실행 명령:
Verification Log
- 2026-07-14: 사용자 요청에 따라 이번 단계에서는 PRD와 구현 계획 문서만 작성했다. 운영 코드, 테스트 코드, DI는 아직 변경하지 않았고 Gradle 검증은 실행하지 않았다.
- 2026-07-14: 문서 범위는 Access부터 도메인별로 순차 전환하는 하나의 연속 작업으로 정의했다. 화면별 최초 집계 API는 유지하고 반복 action만 단일 소유 진입점으로 통합한다.
- 2026-07-14: 문서 작성 후
./gradlew tasks --all을 실행해 계획에 사용하는:app:testDebugUnitTest,:app:compileDebugKotlin,:app:ktlintCheck작업이 존재함을 확인했으며 명령은BUILD SUCCESSFUL로 종료되었다.git diff --check도 whitespace 오류 없이 통과했다. - 2026-07-14: Community 업로드 후 Home/Community 탭 갱신 설계를 구체화했다. 초기 구현은 전역 observer 대신
CommunityChange결과와 Creator Channel의 단일handleCommunityChangecomposition handler를 사용하며, 레거시ActivityResult는v2adapter에서 변경 결과로 변환한다. - 2026-07-14: 사용자 확인에 따라 Activity result가 필요한 모든 도메인 Action에 공통 결과 전달 원칙을 확장했다. 기본 방식은
ActivityResult -> 명시적 도메인 결과 -> feature 단일 handler이며, Action/Repository 직접 결과도 동일 handler로 합류한다.SharedFlow/observer는 Activity 밖의 독립 소비자와 lifecycle/replay 요구가 확인될 때만 도입한다. - 2026-07-14: Phase 0/1 리뷰 답변을 반영해 Legacy MyPage 호출부와 로그인 token 정책 확인, FanTalk source assertion 수동 삭제 사유를 기록했다. 문서 변경 후
./gradlew tasks --all은BUILD SUCCESSFUL,git diff --check HEAD는 무출력으로 통과했다. - 2026-07-14: Phase 2 Content Action 전환 후 전체
:app:testDebugUnitTest,:app:compileDebugKotlin,tasks --all,git diff --check HEAD가 통과했다. 전체 ktlint는 기존 main source 550줄 기준선 위반으로 실패했지만 Phase 2 변경 Kotlin 파일과 test source의 신규 위반은 0건이다. 연결된 Android 기기가 없어 실기기 수동 탭 검증은 후속 확인 항목으로 남겼다. - 2026-07-15: 사용자 수동 테스트 결과 Phase 3 사전 준비 3개와 시나리오 22개를
PASS로 확인했다. 미체크 시나리오M3-03,M3-04,M3-06,M3-15,M3-24,M3-26,M3-29,M3-30은 사용자 판단에 따라 테스트 불가능 또는 의미 없음으로 보아SKIPPED처리했으며,FAILED는 0개다. 모든 항목의 판정이 완료되어 Task 3.8과 Phase 3 수동 회귀 검증을 완료 처리했다.