fix(content): 성인 콘텐츠 설정 동기화와 국가별 인증 분기를 적용한다

This commit is contained in:
Yu Sung
2026-03-27 17:34:02 +09:00
parent 44daabdcae
commit 1d120b58bd
24 changed files with 1029 additions and 133 deletions

View File

@@ -0,0 +1,178 @@
# 20260326 회원정보 응답 확장 및 콘텐츠 보기 설정 연동
## 개요
- `/member/info` 응답 확장 필드(`countryCode`, `isAdultContentVisible`, `contentType`)를 앱 상태에 반영한다.
- 설정 화면의 `콘텐츠 보기 설정` 메뉴 노출 조건을 기존 `인증 사용자` 기준에서 `인증 사용자 또는 비한국 국가 코드`까지 확장한다.
- 콘텐츠 보기 설정 변경 시 `/member/content-preference`(`PATCH`)를 호출해 서버와 클라이언트 값을 동기화한다.
- 설정값 연타 시 `debounce`로 마지막 변경값만 서버에 전송하고, 전송 중에는 로딩 다이얼로그를 노출한다.
## 요구사항 요약
- `GET /member/info`
- 추가 응답 필드
- `countryCode: String` (접속 국가 코드)
- `isAdultContentVisible: Boolean`
- `contentType: ContentType`
- 메뉴 노출 규칙
- 현재 유지: `UserDefaults.bool(forKey: .auth) == true`이면 노출
- 추가: `countryCode != "KR"`인 경우에도 노출
- `PATCH /member/content-preference`
- 콘텐츠 보기 설정 변경 시 호출
- 응답 필드
- `isAdultContentVisible: Boolean`
- `contentType: ContentType`
- UX
- API 호출 시 Loading Dialog 표시
- 연속 입력 시 마지막 값만 서버 전송
## 완료 기준 (Acceptance Criteria)
- [x] AC1: `GetMemberInfoResponse``countryCode`, `isAdultContentVisible`, `contentType`를 디코딩한다.
- [x] AC2: `HomeViewModel.getMemberInfo`, `AppViewModel.getMemberInfo`에서 신규 필드가 `UserDefaults`에 저장된다.
- [x] AC3: `SettingsView``콘텐츠 보기 설정` 메뉴가 `auth == true || normalizedCountryCode != "KR"` 조건에서 노출된다.
- [x] AC4: `ContentSettingsView` 내 토글/라디오 변경 시 `/member/content-preference` `PATCH`가 호출된다.
- [x] AC5: 콘텐츠 설정 API 호출 중 `LoadingView`가 표시되고, 완료/실패 시 정상 해제된다.
- [x] AC6: 짧은 시간 내 연타(토글/라디오 연속 변경) 시 마지막 상태 1건만 전송된다.
- [x] AC7: 서버 응답 성공 시 로컬(`UserDefaults`) 상태가 최종값과 일치한다.
## 구현 체크리스트
### 1) 회원정보 응답 모델/저장 키 확장
- [x] `SodaLive/Sources/Settings/Notification/GetMemberInfoResponse.swift`
- `countryCode`, `isAdultContentVisible`, `contentType` 필드 추가
- 기존 디코딩 영향(옵셔널/기본값 정책) 점검
- [x] `SodaLive/Sources/Extensions/UserDefaultsExtension.swift`
- `UserDefaultsKey`에 국가 코드 저장 키 추가(예: `countryCode`)
### 2) `/member/info` 수신 데이터 저장 경로 확장
- [x] `SodaLive/Sources/Main/Home/HomeViewModel.swift`
- `getMemberInfo()` 성공 시 신규 3개 필드 저장 로직 추가
- [x] `SodaLive/Sources/App/AppViewModel.swift`
- `getMemberInfo()` 성공 시 신규 3개 필드 저장 로직 추가
- [x] 저장 정책 정리
- 국가 코드는 대문자 정규화(`uppercased`) 후 저장
- `contentType`는 서버값 우선 저장, 미존재/비정상 값은 `ALL` fallback 검토
### 3) 설정 메뉴 노출 조건 확장
- [x] `SodaLive/Sources/Settings/SettingsView.swift`
- 기존 `if UserDefaults.bool(forKey: .auth)` 조건을
`if isAuth || isNonKoreanCountry` 형태로 확장
- `isNonKoreanCountry` 계산 시 공백/소문자 입력 대비 정규화 처리
### 4) 콘텐츠 설정 PATCH API 추가
- [x] `SodaLive/Sources/User/UserApi.swift`
- `case updateContentPreference(request: UpdateContentPreferenceRequest)` 추가
- `path`: `/member/content-preference`
- `method`: `.patch`
- `task`: `.requestJSONEncodable(request)`
- [x] `SodaLive/Sources/User/UserRepository.swift`
- `updateContentPreference(...)` 메서드 추가
- [x] 신규 DTO 추가
- [x] `SodaLive/Sources/Settings/Content/UpdateContentPreferenceRequest.swift`
- [x] `SodaLive/Sources/Settings/Content/UpdateContentPreferenceResponse.swift`
### 5) 콘텐츠 설정 화면 상태/전송 로직 보강
- [x] `SodaLive/Sources/Settings/Content/ContentSettingsViewModel.swift`
- `@Published isLoading`, `errorMessage`, `isShowPopup` 추가
- 토글/라디오 변경 이벤트를 `Subject`로 수집
- `debounce` + `removeDuplicates`로 마지막 값만 전송
- API 성공 시 응답값 기준으로 로컬 상태 최종 확정
- API 실패 시 에러 토스트 노출 및 로딩 해제
- [x] `SodaLive/Sources/Settings/Content/ContentSettingsView.swift`
- `BaseView(isLoading: $viewModel.isLoading)` 적용으로 Loading Dialog 표시
- `.sodaToast(...)` 연결로 실패 메시지 표시
### 6) 회귀 영향 점검
- [x] `UserDefaults.isAdultContentVisible()` 및 기존 콘텐츠 조회 API 파라미터 경로(`HomeTabRepository`, `ContentRepository`, `SearchRepository` 등)에서 신규 저장값 반영 여부 점검
- [x] 앱 재시작 플래그(`AppState.shared.isRestartApp`)와 서버 동기화 타이밍 충돌 여부 점검
### 7) 검증 계획
- [x] 정적 진단: 수정 파일 `lsp_diagnostics` 확인
- [x] 빌드: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- [x] 빌드(개발): `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- [x] 테스트 시도: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test` / `SodaLive-dev test`
- [ ] 수동 QA
- [ ] 한국 계정(`countryCode == KR`, 미인증): 메뉴 비노출
- [ ] 비한국 계정(`countryCode != KR`, 미인증): 메뉴 노출
- [ ] 인증 계정(`isAuth == true`): 국가코드 무관 메뉴 노출
- [ ] 토글/라디오 연타 시 마지막 선택값만 서버 반영
- [ ] API 호출 중 로딩 다이얼로그 표시 및 완료 후 해제
### 8) 국가 기반 성인 접근 분기 및 18+ 확인 팝업
- [x] `SodaLive/Sources/Main/Home/HomeView.swift`
- 성인 라이브 진입 시 국가코드 분기 적용
- `KR(또는 빈값)` + 미인증: 기존 본인인증 팝업 유지
- `non-KR` + 민감 콘텐츠 OFF: `contentViewSettings` 이동 + 안내 팝업 노출
- [x] `SodaLive/Sources/Live/Now/All/LiveNowAllView.swift`
- `HomeView`와 동일한 국가 분기/가드 정책 반영
- [x] `SodaLive/Sources/Settings/Content/ContentSettingsViewModel.swift`
- 민감 콘텐츠 ON 전 18+ 확인 상태 추가
- `handleAdultContentToggleTap`, `confirmAdultContentAgeCheck`, `cancelAdultContentAgeCheck` 구현
- [x] `SodaLive/Sources/Settings/Content/ContentSettingsView.swift`
- 스위치 탭 동작을 뷰모델 핸들러로 연결
- `SodaDialog``아니오/예` 처리 연결(예: ON + API 흐름, 아니오: OFF 유지)
- [x] `SodaLive/Sources/I18n/I18n.swift`
- `adultContentAgeCheckTitle`, `adultContentAgeCheckDesc`, `adultContentEnableGuide` 국제화 문자열 추가
- [x] `SodaLive/Sources/Live/Room/LiveRoomViewModel.swift`
- `requiresAdultAuthenticationByCountry()` 도입
- 성인 방 진입 시 인증 요구 조건을 KR 기반으로 일관화
## 영향 파일(예상)
- `SodaLive/Sources/Settings/Notification/GetMemberInfoResponse.swift`
- `SodaLive/Sources/Extensions/UserDefaultsExtension.swift`
- `SodaLive/Sources/Main/Home/HomeViewModel.swift`
- `SodaLive/Sources/App/AppViewModel.swift`
- `SodaLive/Sources/Settings/SettingsView.swift`
- `SodaLive/Sources/User/UserApi.swift`
- `SodaLive/Sources/User/UserRepository.swift`
- `SodaLive/Sources/Settings/Content/ContentSettingsView.swift`
- `SodaLive/Sources/Settings/Content/ContentSettingsViewModel.swift`
- `SodaLive/Sources/Settings/Content/UpdateContentPreferenceRequest.swift` (신규)
- `SodaLive/Sources/Settings/Content/UpdateContentPreferenceResponse.swift` (신규)
## 리스크 및 의존성
- 백엔드가 `contentType` 문자열을 `ALL/MALE/FEMALE` 외 값으로 내려주면 디코딩 실패 가능성이 있어 방어 로직이 필요하다.
- `/member/content-preference` 응답/에러 정책이 미정이면 실패 시 롤백 기준(로컬 유지/복구) 정의가 필요하다.
- `countryCode` 미수신 시 기본 노출 정책(비노출 권장)을 명확히 정해야 메뉴 오노출을 방지할 수 있다.
## 검증 기록
- 일시: 2026-03-26
- 무엇: 회원정보 응답 확장/콘텐츠 설정 서버 동기화 작업을 위한 구현 계획 문서 작성
- 왜: 요청 범위(응답 필드 확장, 메뉴 노출 조건 변경, PATCH 연동, 로딩/디바운스)를 코드 경로 기준으로 실행 가능한 체크리스트로 정리하기 위함
- 어떻게: 기존 구현 파일(`UserApi`, `UserRepository`, `GetMemberInfoResponse`, `SettingsView`, `ContentSettingsViewModel`)과 기존 계획 문서 포맷을 조사해 항목화
- 실행 명령/도구: `read(docs/*)`, `grep("/member/info|getMemberInfo|isAdultContentVisible|contentType|debounce")`, `read(UserApi.swift, UserRepository.swift, SettingsView.swift, ContentSettingsViewModel.swift 등)`
- 결과: 구현 전용 체크리스트/완료 기준/검증 계획/리스크가 포함된 계획 문서 초안 작성 완료
- 일시: 2026-03-26
- 무엇: 회원정보 응답 확장 및 콘텐츠 보기 설정 서버 동기화 구현 완료
- 왜: `/member/info` 확장 필드 반영, 설정 메뉴 노출 조건 확장, `/member/content-preference` PATCH 연동, debounce/로딩 UX 요구사항을 충족하기 위함
- 어떻게: `GetMemberInfoResponse`/`UserDefaultsKey` 확장, `HomeViewModel`/`AppViewModel` 저장 로직 보강, `SettingsView` 노출 조건 변경, `UserApi`/`UserRepository` PATCH 추가, `ContentSettingsViewModel` Subject+debounce 동기화 및 `ContentSettingsView` 로딩/토스트 연결
- 실행 명령/도구:
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
- `lsp_diagnostics(수정 파일)`
- 결과:
- 두 스킴 Debug 빌드 모두 `BUILD SUCCEEDED`
- 테스트 명령은 두 스킴 모두 `Scheme ... is not currently configured for the test action`으로 실행 불가(테스트 타깃 미구성)
- `lsp_diagnostics`는 SourceKit 해석 범위 한계로 다수의 모듈 미해결 오류를 반환했으나, 실제 Xcode 빌드는 통과하여 컴파일 정상 확인
- 수동 QA는 현재 CLI 환경 한계로 미실행(체크리스트 유지)
- 일시: 2026-03-27
- 무엇: 국가 기반 성인 접근 분기 및 민감 콘텐츠 ON 18+ 확인 팝업 구현 검증
- 왜: 한국/비한국 정책 분기와 민감 콘텐츠 ON 보호 UX(국제화 포함) 요구사항을 반영하고, 실제 빌드 기준으로 회귀 여부를 확인하기 위함
- 어떻게:
- `HomeView`, `LiveNowAllView` 성인 진입 가드에 국가코드 분기 추가
- `ContentSettingsViewModel`/`ContentSettingsView`에 18+ 확인 다이얼로그 플로우 추가
- `I18n.Settings`에 신규 문구 추가 및 `LiveRoomViewModel` 성인 인증 조건을 KR 기반으로 정렬
- 실행 명령/도구:
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test`
- `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test`
- `lsp_diagnostics(I18n.swift, ContentSettingsViewModel.swift, ContentSettingsView.swift, HomeView.swift, LiveNowAllView.swift, LiveRoomViewModel.swift)`
- 결과:
- `SodaLive` 빌드는 최초 병렬 실행 시 `build.db` lock으로 실패했으나, 단독 재실행에서 `BUILD SUCCEEDED`
- `SodaLive-dev` 빌드는 `BUILD SUCCEEDED`
- 테스트는 두 스킴 모두 `Scheme ... is not currently configured for the test action`으로 실행 불가(테스트 액션 미구성)
- `lsp_diagnostics`는 SourceKit 모듈 해석 제약으로 다수 에러를 보고했으나, 실제 `xcodebuild` 통과로 컴파일 정상 확인
- 수동 QA는 CLI 환경 한계로 미실행(체크리스트 유지)