# PRD: 본인인증 후 콘텐츠 보기 설정 ## 문서 정보 | 항목 | 내용 | |---|---| | 문서 상태 | 코드 구현·빌드 검증 완료 / 실제 인증 통합 QA 대기 | | 작성일 | 2026-10-12 | | 최종 수정일 | 2026-10-12 | | 대상 제품 | SodaLive iOS 본인인증 완료 흐름 | | 작성자·결정권자 | 사용자 승인 완료 (2026-10-12) | | API Contract | 본 문서 §11 | | 구현 계획 | `docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md` | | 구현 승인 | **2026-10-12 사용자 “승인”으로 구현 허가. 현재 main 브랜치, worktree 미사용.** | ### 요구사항 상태 | 상태 | 의미 | 구현 처리 | |---|---|---| | 사용자 확정 | 사용자에게 직접 확인된 제품 흐름 | 별도 문서 승인 후 계획 Task의 기준으로 사용 | | 채택 관례 | 기존 코드 동작을 기본값으로 채택 | 명시된 한정 내 적용, 사용자 결정으로 표기하지 않음 | | 구현 근거 | 저장소 소스에서 확인한 현재 동작 | 설계 근거이며 런타임 성공 증거는 아님 | | 위험/검증 | 추가 제품 결정을 요구하지 않는 미검증 런타임 조건 | 구현 후 실제 앱 QA로 판정 | ## 1. Overview Bootpay 본인인증 완료 직후 인증 요청을 보내는 현재 흐름을, 사용자가 콘텐츠 보기 설정을 확인한 뒤 제출하는 흐름으로 바꾼다. 모든 인증 진입점에서 동일한 확인 sheet와 인증·설정 제출·회원정보 동기화 순서를 사용하며, 사용자의 선호가 인증 성별 값에 의해 덮어써지지 않게 한다. ## 2. Problem Statement - 현재 `MyPageViewModel.authVerify`는 인증 완료 직후 `POST /auth`를 보내고, 응답 gender로 콘텐츠 선호를 저장한다. - callback 진입점에서는 성공 callback으로 이어지는 동작이 있지만, `MyPage` 단독 진입은 앱 재시작 경로를 사용한다. - 기존 `ContentSettingsViewModel`은 설정 변경 즉시 로컬 저장과 debounce PATCH를 수행하고 재시작 상태도 설정한다. 미확정 draft에 재사용할 수 없다. - `AppViewModel` 및 `HomeViewModel`의 회원정보 조회는 완료 결과를 반환하지 않고 알림·분석 부수효과도 수행한다. 요청 직후 pending action을 실행하면 동기화 순서가 보장되지 않는다. ## 3. Goals ### 3.1 제품 목표 - 인증 완료 7개 진입점 모두에서 사용자가 설정을 확인하기 전까지 `POST /auth`를 보내지 않는다. - 사용자가 확인한 인증 영수증과 성인 콘텐츠 설정을 한 요청으로 제출한다. - callback 진입점은 인증 성공 후 회원정보를 받아 로컬 인증·콘텐츠 상태를 갱신한 뒤 기존 pending action을 한 번만 실행한다. 마이페이지는 기존 재시작 경로를 유지한다. ### 3.2 UX 목표 - 인증 직후 ON 상태와 전체 콘텐츠를 기본 선택한 설정 sheet를 표시한다. - 바깥 탭, drag, 뒤로/닫기로 확인 단계를 우회해 dismiss하지 못하게 한다. - 실패한 단계별 복구를 구분하고 재요청 또는 action 실행을 잘못 반복하지 않는다. ## 4. Non-Goals - 독립 콘텐츠 설정 화면의 기존 자동 저장/debounce PATCH 동작 변경. - 일반 회원정보 조회 helper 전체의 추상화·리팩터링. - Bootpay 자체 동작, 인증 서버 response schema, 외부 인증 계약의 변경. - 승인 전 앱 코드, UI, 스크립트 또는 프로젝트 설정 변경. - 재시도 idempotency 또는 영수증 만료 정책 보장. ## 5. Target Users and Permissions - 사용자: Bootpay 본인인증을 완료한 로그인 사용자. - 인증 receipt와 앱 token은 외부 입력/민감정보로 취급한다. 원문을 로그나 문서에 남기지 않는다. ## 6. 핵심 사용자 흐름 1. 사용자 또는 기존 기능이 Bootpay 본인인증을 시작한다. 2. Bootpay `onDone`에서 receipt를 보존하고 Bootpay full-screen cover를 닫는다. 3. cover dismiss가 완료된 뒤 사용자 확인을 위한 콘텐츠 설정 sheet를 연다. callback과 pending action도 이 전환 중 보존한다. 4. sheet에서 ON/OFF와 ON일 때 선호를 선택한다. 이 단계에서는 네트워크 요청과 설정 영속화를 하지 않는다. 5. 사용자가 `설정 완료`를 누르면 sheet를 먼저 닫고, 이어 `POST /auth`를 한 번 보낸다. 6. 인증·설정 제출 성공 후 마이페이지는 확인한 콘텐츠 설정을 로컬에 반영하고 기존 앱 재시작 경로로 이동한다. callback 진입점만 `UserRepository.getMemberInfo()` 응답을 기다려 기존 member response를 decode하고 로컬 상태를 갱신한다. 7. callback 진입점은 동기화 성공 뒤 보존된 성공 callback으로 pending action을 한 번 소비한다. 동기화 실패 시 callback을 실행하지 않고 마이페이지와 동일한 앱 재시작 경로로 이동한다. ## 7. 정보 구조와 라우팅 - 앱 route를 추가하지 않는다. 인증 callback 상태를 보존하는 화면 위 modal sheet로 제공한다. - 시작 전에 다른 full-screen cover가 열려 있으면 이를 먼저 dismiss 완료한 뒤 sheet를 표시한다. - sheet는 바깥 탭, swipe/drag, back 또는 close 제스처로 닫히지 않는다. 명시적 확인만 다음 단계로 진행한다. ## 8. 기능 요구사항 ### 8.1 진입점과 확인 순서 | ID | 상태 | 요구사항 | 수용 기준 | |---|---|---|---| | AUTH-001 | 사용자 확정 | 모든 7개 Bootpay 인증 완료 진입점은 확인 전 auth POST를 미룬다. | `MyPageView.swift`, `Main/Home/HomeView.swift`, `Home/HomeTabView.swift`, `Chat/ChatTabView.swift`, `Live/Now/All/LiveNowAllView.swift`, `V2/Main/MainView.swift`, `V2/CreatorChannel/CreatorChannelView.swift`에서 같은 순서를 확인한다. 확인 전 request·설정 저장 모두 0회다. | | AUTH-002 | 사용자 확정 | receipt, draft, action/callback을 cover-dismiss-sheet 표시 순서 동안 보존한다. | `onDone`이 sheet 또는 callback을 잃지 않으며 cover dismiss 완료 전에 sheet가 겹쳐 뜨지 않는다. `onClose` 기존 닫기 동작을 유지한다. | | AUTH-003 | 사용자 확정 | sheet는 확인 외 경로로 dismiss되지 않는다. | 배경 탭, drag, back 및 close 시트 dismiss 0회, 요청 0회다. | | AUTH-004 | 사용자 확정 | 초기 draft는 `isAdultContentVisible=true`, `contentType=ALL`이다. | 초기 렌더 상태와 확인 request가 이를 따른다. | | AUTH-005 | 채택 관례 | OFF 선택 시 `contentType=ALL`로 normalize한다. | 비활성 선호 값은 제출되지 않는다. `ALL` normalize는 기존 코드의 채택 관례이며 별도 사용자 결정은 아니다. | ### 8.2 제출, 동기화, 실패 | ID | 상태 | 요구사항 | 수용 기준 | |---|---|---|---| | AUTH-006 | 사용자 확정 | 확인 시 sheet를 닫은 뒤 combined JSON payload로 `POST /auth`한다. | §11 필드와 enum만 포함하고 중복 탭으로 동시 요청이 발생하지 않는다. | | AUTH-007 | 사용자 확정 | 인증 및 설정 제출 실패 시 오류를 보이고 같은 draft로 sheet를 다시 연다. | receipt와 pending action을 보존하고 사용자가 다시 누를 때만 재전송한다. 자동 제출은 없다. | | AUTH-008 | 사용자 확정 | 인증 성공 후 회원정보 동기화 실패 시 인증을 재전송하지 않는다. | sheet를 다시 열지 않고 pending action도 실행하지 않는다. My Page와 동일하게 `isRestartApp=true`, `setAppStep(.splash)`로 앱 재시작 경로에 보낸다. sync-only retry UI/정책은 추가하지 않는다. | | AUTH-009 | 사용자 확정 | 여섯 callback route는 auth 성공 후 `GET /member/info?container=ios`와 로컬 상태 갱신을 완료하고 callback action을 한 번 소비한다. | 동기화보다 callback이 앞서지 않는다. 일반 fire-and-forget member helper를 부른 직후 진행하지 않는다. | | AUTH-010 | 사용자 확정 | 기존 My Page 재시작 route를 유지하고 auth 응답 gender가 사용자의 content choice를 다시 덮어쓰지 않는다. | gender 기반 선호 저장 제거, callback 없음은 기존 재시작 흐름을 유지한다. | | AUTH-011 | 구현 근거 | shared auth flow에서 기존 `UserRepository.getMemberInfo()` 및 `GetMemberInfoResponse`를 재사용한다. | 성공 response의 기존 필드와 로컬 키를 사용하며 별도 response schema를 만들지 않는다. | ## 9. 반응형 기능 범위 - iPhone SwiftUI sheet만 포함한다. 웹/tablet 반응형 범위는 해당 없음. - bottom safe area 포함 Figma frame: ON 402x372, OFF 402x287. ## 10. UI/UX Expectations ### 10.0 표시 방식 변경 승인 (2026-10-12) - 사용자가 투명 `fullScreenCover`를 표시 컨테이너로 사용하는 커스텀 BottomSheet 전환을 승인했다. 아래 내용은 이전 native `.sheet`/고정 높이 기록보다 우선한다. - 화면 전체에는 기존 UI가 비치는 반투명 검정 배경을 두고 터치를 차단한다. 실제 패널은 좌우·하단에 붙으며 위쪽 모서리만 둥글고, 내용과 safe area에 필요한 높이만 차지한다. - ON/OFF `338/253` 고정 높이 및 접근성 크기에서 `.large`로 전환하는 규칙을 제거한다. 텍스트 줄바꿈과 선호 영역 유무에 따른 자연 높이를 사용한다. - 내용이 화면의 사용 가능한 높이를 초과할 때에만 본문을 스크롤하고 확인 버튼은 접근 가능하게 유지한다. - 확인으로 컨테이너가 닫힌 뒤 기존 `onDismiss` 전송을 실행한다. API·ViewModel·7개 진입점·오류 복구 정책은 변경하지 않는다. - 구현·검증 후속: 커스텀 패널 전환 완료. iOS26 실제 측정은 한국어 ON372pt/OFF287pt, 영어 ON383.67pt이며 고정값이 아닌 내용 높이다. 화면 초과 시 본문 스크롤, 뒤 화면 노출/입력 차단, 두 앱 빌드와 P4 독립 검토를 통과했다. 기존 실제 인증 통합 미검증은 유지한다. - Phase6 승인: 사용자가 배경의 올라오는 애니메이션 제거 방안을 확인하고 적용을 승인했다. fullScreenCover 기본 전환은 끄고 dim 배경은 제자리에 즉시 표시하며, 콘텐츠 패널에만 진입/퇴장 모션을 적용한다. - 패널 퇴장 완료 후 컨테이너를 닫고 기존 onDismiss 제출을 유지한다. ViewModel/API/개발용 mock 정책은 바꾸지 않는다. 동작 줄이기 환경에서는 수직 이동을 줄이고, 같은 닫힘 완료/1회 제출 계약을 유지한다. - 2026-10-13 Phase6 후속: 배경 고정·패널 단독 0.25초 진입/퇴장을 구현했다. 실제 native 중간 프레임, 빠른 확인/재표시/Reduce Motion 검사, 두 앱 빌드와 독립 기능·시각 검토가 통과했다. 제출은 host onDismiss에 유지된다. ### 10.1 시안과 문구 - Figma file `HmN1yNdJ3EIpqknFL0Hkab`, ON `2489:32150`, OFF `2489:32163`을 기준으로 한다. - 시안 링크: [ON](https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/SodaLive-UI-UX?node-id=2489-32150), [OFF](https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/SodaLive-UI-UX?node-id=2489-32163). - 고정 dark background `#202020`, accent `#00bdf7`, 흰색 제목, muted copy `#b5b5b5`, 좌우 padding 14pt. - 제목 18pt medium, 본문 16pt, 안내 caption 12pt. 기존 앱 font/assets와 `I18n.swift` 규칙을 따른다. - 화면 문구는 다음과 같이 고정한다. - `콘텐츠 보기 설정` - `민감한 콘텐츠 보기` - ON 설명: `성인인증 완료 후 성인 콘텐츠를 볼 수 있어요` - OFF 설명: `성인 콘텐츠가 목록과 검색에 노출되지 않아요` - ON일 때만 `선호 콘텐츠` 및 `전체` / `남성향` / `여성향` radio row 표시 - `마이 > 설정 > 콘텐츠 보기 설정에서 언제든 바꿀 수 있어요` - 확인 버튼 `설정 완료` ### 10.2 화면 상태 - sheet는 제출 전 draft만 보유한다. 확인 전 기존 설정 값을 변경하거나 PATCH하지 않는다. - auth+settings POST 실패는 sheet 재개, member info sync 실패는 sheet 재개 없이 restart fallback으로 분리한다. ## 11. API 계약 ### 11.0 개발 타깃 성공 시뮬레이션 승인 (Phase 5) - 사용자가 앱 흐름 테스트 목적의 모의 인증을 승인했다. 실제 서버 인증 검사 API가 거절될 수 있다는 한계를 수용했다. - `SodaLive-dev` Debug/Release에만 `AUTH_VERIFICATION_MOCK` 컴파일 조건을 적용한다. 일반 `SodaLive`는 두 구성 모두 실제 Bootpay/인증 API를 유지하며 모의 실행 코드가 제외된다. - 7개 본인인증 UI에서 개발 모드는 Bootpay를 열지 않고 약 0.5초 로딩 후 기존 ON/ALL 설정 패널로 진행한다. 설정 완료 후 닫힘 완료를 기다리는 기존 순서를 유지한다. - 모의 인증 전송은 실제 POST 없이 정상 성공 응답을 제공해 기존 공통 성공 흐름을 재사용한다. 가짜 receipt를 서버로 보내거나 서버 인증 상태를 바꾸지 않는다. - 후속 회원정보·마이페이지 조회는 정상 서버 응답에서 `isAuth`, `isAdultContentVisible`, `contentType`만 모의 상태로 보정한다. 잔액·역할·개인정보·알림 등 다른 필드는 원본을 유지하고, 실제 조회의 HTTP/API/디코딩 실패를 성공으로 바꾸지 않는다. - 모의 상태는 현재 로그인 세션에 한정한다. 앱 내부 Splash 재시작에는 유지하고, 로그아웃/401 초기화 및 로그인 세션 변경 때 해제한다. 실제 앱 프로세스 종료 후 모의 인증을 영구 유지하는 기능은 만들지 않는다. - 일반 콘텐츠 설정에서 이후 변경한 값을 오래된 모의 선택값으로 덮어쓰지 않도록 상태를 맞춘다. 새 설정 UI/모의 제어 메뉴/서버 권한 우회는 범위 밖이다. - 구현 후속: Phase5 완료. dev Debug/Release만 모의 flag 적용, 표준 Debug 바이너리 mock 심볼 제외, native 로딩/패널 4개 검사, 실제 helper/repository/VM 격리 검사와 두 Debug 앱 빌드 통과. 같은 로그인 세션의 인증 전 GET 지연 응답 회귀를 수정했다. 실제 회원 GET·일반 설정 PATCH는 서버 연결을 유지하며 서버 권한 부여는 하지 않는다. 별도 `api-contract.md`는 만들지 않는다. 아래 계약은 사용자 확인 사항이며 backend 실행/응답은 독립 검증하지 않았다. ### 11.1 인증과 설정 제출 | 항목 | 계약 | |---|---| | 요청 | `POST /auth` | | JSON | `receipt_id: String`, `isAdultContentVisible: Bool`, `contentType: ALL \| MALE \| FEMALE` | | 의미 | Bootpay receipt와 확인된 콘텐츠 설정을 한 번에 제출 | | 성공 decode | 기존 `ApiResponse` 사용, gender 외 새 response field/schema를 가정하지 않음 | | 실패 | transport/API/decode/비성공 envelope를 auth+settings 제출 실패로 처리, 오류 표시와 보존 draft로 사용자 재시도 | | 기존 재시작 진입점 | 성공 시 확인된 설정을 로컬 반영하고 기존 `isRestartApp=true`, `.splash` 전환. 재시작 전에 회원정보 GET을 추가하지 않음 | ### 11.2 callback 진입점의 회원정보 동기화 | 항목 | 계약 | |---|---| | 요청 | `GET /member/info?container=ios` | | response | 기존 `ApiResponse` | | 성공 처리 | `can`, `point`, `auth`, `role`, `isAuditionNotification`, `countryCode`, `isAdultContentVisible`, `contentPreference` 로컬 값 갱신 후 pending action 소비 | | 실패 처리 | sheet 재개 및 auth 재전송 금지. pending action 미실행. `isRestartApp=true` 설정 후 `.splash` 전환 | | 재시작 조회 근거 | `ContentView`가 `isRestartApp=true`일 때 MainView를 제거하고, `SplashView`가 해당 플래그를 해제하면 새 MainView의 `hasLoadedMainData=false` 초기화 경로가 member GET을 호출한다. 실제 조회/로컬 반영은 앱 QA로 확인한다. | | 범위 제약 | Splash 자체에는 member GET이 없다. 앱 active 조회도 `alreadyUpdatedMarketingInfo` 조건에 묶여 있어 단독 동기화 근거로 삼지 않는다. 이번 실패 처리에서 자동 재시작 루프를 추가하지 않는다. | ## 12. 보안과 데이터 취급 - receipt와 token 원문을 문서, 로그, analytics에 기록하지 않는다. - 본 auth flow의 `onDone`에서 receipt payload를 dump하는 기존 `DEBUG_LOG`를 제거한다. 무관한 전체 repo 로그 정리는 하지 않는다. - draft는 확인 전 네트워크와 영속화로 보내지 않는다. ## 13. 성능과 품질 요구사항 - 인증 완료 7 entrypoint, dismiss 차단, sheet 닫힘 후 POST, 중복 확인 방지와 실패 단계별 복구는 수동 앱 QA로 검증한다. - XCTest bundle이 없으므로 test target/framework를 추가하지 않는다. - Foundation 기반 assert self-check는 실제 `AuthVerifyRequest`와 `ContentType` 소스를 compile/run해 serializer payload를 확인한다. 흐름 테스트를 대체하지 않는다. - `SodaLive` 및 `SodaLive-dev` 양쪽 Debug build를 검증한다. - 재시도 idempotency, receipt 유효기간/만료 동작은 미검증 위험으로 기록하며 보장하지 않는다. ## 14. 성공 기준 ### 14.1 기능 수용 기준 - [ ] 일곱 진입점 모두 확인 전 auth POST와 설정 영속화가 0회다. (`AUTH-001`~`AUTH-005`) - [ ] 확인 시 sheet가 먼저 닫히고 정확한 combined payload POST는 한 번만 발생한다. (`AUTH-006`) - [ ] POST 실패 뒤 receipt/draft/action을 보존한 sheet를 재개하고 자동 제출이 없다. (`AUTH-007`) - [ ] member info 실패 시 auth 재전송 0회, sheet 재개 0회, pending action 실행 0회이며 restart fallback이 동작한다. (`AUTH-008`~`AUTH-010`) - [ ] restart fallback이 실제 member refresh를 발생시키고 auth/content 설정을 반영함을 QA에서 확인한다. 소스상 refresh trigger는 런타임 성공 증거가 아니다. ### 14.2 UI/UX 수용 기준 - [ ] Figma ON/OFF, 고정 문구, font scale, radio 표시, safe area가 실제 화면과 일치한다. - [ ] 외부 탭·drag·back·close가 confirmation을 우회하지 않는다. ### 14.3 승인 및 추적성 - [x] PRD와 plan-task 별도 문서 승인을 받기 전 구현을 시작하지 않는다. - [ ] 승인 후 요구사항이 Task와 수동/자동 검증 증거에 연결된다. ## 15. Open Questions - 제품 결정 미결 항목 없음. 요구사항은 사용자 확인 사항이다. - backend combined payload 지원은 사용자 확인에 근거하며 직접 호출하거나 독립 검증한 사실이 아니다. - retry idempotency, receipt 만료, restart 통한 member refresh 실동작은 제품 결정이 아닌 구현/운영 검증 위험이다. ## 16. 요구사항 추적표 | 범위 | 계획 Phase | Goal | 자동 검증 | 수동 검증 | |---|---:|---|---|---| | `AUTH-001`~`AUTH-005` | 2 | `P2-T1` | 실제 request serializer assert, 정적 caller 대조 | 일곱 entrypoint, dismiss, 미확인 network/persistence 0회 | | `AUTH-006`~`AUTH-008` | 2 | `P2-T1` | combined payload serializer assert | duplicate confirm, POST 실패 뒤 값 보존·수동 retry | | `AUTH-009`~`AUTH-011` | 2 | `P2-T1` | source review, response decode 경로 | GET 실패 때 restart, auth 재전송 0회, action 0회; restart member refresh 성공 확인 | | 전체 | 3 | `P3-GATE` | serializer, 두 Debug builds | 최종 흐름 및 두 시안 QA | ## 17. Decision Log | 날짜 | ID | 상태 | 결정 | 근거 | 영향 | |---|---|---|---|---|---| | 2026-10-12 | DEC-001 | 사용자 확정 | 모든 일곱 auth completion을 confirm sheet 뒤로 이동하고 최초 ON/ALL로 시작 | 사용자 확인 | `AUTH-001`~`AUTH-005`, `P2-T1` | | 2026-10-12 | DEC-002 | 사용자 확정 | 확인으로 닫은 뒤 combined auth/settings 제출, 여섯 callback은 회원정보 동기화 후 action 1회 소비 | 사용자 확인 | §11, `AUTH-006`, `AUTH-009` | | 2026-10-12 | DEC-003 | 사용자 확정 | POST 실패는 sheet/draft 재개, 회원정보 실패는 재전송 없이 restart fallback | 최신 사용자 확인 | `AUTH-007`~`AUTH-008` | | 2026-10-12 | DEC-004 | 채택 관례 | OFF일 때 `contentType=ALL` | 기존 ContentSettingsViewModel normalize | `AUTH-005` | ## 18. 변경 관리 - 요구사항 변경은 Decision Log와 요구사항 표, §11 계약, plan-task를 함께 갱신한다. - 승인 전 앱 코드는 변경하지 않는다. 사용자가 이 PRD와 계획 문서를 명시적으로 승인하면 구현 시작 허가로 처리하고, 승인 기록만 갱신한 뒤 현재 브랜치에서 계획을 실행한다. 별도 추가 승인을 요구하지 않는다. ## 인터뷰 요약 - 사용자 확정 흐름 및 제약을 기준으로 정리했다. 승인되지 않은 제안은 없다. - 범위/목표/제약/성공 기준 명확도 1.0, 기존 코드 문맥 0.9. - 모호도는 휴리스틱 추정 `0.015 ≈ 0.02` (`1 - (목표×0.30 + 범위×0.20 + 제약×0.15 + 성공×0.20 + 문맥×0.15)`)이며 측정값이 아니다. 문서/구현 승인은 아직 부여되지 않았다. ## 문서 작성 검증 - 2026-10-12: PRD와 계획 샘플, 정책 및 지정 소스 경로를 읽고 요구사항/구현 근거를 분리해 기록했다. backend 호출, 빌드, 앱 실행은 하지 않았다. - 2026-10-12: 마이페이지의 기존 재시작과 callback 경로의 GET 동기화를 분리하고, 문서 승인 한 번을 구현 시작 허가로 처리하도록 사용자 지시와 대조했다. 변경 범위와 공백 오류를 확인했으며 구현 승인은 여전히 대기 중이다. - 2026-10-12 후속: 사용자가 문서를 승인했다. 이전 승인 대기 기록은 이력으로 보존하며, 승인된 범위의 구현·검증을 시작한다. - 2026-10-12 구현 후속: 7개 인증 진입점과 결합 전송·단계별 복구를 구현했다. 실제 DTO/VM 격리 실행, 네이티브 sheet UI 검사, 두 앱 Debug 시뮬레이터 빌드가 통과했다. 실제 인증 7개 경로와 실패 재시작 후 실서버 회원정보 갱신은 QA 환경이 없어 미검증이며 최종 수용 기준을 일괄 완료 처리하지 않는다. 상세 증거와 한계는 plan-task의 Progress 및 `reviews/phase2-auth-content-settings.md`에 기록한다.