From 1b50f559bd50c7230a22aadc509bd05c3845970c Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Tue, 13 Oct 2026 11:37:26 +0900 Subject: [PATCH] =?UTF-8?q?docs(auth):=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD?= =?UTF-8?q?=EA=B3=BC=20=EA=B2=80=EC=A6=9D=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 385 ++++++++++++++++++ .../prd.md | 252 ++++++++++++ .../reviews/phase2-auth-content-settings.md | 86 ++++ .../reviews/phase4-intrinsic-bottom-panel.md | 59 +++ .../reviews/phase5-dev-auth-simulation.md | 60 +++ .../phase6-fixed-backdrop-panel-motion.md | 52 +++ 6 files changed, 894 insertions(+) create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase2-auth-content-settings.md create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase4-intrinsic-bottom-panel.md create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase5-dev-auth-simulation.md create mode 100644 docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase6-fixed-backdrop-panel-motion.md diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md new file mode 100644 index 00000000..20da9070 --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md @@ -0,0 +1,385 @@ +# 본인인증 후 콘텐츠 보기 설정 계획 + +## 기준 문서 + +- PRD: `docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md` +- API 기준: PRD §11, 별도 `api-contract.md` 없음 +- 상태: **코드 구현·빌드 검증 완료 / 실제 인증 통합 QA 대기** +- 현재 브랜치: `main`, worktree 사용 안 함 +- 현재 Phase: Phase 3 실제 인증 통합 QA 대기 +- 구현 허가: **2026-10-12 사용자 “승인” 확인. 현재 브랜치에서 승인된 구현·검증을 진행한다.** + +## 목표 + +7개 진입점에서 설정 확인 후 제출하고 member sync 뒤 기존 action을 한 번 이어간다. + +## 현재 상태 + +| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 | +|---:|---|---:|---|---| +| 1. 승인된 계약과 serializer | 완료 | `2/2` | 없음 | 없음 | +| 2. sheet·진입점·동기화 | 코드 구현 완료 / 통합 QA 대기 | `0/1` (코드/격리 검사 완료) | `P2-T1` | 실제 인증 7개 진입점 및 실패 재시작 QA | +| 3. 최종 검증 | 자동 검증 완료 / 통합 QA 대기 | `1/2` | `P3-QA` | 승인된 테스트 계정·유효한 인증 영수증·실패 응답 제어 환경 | + +- 코드·격리 검증과 실서버 통합 QA를 구분한다. 미실행 통합 QA가 포함된 Task는 완료로 표시하지 않는다. +- 현재 남은 goal은 `P3-QA`이며 실제 인증 QA 환경이 필요하다. + +## 범위 + +### 포함 + +- Phase 1: §11 combined payload와 실제 `AuthVerifyRequest`/`ContentType` 기반 Foundation assert check. +- Phase 2: presentation-only sheet, 7 entrypoint, pending callback/action, 동기화 및 단계별 실패 처리. +- Phase 3: source check, 수동 app QA, 두 Debug target 빌드. + +### 제외 + +- 승인 범위 밖의 코드·프로젝트 설정 변경 및 실제 계정의 임의 인증/설정 서버 변경. +- standalone 콘텐츠 설정 자동 저장 변경, 일반 member helper 대규모 리팩터링. +- 테스트 타깃 또는 테스트 프레임워크 추가, 신규 API/response schema. +- sync-only retry UI와 idempotency/receipt expiry 보장. + +## 기술적 제약과 검증 원칙 + +- Sheet/상태/요청: `SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift`, `SodaLive/Sources/MyPage/MyPageViewModel.swift`, `SodaLive/Sources/MyPage/Auth/AuthVerifyRequest.swift`, `SodaLive/Sources/MyPage/Auth/AuthRepository.swift`. +- Phase 2에 7개 caller를 모두 나열하고 새 소스는 `SodaLive.xcodeproj/project.pbxproj`의 두 app target에 등록한다. +- standalone `ContentSettingsViewModel` 동작은 보존하고 미확정 draft에 재사용하지 않는다. 일반 member helper는 broad refactor하지 않으며 shared flow가 `UserRepository.getMemberInfo()` 완료를 기다린다. +- member response에서 갱신할 local keys: `can`, `point`, `auth`, `role`, `isAuditionNotification`, `countryCode`, `isAdultContentVisible`, `contentPreference`. gender는 선택 선호를 덮지 않는다. +- OFF는 기존 코드 관례로 `contentType=ALL` normalize. 사용자 확정이 아니다. +- receipt/token/payload 원문을 기록하지 않고, 이 흐름의 raw `onDone` log만 제거한다. +- XCTest bundle이 없어 신규 framework/target 없이 실제 모델 Foundation assert를 쓰고, flow/caller/dismiss/callback/duplicate 및 양쪽 Debug build는 앱에서 수동 검증한다. +- Backend combined support는 사용자 확인이나 독립 검증은 하지 않았다. idempotency/receipt expiry를 보장하지 않고 live-server 위험으로 남긴다. + +## 실행 전 승인 Gate + +**2026-10-12 사용자 “승인”으로 통과했다.** + +- [x] 사용자에게 PRD와 plan-task에 대한 명시적 문서 승인을 받는다. 이 승인을 구현 시작 허가로 처리한다. +- [x] 승인 시점과 범위를 기록하고 `main`에서 계획을 실행한다. 별도 추가 승인이나 worktree 생성 없이 진행한다. + +## Phase 1: 승인된 계약과 serializer + +**Phase 결과:** 승인된 PRD 계약을 `AuthVerifyRequest`가 표현하고, 실제 앱 모델로 combined payload를 검증한다. + +**선행조건:** 사용자 문서 승인(구현 시작 허가). 승인 전 문서 외 변경 없음. + +**Phase 완료 조건:** `P1-T1`, `P1-T2`, `P1-GATE` 완료. 현재 serializer 검증 전이므로 완료 아님. + +### Task 1.1 기준 문서 작성 + +**Goal 실행 `P1-T1`:** PRD에 사용자 확정 요구사항, 기존 코드 근거, 실패 단계, API 계약 및 승인 gate를 추적 가능하게 남긴다. + +- 시작 조건: 지정 샘플·정책·소스 확인. 완료 증거: PRD와 source review, 문서 외 변경 없음. +- TDD 예외: 문서 전용 작업이다. 실제 작성·diff 확인만 완료 처리한다. + +**Files:** + +- Create: `docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md` +- Read: `MyPage/MyPageViewModel.swift`, `MyPage/Auth/{AuthVerifyRequest,AuthRepository}.swift`, `Settings/Content/{ContentSettingsViewModel,UpdateContentPreferenceRequest,ContentType}.swift`. +- Read: `Settings/Notification/GetMemberInfoResponse.swift`, `User/UserRepository.swift`, `App/AppViewModel.swift`, `Main/Home/HomeViewModel.swift`, `Extensions/UserDefaultsExtension.swift`, `Splash/SplashView.swift`, `ContentView.swift`. + +- [x] 지정 source/작성 규칙을 확인하고 요구사항·§11 계약·승인 gate를 PRD에 기록한다. 별도 `api-contract.md`는 만들지 않는다. +- [x] 문서 외 수정이 없음을 diff로 확인한다. + +### Task 1.2 payload serializer 구현 + +**Goal 실행 `P1-T2`:** 승인된 receipt와 설정 값을 실제 `AuthVerifyRequest` 모델에서 JSON으로 직렬화해 §11 계약을 만족한다. + +- 시작 조건: Task 1.1 및 사용자 문서 승인·구현 허가. 완료 증거: 실제 모델 Foundation assert와 RED/GREEN. +- 범위 밖: 새 response schema, mock serializer, 별도 test target/framework. +- Files: + - Modify: `SodaLive/Sources/MyPage/Auth/AuthVerifyRequest.swift` + - Confirm: `SodaLive/Sources/Settings/Content/ContentType.swift` + - Create: `work/scripts/check-auth-content-settings.swift` (`@main`) +- Interfaces: + - Consumes: PRD §11.1의 `receipt_id`, `isAdultContentVisible`, `contentType` 계약. + - Produces: `AuthVerifyRequest`의 JSON encoder 출력과 runnable assert check. + +- [x] **RED:** check를 작성해 실제 모델의 JSON key/type과 ON/ALL, ON/MALE, ON/FEMALE, OFF/ALL 값을 assert한다. 실제 receipt/token을 fixture로 사용하지 않는다. +- [x] **RED 확인:** `swiftc SodaLive/Sources/MyPage/Auth/AuthVerifyRequest.swift SodaLive/Sources/Settings/Content/ContentType.swift work/scripts/check-auth-content-settings.swift -o "${TMPDIR%/}/opencode/check-auth-content-settings" && "${TMPDIR%/}/opencode/check-auth-content-settings"`를 실행한다. 새 필드/initializer 부재로 인한 compile 또는 assertion 실패를 기록한다. 임시 부모 경로 존재 여부를 먼저 확인한다. +- [x] **GREEN:** 실제 `AuthVerifyRequest`에 설정 필드를 추가하고 기존 `receipt_id` CodingKey를 보존한다. +- [x] **GREEN 확인:** 같은 compile/run 명령이 exit 0으로 완료되고 실제 encoder JSON key/type/enum을 assert함을 확인한다. +- [x] **REFACTOR:** 해당 없음이면 건너뛰고 불필요한 추상화를 추가하지 않는다. 실행 결과를 Progress에 기록한다. + +### Phase 1 완료 조건 및 Gate + +- [x] 두 Task 증거, 명시적 사용자 문서 승인, serializer RED/GREEN 및 diff check를 확인한다. + +**Goal 실행 `P1-GATE`:** 승인된 계약과 앱 모델 serializer의 일치 여부를 판정한다. + +- 시작 조건: 두 문서 명시 승인, Phase 1 Task 완료. +- 완료 증거: assert check 성공, 승인 시점/범위 기록. +- 승인 전 실행하지 않는다. + +## Phase 2: 설정 sheet, 일곱 진입점과 순차 동기화 + +**Phase 결과:** 모든 인증 완료 진입점에서 확인 전 제출을 차단하고, 확인/동기화 완료 후 기존 action만 한 번 실행한다. + +**선행조건:** `P1-GATE` 완료. backend support는 사용자 확인에 기반하고 live runtime QA가 남는다. + +**Phase 완료 조건:** `P2-T1`, app QA 및 restart 동기화 증거 완료. + +### Task 2.1 shared confirm-before-submit 흐름 + +**Goal 실행 `P2-T1`:** 공용 presentation-only sheet와 shared auth state가 draft·receipt·pending action을 보존하고, 제출·회원정보·복구 순서를 보장한다. + +- 시작 조건: `P1-GATE`, 기존 response/helper 및 Figma 확인. 완료 증거: source 검사, 앱 QA, 두 Debug build. +- TDD 예외 사유: SwiftUI modal presentation/callback lifecycle을 현행 XCTest target에서 자동 실행할 수 없음. +- 대체 검증: 7 caller POST 순서, 확인 전 요청/저장 0회, 단계별 request/screenshot, source diff와 두 build 기록. payload는 Task 1.2의 실제-model assert로 검증. +- callback 순서: 모든 `onDone`에서 원 receipt와 action/callback을 먼저 보존하고 Bootpay cover를 닫는다. cover dismiss completion 다음 sheet 표시. 확인에서는 draft/action을 보존한 채 sheet를 먼저 닫고 POST한다. `onClose`는 기존 취소/닫기 의미를 유지하며 인증 성공 action을 실행하지 않는다. +- UI: PRD §10 copy/design 및 Figma ON/OFF를 따른다. ON에서만 선호 radio 행, OFF에서 숨김. + +**Files:** + +- Create: `SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift` +- Modify: `SodaLive/Sources/MyPage/MyPageViewModel.swift` +- Modify: `SodaLive/Sources/MyPage/Auth/AuthVerifyRequest.swift` +- Modify: `SodaLive/Sources/MyPage/Auth/AuthRepository.swift` +- Modify: `SodaLive/Sources/MyPage/MyPageView.swift` (My Page, callback 없음, 기존 restart route) +- Modify: `SodaLive/Sources/Main/Home/HomeView.swift` +- Modify: `SodaLive/Sources/Home/HomeTabView.swift` +- Modify: `SodaLive/Sources/Chat/ChatTabView.swift` +- Modify: `SodaLive/Sources/Live/Now/All/LiveNowAllView.swift` +- Modify: `SodaLive/Sources/V2/Main/MainView.swift` +- Modify: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` +- Confirm: `SodaLive/Sources/User/UserRepository.swift` (기존 완료 대기 가능한 publisher를 그대로 사용) +- Confirm: `SodaLive/Sources/Extensions/UserDefaultsExtension.swift` (기존 저장 함수/키를 그대로 사용) +- Modify: `SodaLive/Sources/I18n/I18n.swift` (기존 localization 방식으로 요청 copy 추가) +- Modify: `SodaLive.xcodeproj/project.pbxproj` (신규 sheet를 양쪽 app target에 등록. 독립 self-check는 앱 소스로 등록하지 않음) +- Confirm: `SodaLive/Sources/Settings/Notification/GetMemberInfoResponse.swift`, `Settings/Content/ContentType.swift`, `App/AppState.swift`, `Splash/SplashView.swift`, `ContentView.swift`, `App/SodaLiveApp.swift` +- Test: Task 1.2의 `work/scripts/check-auth-content-settings.swift` + +**Interfaces:** + +- Consumes: Task 1.2의 확장된 `AuthVerifyRequest`; 기존 `UserRepository.getMemberInfo()`와 `ApiResponse`. +- Produces: sheet draft(`isAdultContentVisible`, `contentType`), receipt 및 단일 pending action을 shared auth flow에 전달하는 시작/확인 계약. My Page는 POST 성공과 설정 로컬 반영 후 바로 재시작한다. 여섯 callback route만 GET 완료 후 action closure를 실행한다. +- 동기화 local key: `can`, `point`, `auth`, `role`, `isAuditionNotification`, `countryCode`, `isAdultContentVisible`, `contentPreference`. + +- [x] **변경 전 확인:** 현재 일곱 caller와 `authVerify`에서 확인 UI 없이 POST하고 즉시 callback/재시작으로 진행하는 소스 경로를 기록한다. 자동 serializer의 RED는 완료한 Task 1.2 증거를 참조하며 이미 GREEN인 검사를 다시 실패시키지 않는다. +- [x] **GREEN:** presentation-only sheet, 7개 caller의 cover-dismiss-sheet 전환, 확인 한 번의 POST, 기존 callback/restart action을 구현한다. standalone `ContentSettingsViewModel`은 수정하지 않는다. +- [x] **GREEN 동기화:** callback 경로만 POST 성공 후 기존 repository의 member GET을 구독하고, 성공 decode와 여덟 로컬 키 저장 이후 callback을 실행한다. GET의 transport/API/decode 실패는 callback과 인증 재전송 없이 마이페이지와 같은 재시작 액션으로 처리한다. 마이페이지는 GET 없이 확인된 설정을 로컬 반영하고 재시작한다. +- [x] **GREEN 확인:** serializer assert를 통과시키고 단계 순서/source diff를 대조한다. transport/API/decode 실패는 POST 실패 복구로 연결하고, POST 성공 뒤 gender 기반 선호 덮어쓰기는 없어야 한다. +- [x] **REFACTOR:** 이번 변경의 중복만 정리하고 실제-model serializer check를 다시 실행한다. member helper broad refactor 금지. +- [ ] POST 실패 앱 QA: 오류가 보이고 sheet가 receipt/draft/pending action 그대로 다시 열리며 재요청은 사용자 확인 동작 때만 발생한다. + +**수동 QA 필수 시나리오:** + +- [ ] 일곱 화면 전부에서 cover-dismiss-sheet 순서, confirmation 전 POST/설정 저장 0회, 배경/drag/back/close dismiss 거부. +- [ ] ON/ALL 시작, OFF radio 숨김/ALL normalize, 연속 확인에도 sheet 닫힘 후 POST 1회. +- [ ] transport/API/decode 실패: 오류와 draft/receipt/action 보존, 사용자 확인 전 재전송 0회. +- [ ] callback 경로 POST·GET 성공: 로컬 auth/content 갱신 뒤 callback 1회. My Page는 POST 성공·설정 로컬 반영 후 GET 없이 기존 restart route 유지, gender 선호 덮어쓰기 없음. +- [ ] GET 실패: sheet 재개 0, auth 재전송 0, pending action 0. restart 후 member GET과 auth/content 반영 실동작 확인. +- [ ] Figma 화면/copy/safe area 대조 및 raw `onDone`/receipt/token/payload 로그 부재 확인. + +## Phase 3: 최종 검증 + +**Phase 결과:** serializer와 두 앱 target 빌드, 요구 흐름 및 restart sync runtime evidence를 갖춘다. + +**선행조건:** Phase 2 구현 및 기기/시뮬레이터 수동 QA 환경. + +### Task 3.1 문서 범위 및 자동 검증 + +**Goal 실행 `P3-GATE`:** 승인된 요구사항만 구현했는지와 serializer/build 통과 여부를 확인한다. + +- 시작 조건: `P2-T1` 완료. 완료 증거: assert check, diff check, 두 Debug builds 및 Progress 기록. +- TDD 예외: release gate이며 독립 구현이 아니다. 문서 requirement/source 대조와 명령 결과를 대체 증거로 기록한다. + +**Files:** + +- Confirm: `work/scripts/check-auth-content-settings.swift` +- Confirm: `SodaLive.xcodeproj/project.pbxproj` +- Confirm: 두 app source target 전체 diff + +- [x] 실제 모델 serializer compile/run check exit 0, `git diff --check` 오류 0건을 기록한다. +- [x] `SodaLive` workspace Debug 시뮬레이터 빌드 exit 0을 확인한다. 실제 destination/아키텍처 옵션은 아래 Progress에 기록한다. +- [x] `SodaLive-dev` workspace Debug 시뮬레이터 빌드 exit 0을 확인한다. 실제 destination/아키텍처 옵션은 아래 Progress에 기록한다. +- [x] 양쪽 target의 신규 소스 등록을 확인한다. + +### Task 3.2 실제 앱 최종 Gate + +**Goal 실행 `P3-QA`:** 최종 앱 QA에서 POST/GET 순서, 실패 동작, action 중복 방지 및 재시작 refresh를 확인한다. + +- 시작 조건: Task 3.1 성공 및 QA 앱 실행 가능. 완료 증거: Phase 2 QA, Figma screenshot, restart refresh/로컬 반영. +- 예외: provider callback, SwiftUI 전환, network/lifecycle은 XCTest 재현 불가. Phase 2 수동 QA를 실행한다. +- 확인 파일: 7개 caller, `MyPage/MyPageViewModel.swift`, `App/AppViewModel.swift`, `Main/Home/HomeViewModel.swift`, `V2/Main/MainView.swift`, `App/SodaLiveApp.swift`, `Splash/SplashView.swift`, `ContentView.swift`. +- [ ] Phase 2 수동 QA 전체 통과 및 receipt/token 원문 로그가 없음을 기록한다. + +**Expected:** 모든 성공 기준 증거가 있다. POST 실패는 사용자가 재확인할 때만 재전송하며, GET 실패는 auth 재전송·잘못된 action 실행 없이 재시작으로 복구한다. 실패나 미실행은 성공으로 기록하지 않고 Phase를 미완료로 둔다. + +## 실행 순서와 의존성 + +`P1-T1` (완료) → 사용자 승인 → `P1-T2` → `P1-GATE` → `P2-T1` → `P3-GATE`/`P3-QA`. +승인 차단 중에는 문서 외 변경을 하지 않는다. 미검증 런타임은 gate 미완료로 둔다. + +## 위험과 런타임 확인 + +- Backend combined JSON 지원은 사용자 확인 사항이나 현재 세션에서 독립 검증하지 않았다. live server에서 실제 승인 환경으로 검증하되 receipt/token은 기록하지 않는다. +- 재시도 idempotency와 영수증 만료는 확인되지 않았다. 보장으로 표현하지 않는다. +- 재시작 조회 근거는 `ContentView:22`의 MainView 제거 → `SplashView:73`의 restart 플래그 해제 → 새 `MainView:59`의 `hasLoadedMainData=false` 및 token 검사 → `HomeViewModel.getMemberInfo()` 호출이다. 앱 active 조회는 marketing 상태 조건에 묶여 있으므로 보장 경로가 아니다. GET 실패 뒤 재시작 후 GET 발행·로컬 반영을 실제 앱에서 확인하며, 자동 재시작 루프는 추가하지 않는다. +- provider `onDone` 직후 fullScreenCover dismiss와 sheet presentation 간 callback/draft 유실 가능성은 모든 caller에서 수동으로 확인한다. + +## Progress + +### Phase 6: 배경 고정과 패널 애니메이션 분리 + +**Goal `P6-T1`:** dim 배경이 화면 아래에서 올라오지 않고 제자리에 표시되며 패널만 진입/퇴장한다. + +- 승인: 사용자 “적용한다.” 확인. PRD §10.0 Phase6 모션 계약 적용. +- Files: `SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift`; 기존 PRD/계획/검증 기록. VM·API·7개 caller·mock·프로젝트 설정 변경은 제외한다. +- 구현 기준: UI 내부 컨테이너 표시와 패널 표시를 분리하고 컨테이너의 시스템 전환만 비활성화한다. 패널 퇴장 완료 후 host 닫힘/onDismiss에서 기존 제출을 실행하며, 고정 시간 후 서버 전송 같은 우회는 추가하지 않는다. +- [x] 변경 전 모션에서 host/dim이 함께 올라오는 것을 확인하고, 배경 고정·패널 이동 프레임 및 퇴장 이후 callback 기준을 마련한다. +- [x] 최소 iOS16.6에서 컴파일되는 패널 전환/완료 연결을 구현하고 기존 intrinsic 높이·safe area·오류 재표시를 유지한다. +- [x] native 진입/중간/완료 프레임에서 dim은 정지하고 패널만 이동함을 확인한다. 배경 탭·드래그 차단, 확인/닫힘 callback1회, 빠른 확인 및 재표시를 검증한다. +- [x] ON/OFF 높이·overflow·동작 줄이기, 모의 로딩→패널 흐름 및 일반 인증 로직 회귀를 검사한다. +- [x] 두 앱 빌드·독립 기능/시각 검토와 QA 정리를 완료한다. + +**TDD 예외:** SwiftUI 모션은 별도 native QA host에서 실제 View/modifier의 프레임과 callback 순서를 검사한다. 앱에 테스트 타깃이나 모션용 외부 의존성은 추가하지 않는다. 이전 실서버 통합 미검증은 유지한다. + +### Phase 5: SodaLive-dev 전용 인증 성공 시뮬레이션 + +**Goal `P5-T1`:** 개발 타깃에서 실제 본인인증 없이 로딩→설정 패널→성공 후 재시작/대기 동작을 테스트한다. + +- 승인: 사용자가 서버 보호 API의 거절 가능성을 수용하고 권장 흐름대로 작업 승인. PRD §11.0이 이번 개발 모드에 적용된다. +- 대상: `SodaLive.xcodeproj/project.pbxproj`, 기존 인증 7개 View, 신규 `SodaLive/Sources/V2/Component/Modal/AuthVerificationView.swift`, 신규 `SodaLive/Sources/V2/Auth/AuthVerificationMock.swift`, `MyPage/Auth/AuthRepository.swift`, `User/UserRepository.swift`, `Extensions/UserDefaultsExtension.swift`, 최소 `work/tests` 검사. +- API/표시 계약: 공용 View는 기존 각 caller의 cancel/error/done/close 동작을 유지하며 dev에서만 로딩 후 모의 완료를 전달한다. repository에서 모의 POST 성공을 제공해 기존 `MyPageViewModel` 성공 분기를 그대로 사용한다. +- [x] **RED:** 실제 모의 상태/응답 변환을 검사하는 최소 assert 검사를 추가하고 미구현 실패를 확인한다. +- [x] **GREEN:** dev 전용 condition, 인증 모의 성공, 계정별 메모리 상태와 정상 self-member 응답의 세 필드 보정을 구현한다. +- [x] **GREEN UI:** 7개 caller에서 공용 인증 View로 real/mock 경로를 선택하며, 로딩→패널 및 기존 닫힘 후 전송 흐름을 유지한다. +- [x] 세 필드 외 값·서버 실패 보존, 세션 reset, 이후 콘텐츠 설정 변경 보존, 생산 빌드에서 mock 제외를 검사한다. +- [x] 두 앱 Debug 빌드 및 두 타깃 Debug/Release compilation conditions, 기존 회귀·실제 VM 드라이버를 검증한다. +- [x] native QA로 로딩→실제 패널→설정 완료/닫힘 콜백을 확인하고, 실제 repository/VM 격리 실행으로 공통 성공 분기를 확인한다. 두 증거가 실서버 통합 검증은 아님을 구분하고 리뷰/QA 정리를 기록한다. + +**중단 기준:** 개발 흐름 성공과 일반 앱 mock 제외의 실행 증거가 확보되고, 관련 회귀·빌드·리뷰가 통과하면 종료한다. 실제 서버 권한을 우회하거나 실제 계정의 인증 상태를 임의 변경하지 않는다. + +#### Task P5-R1: 모의 상태의 늦은 응답 회귀 방지 + +- 시작 조건: `reviews/phase5-dev-auth-simulation.md` REV-P5-001/002의 실제 저장소 드라이버 재현. +- Files: `SodaLive/Sources/V2/Auth/AuthVerificationMock.swift`, `SodaLive/Sources/User/UserRepository.swift`, `work/tests/AuthVerificationMockCheck.swift`; 임시 실제 repository driver. +- [x] RED: 같은 로그인 세션에서 인증 전 시작한 GET이 인증 성공 뒤 도착해 `auth=false`/기존 선택으로 되돌아가는 사례를 assert로 고정한다. +- 제외(후속 판정): preference A→B/B→A는 repository 단독 재현만 확인했으며 실제 설정 UI의 로딩 차단을 포함한 사용자 경로는 확정하지 못했다. 요청 순서의 추가 상태·처리는 이번 최소 수정에서 제외하고 판정 이력을 리뷰에 남긴다. +- [x] GREEN: 로그인 세션 식별과 모의 인증 활성/선택 상태를 분리한다. 같은 로그인 세션의 인증 전 요청은 응답 시점의 활성 모의 상태로 보정하고, 계정 변경·동일 token 재로그인·서버 오류 보존 규칙은 유지한다. +- [x] 실제 helper/repository RED→GREEN, 기존 real/mock 검사와 생산 코드 제외 검사를 재실행한다. + +완료 증거: 늦은 인증 전 GET 뒤 `true/FEMALE`, 기존 계정 격리·후속 설정 성공 검사 통과; 일반 앱 동작 변경 없음. 신규 UI/영구 저장/실서버 권한 변경은 범위 밖이다. + +### Phase 4: 콘텐츠 높이 커스텀 BottomSheet 전환 + +**Goal `P4-T1`:** 고정 높이 native sheet를 투명 fullScreenCover 내부의 콘텐츠 높이 하단 패널로 교체한다. + +- 시작 조건: 2026-10-12 사용자가 “해당 방식으로 진행” 승인. 이전 구현 및 검증 기록은 보존한다. +- Files: `SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift`; 본 PRD/계획. 네트워크·ViewModel·7개 caller 변경은 제외한다. +- TDD 예외: UI 레이아웃/표시 변경이며 기존 임시 native QA 앱으로 변경 전/후 geometry·터치·콜백을 검증한다. 앱에 새 테스트 타깃을 추가하지 않는다. +- [x] `.sheet`, detents 고정 높이, `.large` 분기를 제거하고 투명 컨테이너/하단 intrinsic 패널로 바꾼다. +- [x] ON/OFF 및 긴 문구에서 내용 높이만 차지함을 확인한다. 화면보다 긴 내용은 본문만 스크롤하고 확인 버튼을 유지한다. +- [x] 실제 네이티브 캡처에서 기존 UI가 배경 너머 보이고 패널 좌우·하단에 floating 여백이 없음을 검증한다. +- [x] 배경/드래그 닫힘 차단과 확인 1회→닫힘 완료 콜백 1회를 native QA로 검증한다. 오류/로딩 연결은 소스 대조로 보존을 확인하며 실제 toast 런타임은 기존 미검증으로 남긴다. +- [x] 두 앱 스킴 빌드 및 serializer/기존 회귀 검사, 독립 검토, 임시 QA 앱·프로세스 정리를 완료한다. + +**검증 범위:** 이번 표현 방식 변경에 대한 로컬 UI 검증이며, 기존 `P3-QA`의 실제 계정/실서버 통합 미검증은 계속 별도로 남긴다. + +### `P1-T1` 문서 작성 1차 실행 — 2026-10-12 + +- 상태: 완료 (문서 및 지정 소스 확인만) +- 무엇을/왜: 사용자 확정 흐름과 source 근거를 기록해 승인 전 구현을 막고 추적한다. +- 어떻게: 샘플/정책/source read, 초기 git status/diff 확인. xcodebuild/backend/app QA 미실행. +- 남은 항목/다음: 사용자 문서 승인 및 모든 구현/QA 체크. 문서 승인이 구현 시작 허가이며, 승인 전 문서 검토 대기. + +## Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 | +|---|---|---|---|---|---| +| 2026-10-12 | DEC-001 | 사용자 확정 | 일곱 인증 완료 진입점에서 sheet 확인 전 제출 금지, ON/ALL 시작, 확인 외 dismiss 금지 | PRD 사용자 확인 | `P2-T1` | +| 2026-10-12 | DEC-002 | 사용자 확정 | sheet 닫은 뒤 combined JSON 제출, callback 여섯 route는 member sync 후 action 1회 | PRD 사용자 확인 | `P1-T2`, `P2-T1` | +| 2026-10-12 | DEC-003 | 사용자 확정 | POST 실패는 draft sheet 재개 후 사용자 재시도, GET 실패는 sheet 없이 auth/action 차단 후 기존 restart route | 최신 사용자 확인 | `P2-T1`, `P3-QA` | +| 2026-10-12 | DEC-004 | 채택 관례 | OFF는 `contentType=ALL`로 normalize | 기존 `ContentSettingsViewModel` | `P1-T2`, `P2-T1` | +| 2026-10-12 | DEC-005 | 사용자 확정 | native sheet의 고정 높이/외곽을 투명 fullScreenCover와 콘텐츠 높이의 전폭 하단 패널로 교체 | 표시 방식 설명 후 “해당 방식으로 진행” 승인 | PRD §10.0, `P4-T1` | + +## 발견된 문제 및 미확정 위험 + +| ID | 상태 | 내용 | 영향 Goal | 처리 | +|---|---|---|---|---| +| RISK-001 | 런타임 검증 대기 | Splash는 member fetch를 하지 않음. restart가 GET/설정 반영을 보장하는지 미검증. | `P3-QA` | 실제 GET/로컬 반영 전 gate 미통과 | +| RISK-002 | live-server 검증 대기 | combined payload는 사용자 확인, idempotency/receipt expiry는 미검증. | `P1-GATE`, `P3-QA` | 승인 환경 QA, 보장 주장 금지 | + +## 문서 검증 기록 + +- 2026-10-12: 지정한 AGENTS/policy/sample 및 auth, content settings, member response/repository, restart 관련 소스를 읽었다. 신규 문서 2개만 작성했다. backend 호출, xcodebuild, 앱 실행은 하지 않았다. +- 2026-10-12: 문서 길이와 체크 상태를 확인하고 승인되지 않은 구현 체크는 미완료로 유지했다. +- 2026-10-12: 요구사항 대조 후 마이페이지에 추가 GET을 넣지 않는 기존 재시작 분기, 단일 문서 승인 게이트, 재시작 후 실제 member 조회 경로, 기존 workspace 빌드 명령을 보정했다. 각 신규 문서에 `git diff --no-index --check`로 공백 오류가 없음을 확인했다. 문서 두 개 외 수정은 없다. +- 2026-10-12 후속: 사용자 “승인” 확인. 구현 시작 허가로 처리한다. `xcodebuild -workspace "SodaLive.xcworkspace" -list`에서 두 앱 스킴과 의존성 구성을 확인했다. `work/tests`에 기존 gift 관련 Swift assert 검사가 있고, 본 인증 흐름의 기존 검사는 없다. 앱 코드 구현은 다음 단계부터 진행한다. + +### `P1-T2`, `P1-GATE` — 2026-10-12 + +- 상태: 완료. 실제 DTO 직렬화 check는 변경 전 `extra arguments at positions #2, #3 in call`로 RED, 변경 후 4개 조합의 JSON key/type/value 검사로 GREEN(exit 0)을 확인했다. 검증 스크립트는 `work/scripts/check-auth-content-settings.swift`이며 앱 소스로 등록하지 않았다. +- coordinator도 같은 실제 DTO·ContentType 소스를 재컴파일해 4개 조합 PASS를 확인했다. 인증 헤더·서버 response schema는 변경하지 않았다. + +### `P2-T1` 코드 및 격리 실행 — 2026-10-12 + +- 상태: 코드 구현 완료, 실서버 통합 QA 미완료. 7개 caller, 공용 sheet, DTO/repository/VM, I18n, 두 타깃 등록을 반영했다. 독립 설정 화면의 autosave는 변경하지 않았다. +- 실제 `MyPageViewModel`, `AuthRepository`, response/DTO/UserDefaults extension을 native Combine과 임시 transport/AppState/UserDefaults stubs로 컴파일한 드라이버를 실행했다. 잘못된 receipt 6종, POST 실패 6종, GET 실패 7종, 확인 전 무요청/무저장, 중복 차단, 요청 snapshot, 8개 키 저장 후 callback 1회, GET 실패 재시작 1회가 PASS다. stub 재시작은 실제 앱 재생성 증거가 아니다. +- 임시 드라이버: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/CheckAuthFlow.swift`, 의존성 `AuthFlowDependencies.swift`, 실행 파일 `check-auth-flow-coordinator`. 실제 Moya/실서버 호출은 하지 않았다. +- 실제 sheet/modifier·font·I18n·색상/간격 소스로 별도 네이티브 QA 앱을 빌드했다. iPhone 17 Pro / iOS 26에서 한국어 ON/OFF, 영어 ON/OFF PNG를 캡처하고 XCTest 2개(exit 0)로 toggle/radio/swipe-dismiss 차단/확인 후 닫힘을 검증했다. 임시 host의 VM·toast는 stubs로 실제 인증/오류 토스트 증거는 아니다. +- QA 증거: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-settings-qa/EVIDENCE.md`, `interactions.log`, `NativeInteractions.xcresult`, `ko-on-all.png`, `ko-off-all.png`, `en-on-all.png`, `en-off-all.png`. +- 시각 독립 검토는 APPROVE. iOS 26 native sheet 외곽이 Figma Android처럼 edge-to-edge가 아닌 floating/inset 형태임을 명시했으며 pixel-perfect 일치를 주장하지 않는다. + +### `P3-GATE` 자동 검증 — 2026-10-12 + +- 상태: 완료. coordinator가 변경 Swift 전체 `swiftc -frontend -parse`, 프로젝트 `plutil -lint`, `git diff --check`, 기존 `for test in work/tests/check-*.sh; do bash "$test" || exit $?; done`를 실행해 모두 exit 0을 확인했다. 기존 suite 중간 실행 한 번은 동시 빌드 부하로 timeout 후, 전체 재실행을 통과했다. +- `SodaLive-dev` 14:37:23~14:46:37, `SodaLive` 14:46:56~14:53:05 빌드 exit 0. coordinator가 14:55에 현재 소스로 양쪽 incremental build를 다시 실행해 모두 exit 0을 확인했다. +- 두 빌드 공통 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme SCHEME -configuration Debug -destination 'platform=iOS Simulator,id=E9FC7721-AA96-440F-8349-2DC4B85F40F5' -derivedDataPath DERIVED_DATA -disableAutomaticPackageResolution CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES ARCHS=x86_64 -quiet build`. SCHEME은 `SodaLive-dev`, `SodaLive`; DERIVED_DATA는 각각 임시 opencode의 `SodaLive-dev-commit-build`, `SodaLive-commit-build`이다. +- 빌드 로그: 임시 opencode의 `SodaLive-dev-debug-simulator-20261012-143723.log`, `SodaLive-debug-simulator-20261012-144656.log`. 기존 Pods 경고, 기존 Compile Sources 중복, Crashlytics dSYM 경고는 이번 범위 밖이며 수정하지 않았다. +- `lsp_diagnostics` 도구는 제공되지 않아 실행하지 못했다. Swift 파싱·별도 타입 검사·두 앱 실제 컴파일/링크 증거로 구분해 기록한다. + +### `P3-QA` 독립 리뷰와 남은 검증 — 2026-10-12 + +- 상태: **[blocked] 실제 인증 통합 QA 환경 필요**. Oracle은 확정 production defect 없음으로 평가했으나, 7개 실제 Bootpay caller의 presentation/오류 복구 및 GET 실패 후 실제 MainView 재생성→member GET→로컬 저장 증거가 없어 최종 Gate 승인을 보류했다. +- 리뷰: `reviews/phase2-auth-content-settings.md`. 코드 수정 확정 항목은 없다. 증거 부족을 코드 결함으로 오인해 추가 리팩터링하지 않는다. +- 재개 조건: 유효한 인증 영수증을 얻을 수 있는 승인된 QA 계정/기기와 POST·GET 실패를 제어할 수 있는 테스트 환경. 실서버 작업 권한과 테스트 계정 없이 사용자의 실제 인증/설정을 임의 변경하지 않는다. + +### 네이티브 배경 탭 추가 검증 및 정리 — 2026-10-12 + +- 임시 XCTest `testKoreanControlsAndDismissal`에 dimmed 영역 좌표 `(0.5, 0.2)` 실제 탭을 추가해 focused test를 실행했다. `NativeBackdrop.xcresult`: 1 test, 0 failures, exit 0. 탭 후 sheet 유지와 확인 버튼 hit 가능, 최종 확인/닫힘 각각 1회가 확인됐다. +- 실제 캡처에서 배경 탭 직후 `confirmed=0;dismissed=0`을 확인했다. `auth-settings-qa/backdrop.log`에 탭 좌표와 event synthesis, `backdrop-attachments/E275BEC9-9405-431F-8D10-413BF658E5C6.png`에 화면 증거가 있다. production sheet hash는 기존 캡처와 동일하다. +- QA 임시 앱 `com.sodalive.authsettings.qa`와 전용 UI runner를 simulator에서 제거하고, 이번 작업에서 부팅한 `E9FC7721-AA96-440F-8349-2DC4B85F40F5`를 종료했다. `xcrun simctl list devices booted`에 booted 기기 없음, `pgrep -fl 'xcodebuild|AuthSettingsQA'`에 남은 프로세스 없음. 증거 파일은 임시 opencode 경로에 보존했다. +- 현재 브랜치 `main`, 커밋/푸시/worktree 생성 없음. 최종 상태는 코드·로컬 검증 완료이며 `P3-QA`의 실서버 통합 항목은 [blocked]다. + +### `P4-T1` 콘텐츠 높이 패널 — 2026-10-12 + +- 상태: **P4 범위 완료**. 생산 코드 변경은 공용 `AuthContentSettingsSheet.swift` 한 파일에 한정했다. 사용자 추가 thumb 20×20 frame을 보존했다. 기존 API·VM·7개 caller는 이번 단계에서 변경하지 않았다. +- 구현: 투명 fullScreenCover/검정 0.6 dim/상단 모서리만 라운드/좌우·하단 밀착. `ViewThatFits`의 자연 높이 본문을 우선하고 공간 부족 시 같은 본문을 ScrollView로 표시한다. 확인 버튼은 바깥에 고정하며 기존 onDismiss 제출을 유지한다. +- 실제 native geometry(iPhone17Pro iOS26, 402×874pt): 한국어 ON `(0,502,402,372)`, OFF `(0,587,402,287)`, 영어 ON `(0,490.33,402,383.67)`. 이는 측정 결과이며 고정 높이 상수가 아니다. +- 실제 시스템 최대 글자/가로 화면 874×402pt: 패널은 가용 높이402pt로 제한되고 본문 스크롤/안내문 접근/확인 버튼 탭이 통과했다. 최초 scratch 환경값 주입 실패는 실제 시스템 `content_size`로 재검증했고 설정을 원래 large로 복원했다. +- native tests: `IntrinsicPanelPass.xcresult` 3개/0실패, `IntrinsicOverflow.xcresult` 1개/0실패. background marker 픽셀 RGBA `[24,84,102,255]`로 뒤 화면 노출, 뒤 버튼 `isHittable=false`/탭 횟수0, 배경·드래그 닫힘 차단 및 확인/닫힘 각각1회를 검증했다. scratch는 실제 panel/modifier 소스와 VM/toast stubs를 사용했다. +- 증거 디렉터리: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-settings-qa`. 로그 `intrinsic-panel-pass.log`, `intrinsic-overflow.log`. PNG는 `intrinsic-panel-attachments` 및 `intrinsic-overflow-attachments` 아래에 있다. +- 17:28 coordinator가 기존 P3-GATE와 같은 workspace/simulator/derivedData 옵션으로 `SodaLive-dev`, `SodaLive`를 빌드해 둘 다 exit0을 확인했다. Swift parse, serializer4조합, 실제 VM 격리 driver, 기존 `work/tests/check-*.sh` 전체, git diff --check도 PASS다. LSP 도구 미제공은 그대로이며 실제 compiler/build 증거를 사용했다. +- 기능/시각 독립 검토 모두 **APPROVE(P4 한정)**. 리뷰 `reviews/phase4-intrinsic-bottom-panel.md`. source hash `9b88f2e4999f9dfd28f32576385ced5b6078002671f6092f199f243ea43b1a27`와 캡처 대상이 일치한다. +- 정리: 임시 QA app/runner 제거, 작업 소유 simulator 종료. booted 기기 및 `xcodebuild|AuthSettingsQA` 프로세스 없음 확인. 커밋/푸시 없음. +- 남은 한계: 이전 `P3-QA` 실제 인증/실서버 통합은 [blocked] 유지. iOS16.6 기기 실행은 하지 않았고 이번 실제 UI 실행 환경은 iOS26이다. + +### `P5-T1`, `P5-R1` 개발 성공 시뮬레이션 — 2026-10-12 + +- 상태: **P5 승인 범위 완료**. 공용 `AuthVerificationView`가 표준 앱에서는 실제 Bootpay, dev에서는 0.5초 LoadingView→synthetic 완료 이벤트를 사용한다. 7개 caller의 성공/취소/오류/닫기 callback을 유지하며 기존 패널과 `MyPageViewModel` 성공 분기는 변경하지 않았다. +- 모의 인증 POST는 네트워크 호출0회. 정상 self-member GET의 세 필드만 메모리 상태로 보정한다. 같은 로그인 세대 객체를 인증 전부터 유지하므로 늦은 GET도 최신 확정값을 사용한다. reset/token setter는 세대를 폐기하고, 다른 계정/같은 token 재로그인 이전 응답의 mock 보정을 차단한다. 로그인 토큰이 없으면 실제 POST 우회 없이 로컬 실패한다. +- 실제 일반 콘텐츠 설정 PATCH는 유지한다. 성공한 최신 수신 결과를 활성 mock preference에 반영하며 실패 응답은 원본/기존 상태를 보존한다. REV-P5-002는 실제 사용자 경로 미확정으로 별도 보류이며 요청 순서 counter는 추가하지 않았다. +- RED: helper 소스 부재 compile 실패와 dev actual repository의 no-token API 도달 assertion 실패. R1 RED는 late GET 결과 false/기존 선택 assertion 실패(exit132). GREEN: committed `bash work/tests/check-auth-verification-mock.sh` 및 실제 repository/VM 임시 driver가 모두 exit0, 지연 GET 뒤 `auth=true, preference=FEMALE` 유지. +- coordinator가 `bash .../auth-verification-repository-check/run.sh dev`, `standard-debug`, `standard-release`를 직접 실행해 PASS를 확인했다. `...`는 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode`이다. native Combine, 실제 변경 소스, 격리 transport/앱 의존성을 사용하며 실서버 실행은 아니다. +- native QA: `auth-mock-qa/AuthMockLifecycle.xcresult` 및 `native-mock.log`에서 4 tests/0 failures. cover/inline 초기 완료0회, 약0.543/0.607초 뒤 완료1회, 기존 ON/ALL 패널, 확인/닫힘 각각1회. 0.1초 로딩 취소 시 완료0회, navigation 재등장 시 완료 총1회. 실제 wrapper/패널·폰트를 컴파일하고 Bootpay는 Payload 타입만 제공하는 임시 모듈, host VM/toast는 대체했다. +- 일반 앱 제외: 실제 유효 flag는 dev Debug=`DEBUG AUTH_VERIFICATION_MOCK`, dev Release=`AUTH_VERIFICATION_MOCK`, standard Debug=`DEBUG`, standard Release=없음. 실제 standard Debug executable/debug dylib에 mock 정의 심볼 없음, 실제 dev debug dylib에 존재, standard wrapper의 Bootpay 인증 SDK 참조 유지. Release 전체 앱 빌드는 하지 않았으며 설정 및 helper/저장소 분기 compile 검사만 수행했다. +- 두 Debug workspace 빌드 최초 exit0 후, R1 수정 뒤 coordinator가 18:51에 동일 cached derivedData/`ARCHS=x86_64`/`CODE_SIGNING_ALLOWED=NO`/`generic/platform=iOS Simulator` 옵션으로 두 앱을 다시 빌드해 모두 exit0을 확인했다. 기존 전체 `work/tests/check-*.sh`, Swift parse, plutil, diff whitespace 검사도 PASS다. +- 독립 Oracle 재검토: REV-P5-001 수정 **APPROVE**, 추가 차단 결함 없음. 재검토 시 요청한 수정 후 두 빌드 exit0도 충족했다. 리뷰 `reviews/phase5-dev-auth-simulation.md`. +- 리소스: auth-mock QA 앱/전용 runner를 삭제하고 소유 simulator를 종료했다. loading-inline PNG는 `auth-mock-qa/loading-inline.png`에 보존했다. 실제 사용자 계정/서버를 변경하지 않았으며 커밋/푸시/worktree 생성 없음. +- 사용: 로그인된 `SodaLive-dev`에서 기존 본인인증 버튼을 누른다. 회원정보 GET은 실제 서버 연결이 필요하고 서버 오류는 기존 실패 흐름을 따른다. 실제 보호 API 권한은 생기지 않으며, 일반 앱 실제 인증과 이전 P3 실서버 통합 미검증은 별도다. + +### `P6-T1` 배경 고정·패널 모션 분리 — 2026-10-13 + +- 상태: **P6 완료**. 생산 변경은 `AuthContentSettingsSheet.swift` 한 파일이다. host 표시 상태를 VM의 표시 의도와 분리하고 host transaction에만 `disablesAnimations=true`를 적용한다. dim은 제자리에 표시하고, 패널의 자체 높이만큼 `GeometryEffect`로 0.25초 이동한다. +- 퇴장 진행값이 실제 렌더 경로에서 0에 도달하면 다음 main turn에서 host를 닫는다. 기존 host `onDismiss`에서만 제출하며, 고정 시간 뒤 제출/닫힘 처리는 없다. Reduce Motion은 수직 이동 없이 즉시 처리한다. +- native baseline `baseline-native.mp4`의 배경 이동 경계를 확인했다. 최종 `panel-final.mp4`의 진입 마커 위치 y246..485px/RGB(23,83,101)이 고정된 채 패널 top은 화면 밖→2584→2130→1508px로 이동한다. 퇴장 마커는 같은 위치/RGB(39,87,102) 고정, 패널만 하단 밖으로 이동한다. 단계별 색 차이는 기존 underlying loading overlay이다. +- 퇴장 완전 이탈은 `panel-final-132.png`, host 제거/onDismiss1은 `133.png`에서 확인한다. `131.png`에는 패널의 얇은 끝부분이 남아 있어 완전 이탈 증거로 사용하지 않는다. 확인→onDismiss1 측정값은 약294ms이며 임시 host의 UIKit presented controller 없음 assertion이 통과했다. +- native 최종 검사: 기본6/0실패, 빠른 확인5회/0실패, 시스템 큰 글자 가로 overflow1/0실패, background/foreground1/0실패, 실제 mock loader→panel4/0실패. 오류 재열기 FEMALE 유지와3회 cycle당 callback1회, ON/OFF372/287pt 유지 확인. +- 구현 중 didSet-only observer의 빠른 확인 hang이 재현되어 실제 GeometryEffect 렌더 진행값 기반으로 보완했다. 최종 증거는 보완 후 source hash `1eee37ffc5c8a937b7be15c0f388782be8965447738aa8fa883a84b729719403`와 일치한다. +- 증거: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-settings-qa/MOTION-EVIDENCE.md`, `motion-verification.json`, `motion-production.diff`, `MotionPanelFinal.xcresult`, `MotionQuickRepeated.xcresult`, `MotionOverflowFinal.xcresult`, `MotionHostLifecycle.xcresult`; sibling auth-mock-qa의 `MotionMockLifecycle.xcresult`. +- coordinator가 `python3 verify_motion.py`를 재실행해 pixel/freshness assertions PASS, 변경 소스 parse/hash 확인, 기존 `work/tests/check-*.sh` 전체 PASS를 확인했다. 두 workspace Debug simulator build도 현재 소스로 exit0이었다. 기존 P3-GATE와 같은 derivedData/arch/signing 옵션을 사용했고 로그는 tmp/opencode의 `auth-panel-motion-dev-build.log`, `auth-panel-motion-standard-build.log`이다. +- 독립 기능/시각 검토 모두 **APPROVE(P6 한정)**. 리뷰 `reviews/phase6-fixed-backdrop-panel-motion.md`. 전체 앱 빌드에도 변경 파일 오류/경고가 없었다. 기존 unrelated 빌드 경고는 유지한다. +- QA 앱2개 및 runner2개 제거, 작업 소유 simulator 종료, recorder 종료를 확인했다. booted 기기 및 QA/build/recordVideo 프로세스 없음. 시스템 글자크기 large/ReduceMotion0 복원 증거를 보존했다. +- 한계: 실제 실행 OS는 iOS26이며 minimum16.6 compile만 확인했다. 실제 toast/7개 실계정 인증/실서버 통합은 이전 미검증 범위다. VM·API·개발 mock·caller는 이번 단계에서 수정하지 않았다. 커밋/푸시/worktree 생성 없음. diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md new file mode 100644 index 00000000..441db8be --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md @@ -0,0 +1,252 @@ +# 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`에 기록한다. diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase2-auth-content-settings.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase2-auth-content-settings.md new file mode 100644 index 00000000..612993d4 --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase2-auth-content-settings.md @@ -0,0 +1,86 @@ +# 본인인증 후 콘텐츠 보기 설정 리뷰 + +## 1. 리뷰 정보 + +| 항목 | 내용 | +|---|---| +| 대상 | Phase 1~3 / 현재 main working tree의 인증 설정 변경 | +| 일자 | 2026-10-12 | +| 기준 | `../prd.md`, `../plan-task.md` | +| 검토 | Oracle 기능/회귀 검토, 독립 시각 검토, coordinator 실행 검증 | +| 상태 | 코드 결함 미발견 / 실제 인증 통합 QA 증거 대기 | + +## 2. 목적과 범위 + +- 범위: `MyPageViewModel`, 인증 DTO/repository, 공용 `AuthContentSettingsSheet`, 7개 인증 caller, I18n, Xcode 등록, serializer check. +- 기준: 확인 전 요청·저장 금지, ON/ALL, 확인 후 닫힘→POST, POST 실패 시 동일 draft 재개, callback 경로 GET/로컬 동기화 후 action, GET 실패 재시작, 기존 마이페이지 재시작 유지. +- 제외: 기존 콘텐츠 설정 autosave 변경, SDK/인증 서버 변경, unrelated 경고 정리, 사용자 계정의 임의 인증. + +## 3. 판정 기준 + +확정된 사용자 요구사항 위반은 수정 대상으로 판정한다. 실제 서버/화면 통합 증거가 없는 항목은 코드 결함과 분리해 보류하고 최종 Gate를 통과로 표시하지 않는다. + +## 4. 검토한 근거 + +### 소스 + +- `SodaLive/Sources/MyPage/MyPageViewModel.swift`: receipt capture, immutable request, onDismiss submit, POST/GET 실패 분리, callback 소비. +- `SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift`: binding-only 입력, native sheet dismissal 금지, onDismiss 전송, 오류/로딩 연결. +- `MyPageView`, `HomeView`, `HomeTabView`, `ChatTabView`, `LiveNowAllView`, `V2/Main/MainView`, `CreatorChannelView`: provider 닫기와 설정 시트 연결, pending action 보존. +- `ContentView:22`, `SplashView:73`, `MainView:59`: 재시작 시 MainView 제거·재생성과 기존 member GET 연결. 런타임 성공 증거는 아님. + +### 실행 환경과 증거 + +- macOS Intel, Xcode 26.0, iOS 26.0 iPhone 17 Pro Simulator. deployment target 16.6. +- 증거 루트: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode`. + +| 실행 | 결과 | 근거/한계 | +|---|---|---| +| 실제 DTO swiftc compile/run | PASS / exit 0 | 4개 조합 exact keys/types/values. RED는 새 initializer 인자 부재 compile 실패 | +| 실제 VM/repository + native Combine 임시 driver | PASS / exit 0 | malformed6, POST failure6, GET failure7, callback 순서·중복 방지. transport/AppState/UserDefaults는 임시 stubs | +| 네이티브 실제 sheet/modifier 임시 앱 XCTest | PASS / 2 tests, 0 failures | toggle/radio/swipe 차단/confirm dismiss. VM·toast stub으로 실서버/오류 토스트 미검증 | +| 한·영 ON/OFF native screenshots | 시각 검토 APPROVE | 잘림·겹침 없음. Android 대비 iOS26 floating 외곽 차이, pixel-perfect 주장 없음 | +| 기존 `work/tests/check-*.sh` 전체 | PASS / exit 0 | 변경 전 및 변경 후 회귀 확인 | +| 두 workspace Debug Simulator builds | PASS / exit 0 | SodaLive-dev, SodaLive, coordinator 재실행 포함. 기존 SDK/중복 소스/dSYM 경고 존재 | +| Swift parse / plutil / git diff --check | PASS / exit 0 | LSP 도구 미제공, LSP 실행 성공으로 대체 표기하지 않음 | + +UI 증거: `auth-settings-qa/{EVIDENCE.md,interactions.log,NativeInteractions.xcresult,ko-on-all.png,ko-off-all.png,en-on-all.png,en-off-all.png}`. 실제 sheet hash는 `ff44380bedf20a364d59b5ad5b07164f97dfaa87cb78bbb09a1e1dc3f80eddff`이며 캡처 이후 소스 변경이 없다. + +## 5. 발견 사항 요약 + +**확정 코드 결함 없음.** 아래는 최종 수용 판단을 위한 증거 부족 항목이다. + +| ID | 상태 | 내용 | 소유 Goal | +|---|---|---|---| +| QA-001 | 보류 | 실제 7개 인증 caller의 provider-dismiss-sheet, POST 실패 오류/재개 통합 증거 없음 | P3-QA | +| QA-002 | 보류 | GET 실패 후 실제 앱 재시작으로 member GET과 로컬 저장이 이루어지는 증거 없음 | P3-QA | + +## 6. 발견 사항 상세 + +### QA-001: 실제 인증 진입점 통합 + +- 관련 기준: AUTH-002/003/006/007. 실제 마이페이지의 inline provider와 다른 6개 cover를 종료한 뒤 시트가 정상 표시되는지 검증해야 한다. +- 현재 증거: 7개 caller 소스/컴파일, shared VM 실행, native modifier 단독 상호작용. 실제 전체 화면 계층·Bootpay와의 연동 성공을 대신하지 않는다. +- 재개: 승인된 QA 계정/유효한 receipt로 모든 진입점 정상·전송 실패 경로 실행. raw receipt/token 기록 금지. + +### QA-002: 재시작 후 실제 동기화 + +- 관련 기준: PRD §14.1의 restart fallback 실제 member refresh 및 로컬 반영. +- 현재 증거: VM driver에서 GET 실패→재시작 1회/추가 POST 0회/callback 0회; 소스에서 새 MainView의 member GET 경로 확인. +- 미검증: 실제 화면 재생성과 GET 응답/로컬 상태 반영. 임시 AppState 카운터만으로 통과 처리하지 않는다. +- 재개: POST 성공 후 GET만 실패시키고, 재시작 뒤 GET 성공을 허용해 auth/content 설정 갱신과 pending action 미실행을 관찰한다. + +## 7. plan·goal 전환 + +코드 수정 확정 항목이 없어 신규 회귀 수정 Task는 없다. QA-001/002는 기존 `P3-QA`에 [blocked]로 유지한다. 실패가 실제 재현되면 코드 수정 전에 신규 회귀 Task를 추가한다. + +## 8. 리뷰 종료 판정 + +- 소스/격리 실행/네이티브 시각 검토: 완료, 확정 production defect 없음. +- 전체 사용자 흐름 최종 Gate: **외부 QA 조건 대기**. Oracle은 코드 결함이 아니라 실제 통합 증거 부족으로 최종 승인을 보류했다. +- 실서버 지원은 사용자 확인에 근거한다. receipt 만료/재시도 idempotency 및 작은 화면·큰 글자 실사용은 별도 미검증이다. + +## 9. 후속 검증 기록 + +- 2026-10-12: coordinator가 현재 소스의 양쪽 앱 incremental build와 기존 회귀 스크립트 전체를 재실행해 exit 0을 확인했다. 배경 탭 차단 추가 네이티브 검사는 별도 후속 기록으로 남긴다. +- 2026-10-12 후속: 실제 dimmed 영역 `(0.5,0.2)` 탭 뒤 시트 유지, 그 후 설정 완료로만 닫힘을 focused XCTest 1개/실패 0개로 확인했다. `auth-settings-qa/NativeBackdrop.xcresult`, `backdrop.log` 및 `backdrop-attachments`에 증거가 있다. 임시 앱·runner 제거와 simulator 종료를 확인했다. 추가 검사는 단독 native host 증거로 QA-001/002의 실제 인증 통합 차단을 해소하지 않는다. diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase4-intrinsic-bottom-panel.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase4-intrinsic-bottom-panel.md new file mode 100644 index 00000000..cf0e7789 --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase4-intrinsic-bottom-panel.md @@ -0,0 +1,59 @@ +# 콘텐츠 높이 커스텀 BottomSheet 리뷰 + +## 1. 리뷰 정보 + +| 항목 | 내용 | +|---|---| +| 대상 | Phase4 / P4-T1 / 현재 main working tree의 표시 방식 변경 | +| 일자 | 2026-10-12 | +| 기준 | `../prd.md` §10.0, `../plan-task.md` P4-T1 | +| 리뷰어 | 기능 독립 검토 + 시각 독립 검토 + coordinator 증거 확인 | +| 상태 | P4 APPROVE / 기존 P3 실서버 통합 미검증은 유지 | + +## 2. 목적과 범위 + +- `AuthContentSettingsSheet.swift`의 native sheet를 투명 fullScreenCover + 콘텐츠 자연 높이의 전폭 하단 패널로 바꾼 변경만 검토한다. +- 배경 노출/입력 차단, 자연 높이, safe area, overflow 스크롤, 확인 후 dismiss 콜백을 확인한다. +- 기존 인증 API/회원 동기화/실서버 검증 공백은 이번 UI-only 판정과 분리한다. + +## 3. 판정 기준 + +일반 화면에서 높이는 내용으로 결정되고 외곽 floating 여백이 없어야 한다. 실제 공간 초과 때만 본문을 스크롤하며 확인 버튼 접근과 닫힘 완료 후 제출 연결을 유지해야 한다. + +## 4. 근거와 실행 검증 + +- 실제 소스 hash: `9b88f2e4999f9dfd28f32576385ced5b6078002671f6092f199f243ea43b1a27`. +- native 증거 root: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-settings-qa`. +- 실제 production View/modifier/font/I18n/token을 직접 컴파일한 임시 앱이다. VM과 toast는 stub으로 실제 서버나 오류 toast 검증은 아니다. + +| 검사 | 결과 | 근거 | +|---|---|---| +| ko ON / OFF geometry | PASS | 화면402×874pt, 패널 `(0,502,402,372)` / `(0,587,402,287)` | +| en ON geometry | PASS | 높이383.67pt로 실제 줄바꿈에 따라 증가 | +| 배경 투명/입력 차단 | PASS | marker RGBA `[24,84,102,255]`, 뒤 버튼 unhittable, backgroundTaps0 | +| toggle/radio/배경·드래그 차단/확인 | PASS | `IntrinsicPanelPass.xcresult` 3 tests, 0 failures | +| 가로·시스템 최대 글자 overflow | PASS | `IntrinsicOverflow.xcresult` 1 test, 0 failures. 본문 스크롤·확인 접근 | +| Swift parse / 두 앱 Debug Simulator build | PASS | coordinator 실제 실행 exit0, 기존 SDK/중복 소스/dSYM 경고 유지 | +| serializer / VM 격리 driver / 기존 regression | PASS | 실제 DTO4조합, 기존 상태 전이, work/tests 전체 | + +캡처 대조: +- `intrinsic-panel-attachments/5BBF64BD-30D4-4FE3-A0F7-D1667609709A.png`: ko ON. +- `intrinsic-panel-attachments/CDE33DE3-749C-4C73-9594-5C7D55BF0F8A.png`: ko OFF. +- `intrinsic-panel-attachments/F0FACA4C-7025-488D-BC25-38D44F79E8DD.png`: en ON. +- `intrinsic-overflow-attachments/41F47EE5-69D0-40DD-86E9-5033A1F5E49F.png`, `0C90E2E0-8109-476C-AC95-C680FFE681E4.png`: 가로 최대 글자 스크롤 전/후. + +## 5. 발견 사항 + +**확정 발견 사항 없음(P4 범위).** 기능/시각 검토 모두 APPROVE다. 코드에서 고정 높이·`.large` 분기가 제거됐으며, 두 layout 후보는 동일 binding의 본문을 공유하고 부수효과가 없다. 확인 버튼은 두 후보 밖에 유지된다. + +## 6. plan·goal 전환 + +새로운 회귀 수정 Task 없음. P4-T1은 완료하고 기존 P3-QA의 실제 인증 통합 미검증은 별도로 보존한다. + +## 7. 종료 판정과 한계 + +**P4 표시 방식 변경 승인.** 실제 네이티브 캡처·geometry assertions와 현재 소스가 일치한다. pixel-perfect 차이율은 측정하지 않았다. iOS16.6 기기 실행 및 실제 계정/서버와 연결된 7개 caller의 전체 흐름은 검증하지 않았다. LSP 도구는 미제공이며 compiler/build로 검증했다. + +## 8. 정리 기록 + +QA app/runner 제거와 작업 소유 simulator 종료를 실행했다. `simctl list devices booted`에 실행 기기 없음, `pgrep -fl 'xcodebuild|AuthSettingsQA'`에 남은 프로세스 없음. 증거는 tmp 경로에 보존했다. diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase5-dev-auth-simulation.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase5-dev-auth-simulation.md new file mode 100644 index 00000000..d6d3404f --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase5-dev-auth-simulation.md @@ -0,0 +1,60 @@ +# 개발 전용 인증 시뮬레이션 리뷰 + +## 1. 리뷰 정보 + +- 대상: P5-T1 개발 타깃 조건, 공통 인증 View, 모의 repository/세션 상태. +- 기준: `../prd.md` §11.0, `../plan-task.md` Phase5. +- 일자: 2026-10-12. 상태: P5 수정·검증 완료 / 독립 재검토 APPROVE. + +## 2. 목적과 범위 + +SodaLive-dev만 SDK/인증 POST를 모의 처리하고 기존 성공 흐름을 재사용하는지, 일반 앱에 모의 코드가 제외되는지, 조회·계정 변경·후속 설정이 일관되는지 확인한다. 실제 서버 권한 부여는 범위 밖이다. + +## 3. 검토 근거 + +- 실제 `AuthRepository`, `UserRepository`, `AuthVerificationMock`와 native Combine을 컴파일하고 transport/UserDefaults 의존성만 격리했다. +- 재현 파일: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-verification-repository-check/CheckMockRaces.swift`. +- 기존 Foundation mock 체크와 저장소/VM 정상 흐름은 통과했으나 아래 두 순서 사례는 빠져 있었다. + +## 4. 발견 사항 요약 + +| ID | 심각도 | 상태 | 내용 | Task | +|---|---|---|---|---| +| REV-P5-001 | High | 수정 완료 | 인증 전 GET의 늦은 응답이 활성 모의 상태를 되돌림 | P5-R1 | +| REV-P5-002 | Medium | 보류 / 이번 수정 제외 | repository 단독 역순 응답은 재현되지만 실제 UI 로딩 차단을 포함한 중첩 사용자 요청 미확정 | 후속 재현 시 재평가 | + +## 5. 발견 사항 상세 + +### REV-P5-001 + +`selfMemberPublisher`가 요청 시점의 활성 mock Session을 캡처한다. 인증 전에는 nil이므로, 같은 로그인 세션에서 인증 완료 후 응답이 와도 overlay하지 않는다. App/Home VM은 정상 응답의 isAuth/선호를 그대로 저장하므로 최신 모의 상태가 되돌아갈 수 있다. + +재현: GET A 시작 → 모의 인증 성공 → GET B 완료로 true/FEMALE 반영 → GET A 완료. 실행 결과 `auth=false, preference=ALL`, 기대 `true/FEMALE`. + +### REV-P5-002 + +`updateContentPreference`는 응답마다 공유 snapshot을 갱신하며 요청 순서를 구분하지 않는다. 기존 ContentSettingsViewModel의 latestRequestToken은 그 뒤 UI 갱신만 차단하므로 공유 snapshot이 이미 이전 값으로 변한다. + +재현: MALE 요청 A → FEMALE 요청 B → B 성공 → A 성공 → member GET. 실행 결과 `MALE`, 기대 최신 요청의 `FEMALE`. + +## 6. plan·goal 전환 + +코드 변경 전에 `P5-R1`을 추가했다. 로그인 세션과 mock 활성 상태를 분리하고 preference 요청 순서를 추적하는 최소 수정 후 동일 재현과 real/mock 회귀 검사를 다시 실행한다. + +## 7. 검증 및 판정 + +- 위 두 재현은 coordinator가 실제 변경 소스를 컴파일해 확인했다. 실서버를 호출하지 않았다. +- 독립 리뷰와 두 앱 전체 빌드는 진행 중이며 아직 최종 완료로 판단하지 않는다. +- 2026-10-12 독립 리뷰 후속: Oracle은 REV-P5-001을 실제 Main 회원조회/로컬 저장 경로의 확정 회귀로 판정했다. REV-P5-002는 기존 설정 UI의 LoadingView 차단 때문에 실제 중첩 사용자 요청을 확정하지 못했으므로, repository 단독 재현만으로 제품 결함을 확정한 이전 판정을 보류로 정정한다. 최소 변경 원칙에 따라 추가 preference 요청순서 상태는 구현하지 않는다. +- 두 Debug 앱 빌드 exit0, 네 가지 구성에서 mock flag는 dev Debug/Release에만 존재했다. 실제 standard 실행파일/debug dylib에 mock 심볼 없음, dev dylib에는 존재함을 확인했다. 아래 회귀 수정 후 필요한 검증을 재실행한다. + +## 8. 수정 후 검증 및 종료 판정 + +- REV-P5-001: stable login Session과 optional 확정 preference를 분리했다. 인증 전 요청이 캡처한 동일 세대는 응답 시점의 최신 확정값을 사용한다. 명시적 nil·이전 계정·같은 token 재로그인 이전 세대는 보정하지 않는다. +- `AuthVerificationMockCheck.swift:34`에 late pre-confirmation GET 검사를 추가하고 assertion RED(exit132)→GREEN(exit0)을 확인했다. 기존 assertion 삭제/완화 없음. 임시 실제 repository/변경 없는 VM driver도 `A 시작→모의 인증→B 성공/로컬 저장→A 성공` 후 true/FEMALE 유지, 실제 인증 POST0회를 확인했다. +- Oracle 재검토는 **APPROVE**, 새로운 차단 결함 없음. 재검토가 남긴 수정 후 두 앱 빌드 조건도 coordinator 18:51 재실행 exit0으로 충족했다. +- 전체 `work/tests/check-*.sh`, dev/standard repository driver, Swift parsing, project plutil, diff check PASS. native UI는 수정하지 않아 기존 4test/0failure 증거를 유지한다. +- 실제 wrapper의 cover/inline 로딩→패널→확인/닫힘, 취소·재등장 guard는 iOS26 native host에서 검증했다. 인증 success와 member sync는 실제 VM/repository에 transport stubs를 연결한 격리 실행으로 검증했으며 실서버 인증으로 표현하지 않는다. +- 일반 `SodaLive` Debug/Release effective mockflag 없음. dev Debug/Release만 flag 있음. 실제 Debug Mach-O 표준 mocksymbol0, devmock 존재; Release는 전체 앱 빌드 미실행이며 설정/검사 바이너리만 검증했다. +- REV-P5-002는 실제 UI 경로 미확정이므로 보류 판정을 유지하며 추가 순서 추적을 만들지 않았다. 실제 사용자 경로로 재현되면 별도 회귀 Task로 진행한다. +- P5 앱 흐름 테스트 기능은 승인 가능하다. 기존 P3 실제 계정/서버 통합 공백은 별도이며, 개발 mock은 서버 보호 API 권한을 부여하지 않는다. LSP 도구는 미제공으로 실제 compiler/build 검증을 사용했다. diff --git a/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase6-fixed-backdrop-panel-motion.md b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase6-fixed-backdrop-panel-motion.md new file mode 100644 index 00000000..6a3f16f5 --- /dev/null +++ b/docs/20261012_본인인증_후_콘텐츠_보기_설정/reviews/phase6-fixed-backdrop-panel-motion.md @@ -0,0 +1,52 @@ +# 고정 배경·패널 모션 분리 리뷰 + +## 1. 리뷰 정보 + +- 대상: P6-T1, 현재 working tree 중 `AuthContentSettingsSheet.swift` 모션 변경. +- 일자: 2026-10-13. 기준: PRD §10.0 Phase6, plan-task P6-T1. +- 검토: 독립 기능 검토와 시각/모션 검토, coordinator 실제 소스/프레임/검사 확인. +- 상태: **APPROVE(P6 한정)**. + +## 2. 목적과 범위 + +fullScreenCover 기본 전환을 끄고 배경은 고정, 패널만 이동하도록 한 변경을 검토한다. 기존 intrinsic 높이, 안전 영역, 확인 전송 순서와 오류 재표시를 유지한다. VM/API/mock/실서버 권한 변경은 범위 밖이다. + +## 3. 판정 기준 + +배경 마커 위치와 색은 각 진입/퇴장 모션 동안 고정되어야 한다. 패널은 독립적으로 이동하며 화면 밖으로 퇴장한 뒤 host가 닫히고 기존 onDismiss가 한 번 호출돼야 한다. 빠른 확인·재표시·Reduce Motion에서도 누락/중복 호출이나 host 잔류가 없어야 한다. + +## 4. 검토 근거 + +- 소스: `AuthContentSettingsModifier`의 local host state/transaction, `AuthContentSettingsPanelMotion`의 rendered progress 완료와 자체 높이 변환, 기존 VM confirm/submit guards. +- 최종 hash: `1eee37ffc5c8a937b7be15c0f388782be8965447738aa8fa883a84b729719403`. +- 정확한 P6 delta와 증거: tmp/opencode/auth-settings-qa의 `motion-production.diff`, `MOTION-EVIDENCE.md`, `motion-verification.json`, `panel-final.mp4`, `panel-interrupt.mp4`, `panel-reduce.mp4`. +- native host는 실제 View/modifier·폰트·토큰 소스를 컴파일하고 VM/toast는 격리 대체한다. 실서버 인증 증거로 해석하지 않는다. + +## 5. 검증 결과 + +| 검사 | 결과 | 근거 | +|---|---|---| +| 배경 고정/패널 진입 | PASS | `panel-final-083/087/091.png`, 고정 마커 RGB(23,83,101), 패널 top 변화 | +| 퇴장 완료 후 onDismiss | PASS | `124/128/132/133.png`, 완전 퇴장132 후 host 제거133, callback1회 | +| 빠른 확인/진입 반전 | PASS | normal6개 및 quick 반복5개/0실패, interruption 영상 | +| 오류 재개/선택 유지/3cycles | PASS | native 기본 회귀와 FEMALE 유지, cycle당 callback1회 | +| Reduce Motion | PASS | visible frame의 panel top1508px 일정, 수직 이동 없음 | +| 큰 글자 overflow/foreground 복귀 | PASS | 각각1개/0실패, 확인 접근·선택 유지 | +| 실제 dev loading→panel | PASS | 별도 mock lifecycle4개/0실패, wrapper/패널 실제 소스 | +| 두 앱 Debug builds/회귀 | PASS | coordinator 현재 소스 빌드 exit0, 기존 work/tests 전체·Swift parse·diff 검사 PASS | + +초기 didSet-only observer는 빠른 확인에서 hang을 재현해, 최종 수정에서는 actual GeometryEffect 렌더 진행값에서 완료를 확인한다. 최종 증거는 이 보완 후 생성됐다. + +## 6. 발견 사항 및 plan 전환 + +**확정 코드 결함 없음.** 기능·시각 독립 검토 모두 승인했다. 추가 회귀 수정 Task는 없다. + +증거 표현 정정: `panel-final-131.png`에는 하단 얇은 패널 조각이 남아 있다. 완전 이탈은 `132.png`를 근거로 하며, `133.png`에서 배경과 host가 제거된다. 자동 픽셀 분석의 null만으로131을 완전 이탈로 판단하지 않는다. + +## 7. 종료 판정과 한계 + +P6 승인 기준 충족. 기존 레이아웃·문구·API·mock 코드는 유지됐고, 제출 호출은 여전히 host onDismiss 한 곳이다. iOS26 native 실행과 minimum16.6 compile은 확인했으나 iOS16 실기기, 실제 toast, 실계정/7caller/서버 통합은 미검증이다. LSP 도구 미제공으로 compiler/build 증거를 사용했다. + +## 8. 정리 기록 + +작업 전용 QA 앱/runner 제거, 소유 simulator 종료 및 recorder 종료를 확인했다. 환경의 접근성 설정은 원복했고 프레임·영상·로그는 tmp 경로에 보존했다. 커밋/푸시 없음.