# 메인 홈/콘텐츠 페이지 로그인 및 성인 콘텐츠 가드 Plan/Task ## Assumptions - "메인 홈, 콘텐츠 페이지"는 v2 `HomeMainFragment`, `ContentMainFragment`를 의미한다. - "이동시 guard"는 두 Fragment에서 발생하는 실제 화면 이동(`startActivity`)을 의미한다. - 로그인 여부는 기존 코드와 동일하게 `SharedPreferenceManager.token.isBlank()` 기준으로 판단한다. - 로그인 화면 이동은 기존 `MainV2Activity.showLoginActivity()`를 재사용한다. - 성인 콘텐츠 이동 여부를 판단할 수 있는 경우에는 로그인 이후 본인인증/성인콘텐츠 설정 가드를 적용한다. - 접속 국가가 한국인지 여부는 `SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR"` 기준으로 판단한다. - 한국 접속자는 `SharedPreferenceManager.isAuth`가 `true`일 때 본인인증 완료로 판단한다. - 한국 외 접속자는 본인인증 여부와 무관하게 성인 콘텐츠 설정 확인 단계로 진행한다. - 성인 콘텐츠 표시 설정은 `SharedPreferenceManager.isAdultContentVisible` 기준으로 판단한다. ## Success Criteria - v2 메인 전용 이동 guard helper가 추가된다. - `HomeMainFragment`의 모든 실제 화면 이동이 helper를 통과한다. - `ContentMainFragment`의 모든 실제 화면 이동이 helper를 통과한다. - 성인 콘텐츠로 판별 가능한 이동은 login -> country/auth -> adult setting 순서로 helper를 통과한다. - helper는 `SharedPreferenceManager.countryCode`, `SharedPreferenceManager.isAuth`, `SharedPreferenceManager.isAdultContentVisible`, `ContentSettingsActivity` 이동을 포함한다. - 레거시 `HomeFragment`, `MainActivity`, `BaseFragment`는 수정하지 않는다. - 지정된 source test, 컴파일, ktlint, diff whitespace 검증이 통과한다. ### Phase 1: 현재 이동 지점과 기존 로그인 진입점 확인 - [x] **Task 1.1: `HomeMainFragment` 이동 지점 확인** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt` - 확인 대상: - `openHomeOnAirLive` - `openFollowingChat` - `onBannerClick` - `onRecentActivityClick` - `onAiCharacterClick` - `openCreatorProfile` - `openAudioContentDetail` - `openPopularCommunityPost` - 검증 기준: 실제 `startActivity` 호출 지점을 목록화하고, `Unit` placeholder handler는 이동 대상에서 제외한다. - 검증 기록: - 2026-06-27: source 확인 결과 위 함수들이 실제 화면 이동 지점이다. `onLiveClick`, `onFollowingSectionMoreClick`, `onFollowingLiveClick`, `onFollowingScheduleClick`, `onFollowingNewsClick`은 현재 `Unit` 또는 이동 없음이므로 이번 가드 적용 대상이 아니다. - [x] **Task 1.2: `ContentMainFragment` 이동 지점 확인** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt` - 확인 대상: - `onBannerClick` - `openAudioContentDetail` - `openSeriesDetail` - `openRankingAudioContentDetail`은 `openAudioContentDetail`로 위임됨 - 검증 기준: 실제 `startActivity` 호출 지점과 위임 구조를 목록화한다. - 검증 기록: - 2026-06-27: source 확인 결과 `onBannerClick`, `openAudioContentDetail`, `openSeriesDetail`이 실제 화면 이동 지점이다. 랭킹/카드 클릭은 이 함수들로 위임되므로 최종 이동 함수에 가드를 적용하면 된다. - [x] **Task 1.3: 기존 로그인 진입점 확인** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt` - 확인 대상: `MainV2Activity.showLoginActivity()` - 검증 기준: `SharedPreferenceManager.token.isBlank()` 조건에서 `LoginActivity`를 시작하는 기존 메서드를 재사용할 수 있다. - 검증 기록: - 2026-06-27: `MainV2Activity.showLoginActivity()`는 token blank 조건에서 `LoginActivity`를 시작한다. helper는 이 메서드를 호출하는 방식으로 계획한다. - [x] **Task 1.4: 기존 성인 콘텐츠/본인인증 정책 확인** - 파일: - `app/src/main/java/kr/co/vividnext/sodalive/common/AdultContentVisibilityPolicy.kt` - `app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt` - `app/src/main/java/kr/co/vividnext/sodalive/settings/ContentSettingsActivity.kt` - 확인 대상: - `AdultContentVisibilityPolicy.shouldShowAdultRestrictionSetting(countryCode, isAdultContentVisible, isAuth)` - `SharedPreferenceManager.countryCode` - `SharedPreferenceManager.isAuth` - `SharedPreferenceManager.isAdultContentVisible` - `ContentSettingsActivity` - `Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE` - 검증 기준: v2 helper가 사용할 기존 정책/설정 값을 목록화한다. - 검증 기록: - 2026-06-27: `countryCode` 기본값은 `KR`이고, blank country도 `KR`로 취급하는 기존 패턴을 확인했다. `ContentSettingsActivity`는 `Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE`가 `true`이면 민감 콘텐츠 안내 Toast를 표시한다. ### Phase 2: v2 메인 전용 이동 guard helper 추가 - [x] **Task 2.1: helper source test 추가** - 파일: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuardSourceTest.kt` - 검증 기준: - helper가 `SharedPreferenceManager.token.isBlank()`를 사용한다. - helper가 `(activity as? MainV2Activity)?.showLoginActivity()`를 사용한다. - helper가 `SharedPreferenceManager.countryCode.ifBlank { "KR" }`를 사용한다. - helper가 한국 접속자에 대해 `SharedPreferenceManager.isAuth`를 확인한다. - helper가 한국 접속 미인증 사용자에게 `SodaDialog` 본인인증 안내를 표시한다. - helper가 본인인증 진입에 `Auth.auth`를 직접 재사용한다. - helper가 `SharedPreferenceManager.isAdultContentVisible`를 확인한다. - helper가 `ContentSettingsActivity`로 이동할 때 `Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE`를 `true`로 전달한다. - helper에 레거시 `MainActivity` 의존이 포함되지 않는다. - 실행 명령: ```bash ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest" ``` - 기대 결과: 구현 전에는 helper 파일 부재로 실패한다. - 검증 기록: - 2026-06-27: `MainV2LoginGuardSourceTest`를 추가하고 구현 전 실행했다. `MainV2LoginGuard.kt` 파일 부재로 실패해 RED 상태를 확인했다. - [x] **Task 2.2: helper 구현** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt` - 구현 방향: ```kotlin fun Fragment.ensureMainV2NavigationAllowed( requiresAdultContentAccess: Boolean = false, onAllowed: () -> Unit ) { if (SharedPreferenceManager.token.isBlank()) { (activity as? MainV2Activity)?.showLoginActivity() return } if (requiresAdultContentAccess) { val isKoreanCountry = SharedPreferenceManager.countryCode.ifBlank { "KR" } == "KR" if (isKoreanCountry && !SharedPreferenceManager.isAuth) { showMainV2AuthDialog() return } if (!SharedPreferenceManager.isAdultContentVisible) { startActivity( Intent(requireContext(), ContentSettingsActivity::class.java).apply { putExtra(Constants.EXTRA_SHOW_SENSITIVE_CONTENT_GUIDE, true) } ) return } } onAllowed() } ``` - 검증 기준: Task 2.1 source test가 통과한다. - 검증 기록: - 2026-06-27: `MainV2LoginGuard.kt`를 추가했다. token blank 시 `MainV2Activity.showLoginActivity()`, 성인 콘텐츠 요청 시 한국/본인인증 확인, `SodaDialog` 본인인증 안내, `Auth.auth`, `ContentSettingsActivity` 민감 콘텐츠 안내 extra, 최종 `onAllowed()` 흐름을 포함한다. - 2026-06-27: 리뷰 지적에 따라 `Auth.auth` 완료 콜백에서 `BootpayResponse`를 파싱하고 `AuthVerifyRequest`를 생성해 `MyPageViewModel.authVerify()`로 서버 검증 후 `SplashActivity`를 재시작하는 기존 인증 완료 후처리를 추가했다. - 2026-06-27: 코드 리뷰에서 `Auth.auth` 성공 콜백 후처리 누락을 지적받았고, 같은 Phase 2 범위에서 기존 패턴과 동일한 서버 본인인증 검증 및 상태 갱신 흐름으로 보완했다. - [x] **Task 2.3: 한국 접속 미인증 사용자 처리 방식 확정** - 파일: - `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2LoginGuard.kt` - 필요 시 v2 main 하위 신규 auth guide helper - 확인 대상: - 기존 `SodaDialog` + `Auth.auth` 흐름을 v2 helper에서 직접 재사용한다. - Fragment별 callback 위임 방식은 사용하지 않는다. - 검증 기준: 레거시 파일 수정 없이 v2 main helper 범위에서 본인인증 안내/진입을 처리한다. - 검증 기록: - 2026-06-27: 사용자 결정에 따라 한국 접속 미인증 사용자 본인인증 안내 흐름은 v2 helper에서 직접 재사용하는 것으로 확정했다. ### Phase 3: HomeMainFragment 이동 지점에 로그인 가드 적용 - [ ] **Task 3.1: HomeMainFragment source test 추가** - 파일: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragmentLoginGuardSourceTest.kt` - 검증 기준: - `HomeMainFragment`가 `ensureMainV2NavigationAllowed`를 import/use 한다. - Task 1.1의 실제 화면 이동 함수들이 helper를 통해 `startActivity`를 실행한다. - invalid route/id return 조건은 가드보다 먼저 유지된다. - 성인 콘텐츠 여부를 판단할 수 없는 홈 이동은 `requiresAdultContentAccess = false` 또는 기본값을 사용한다. - 실행 명령: ```bash ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.home.HomeMainFragmentLoginGuardSourceTest" ``` - 기대 결과: 구현 전에는 helper 사용 부재로 실패한다. - [ ] **Task 3.2: HomeMainFragment 적용** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt` - 구현 방향: - `ensureMainV2NavigationAllowed { startActivity(...) }` 형태로 감싼다. - `route ?: return`, invalid id return은 기존 위치를 유지한다. - 홈 모델에서 성인 콘텐츠 여부를 판단할 수 있는 필드가 확인되면 `requiresAdultContentAccess = true`를 전달한다. - 성인 콘텐츠 여부를 판단할 수 없는 이동에는 임의 adult 판정을 추가하지 않는다. - `BaseFragment`, legacy `MainActivity`, legacy `HomeFragment`는 수정하지 않는다. - 검증 기준: Task 3.1 source test가 통과한다. ### Phase 4: ContentMainFragment 이동 지점에 로그인 가드 적용 - [ ] **Task 4.1: ContentMainFragment source test 추가** - 파일: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragmentLoginGuardSourceTest.kt` - 검증 기준: - `ContentMainFragment`가 `ensureMainV2NavigationAllowed`를 import/use 한다. - `onBannerClick`, `openAudioContentDetail`, `openSeriesDetail`이 helper를 통해 `startActivity`를 실행한다. - invalid id return 조건은 가드보다 먼저 유지된다. - `showAdultBadge` 또는 `isAdult`에서 유래한 성인 콘텐츠 여부가 helper의 `requiresAdultContentAccess`로 전달된다. - 실행 명령: ```bash ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.content.ContentMainFragmentLoginGuardSourceTest" ``` - 기대 결과: 구현 전에는 helper 사용 부재로 실패한다. - [ ] **Task 4.2: ContentMainFragment 적용** - 파일: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/content/ContentMainFragment.kt` - 구현 방향: - 최종 이동 함수인 `onBannerClick`, `openAudioContentDetail`, `openSeriesDetail`에서 `ensureMainV2NavigationAllowed { startActivity(...) }`를 사용한다. - `ContentAudioCardUiModel.showAdultBadge`, `ContentCommentedAudioUiModel.showAdultBadge`, 전체 탭 audio/series item의 `showAdultBadge`를 성인 콘텐츠 가드 입력으로 전달한다. - 랭킹 아이템이나 배너처럼 성인 콘텐츠 여부를 판단할 수 없는 이동은 로그인 가드만 적용한다. - 랭킹/카드 클릭 위임 구조는 유지한다. - 검증 기준: Task 4.1 source test가 통과한다. ### Phase 5: 통합 검증 - [ ] **Task 5.1: v2 main source test 실행** - 실행 명령: ```bash ./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.*" ``` - 기대 결과: v2 main 관련 source/unit test 통과. - [ ] **Task 5.2: 컴파일 검증** - 실행 명령: ```bash ./gradlew :app:compileDebugKotlin ``` - 기대 결과: Kotlin debug compile 통과. - [ ] **Task 5.3: ktlint 검증** - 실행 명령: ```bash ./gradlew :app:ktlintCheck ``` - 기대 결과: ktlint 통과. - [ ] **Task 5.4: diff whitespace 검증** - 실행 명령: ```bash git diff --check ``` - 기대 결과: whitespace error 없음. ## Verification Log - 2026-06-27: 사용자 요청에 따라 이번 작업은 문서 생성만 수행한다. source/test/helper 구현 및 Gradle 검증은 아직 실행하지 않는다. - 2026-06-27: 사용자 요청에 따라 본인인증/성인 콘텐츠 설정 가드를 문서 범위에 추가했다. 구현은 아직 수행하지 않는다. - 2026-06-27: 한국 접속 미인증 사용자 본인인증 안내 흐름은 v2 helper에서 기존 `SodaDialog` + `Auth.auth` 패턴을 직접 재사용하는 것으로 확정했다. - 2026-06-27: Phase 1/2 진행 결과 `MainV2LoginGuardSourceTest`와 `MainV2LoginGuard`를 추가했다. 구현 전 단일 source test는 helper 파일 부재로 실패했고, 구현 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`가 통과했다. - 2026-06-27: 리뷰 지적에 따라 빈 `Auth.auth` 콜백을 금지하는 source test를 추가한 뒤 RED를 확인했고, 서버 검증/상태 갱신 후처리 구현 후 `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest"`와 `./gradlew :app:compileDebugKotlin`이 통과했다. - 2026-06-27: Phase 1/2 코드 리뷰 지적을 반영한 뒤 검증을 재수행했다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2LoginGuardSourceTest"`, `./gradlew :app:compileDebugKotlin`, `./gradlew :app:ktlintCheck`, `git diff --check`는 모두 통과했다.