Files
sodalive-ios/docs/20261012_본인인증_후_콘텐츠_보기_설정/plan-task.md
T

63 KiB
Raw Blame History

본인인증 후 콘텐츠 보기 설정 계획

기준 문서

  • PRD: docs/20261012_본인인증_후_콘텐츠_보기_설정/prd.md
  • API 기준: PRD §11, 별도 api-contract.md 없음
  • 상태: Phase 7·R1 구현·로컬 기능/시각 검증 완료 / 실제 인증 통합 QA는 기존 대기 유지
  • 현재 브랜치: main, worktree 사용 안 함
  • 현재 Phase: Phase 7·R1 로컬 검증 완료. 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 사용자 “승인”으로 통과했다.

  • 사용자에게 PRD와 plan-task에 대한 명시적 문서 승인을 받는다. 이 승인을 구현 시작 허가로 처리한다.
  • 승인 시점과 범위를 기록하고 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.

  • 지정 source/작성 규칙을 확인하고 요구사항·§11 계약·승인 gate를 PRD에 기록한다. 별도 api-contract.md는 만들지 않는다.

  • 문서 외 수정이 없음을 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.
  • RED: check를 작성해 실제 모델의 JSON key/type과 ON/ALL, ON/MALE, ON/FEMALE, OFF/ALL 값을 assert한다. 실제 receipt/token을 fixture로 사용하지 않는다.

  • 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 실패를 기록한다. 임시 부모 경로 존재 여부를 먼저 확인한다.

  • GREEN: 실제 AuthVerifyRequest에 설정 필드를 추가하고 기존 receipt_id CodingKey를 보존한다.

  • GREEN 확인: 같은 compile/run 명령이 exit 0으로 완료되고 실제 encoder JSON key/type/enum을 assert함을 확인한다.

  • REFACTOR: 해당 없음이면 건너뛰고 불필요한 추상화를 추가하지 않는다. 실행 결과를 Progress에 기록한다.

Phase 1 완료 조건 및 Gate

  • 두 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<GetMemberInfoResponse>.

  • 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.

  • 변경 전 확인: 현재 일곱 caller와 authVerify에서 확인 UI 없이 POST하고 즉시 callback/재시작으로 진행하는 소스 경로를 기록한다. 자동 serializer의 RED는 완료한 Task 1.2 증거를 참조하며 이미 GREEN인 검사를 다시 실패시키지 않는다.

  • GREEN: presentation-only sheet, 7개 caller의 cover-dismiss-sheet 전환, 확인 한 번의 POST, 기존 callback/restart action을 구현한다. standalone ContentSettingsViewModel은 수정하지 않는다.

  • GREEN 동기화: callback 경로만 POST 성공 후 기존 repository의 member GET을 구독하고, 성공 decode와 여덟 로컬 키 저장 이후 callback을 실행한다. GET의 transport/API/decode 실패는 callback과 인증 재전송 없이 마이페이지와 같은 재시작 액션으로 처리한다. 마이페이지는 GET 없이 확인된 설정을 로컬 반영하고 재시작한다.

  • GREEN 확인: serializer assert를 통과시키고 단계 순서/source diff를 대조한다. transport/API/decode 실패는 POST 실패 복구로 연결하고, POST 성공 뒤 gender 기반 선호 덮어쓰기는 없어야 한다.

  • 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

  • 실제 모델 serializer compile/run check exit 0, git diff --check 오류 0건을 기록한다.

  • SodaLive workspace Debug 시뮬레이터 빌드 exit 0을 확인한다. 실제 destination/아키텍처 옵션은 아래 Progress에 기록한다.

  • SodaLive-dev workspace Debug 시뮬레이터 빌드 exit 0을 확인한다. 실제 destination/아키텍처 옵션은 아래 Progress에 기록한다.

  • 양쪽 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에서 기존 제출을 실행하며, 고정 시간 후 서버 전송 같은 우회는 추가하지 않는다.
  • 변경 전 모션에서 host/dim이 함께 올라오는 것을 확인하고, 배경 고정·패널 이동 프레임 및 퇴장 이후 callback 기준을 마련한다.
  • 최소 iOS16.6에서 컴파일되는 패널 전환/완료 연결을 구현하고 기존 intrinsic 높이·safe area·오류 재표시를 유지한다.
  • native 진입/중간/완료 프레임에서 dim은 정지하고 패널만 이동함을 확인한다. 배경 탭·드래그 차단, 확인/닫힘 callback1회, 빠른 확인 및 재표시를 검증한다.
  • ON/OFF 높이·overflow·동작 줄이기, 모의 로딩→패널 흐름 및 일반 인증 로직 회귀를 검사한다.
  • 두 앱 빌드·독립 기능/시각 검토와 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 성공 분기를 그대로 사용한다.
  • RED: 실제 모의 상태/응답 변환을 검사하는 최소 assert 검사를 추가하고 미구현 실패를 확인한다.
  • GREEN: dev 전용 condition, 인증 모의 성공, 계정별 메모리 상태와 정상 self-member 응답의 세 필드 보정을 구현한다.
  • GREEN UI: 7개 caller에서 공용 인증 View로 real/mock 경로를 선택하며, 로딩→패널 및 기존 닫힘 후 전송 흐름을 유지한다.
  • 세 필드 외 값·서버 실패 보존, 세션 reset, 이후 콘텐츠 설정 변경 보존, 생산 빌드에서 mock 제외를 검사한다.
  • 두 앱 Debug 빌드 및 두 타깃 Debug/Release compilation conditions, 기존 회귀·실제 VM 드라이버를 검증한다.
  • 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.
  • RED: 같은 로그인 세션에서 인증 전 시작한 GET이 인증 성공 뒤 도착해 auth=false/기존 선택으로 되돌아가는 사례를 assert로 고정한다.
  • 제외(후속 판정): preference A→B/B→A는 repository 단독 재현만 확인했으며 실제 설정 UI의 로딩 차단을 포함한 사용자 경로는 확정하지 못했다. 요청 순서의 추가 상태·처리는 이번 최소 수정에서 제외하고 판정 이력을 리뷰에 남긴다.
  • GREEN: 로그인 세션 식별과 모의 인증 활성/선택 상태를 분리한다. 같은 로그인 세션의 인증 전 요청은 응답 시점의 활성 모의 상태로 보정하고, 계정 변경·동일 token 재로그인·서버 오류 보존 규칙은 유지한다.
  • 실제 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·터치·콜백을 검증한다. 앱에 새 테스트 타깃을 추가하지 않는다.
  • .sheet, detents 고정 높이, .large 분기를 제거하고 투명 컨테이너/하단 intrinsic 패널로 바꾼다.
  • ON/OFF 및 긴 문구에서 내용 높이만 차지함을 확인한다. 화면보다 긴 내용은 본문만 스크롤하고 확인 버튼을 유지한다.
  • 실제 네이티브 캡처에서 기존 UI가 배경 너머 보이고 패널 좌우·하단에 floating 여백이 없음을 검증한다.
  • 배경/드래그 닫힘 차단과 확인 1회→닫힘 완료 콜백 1회를 native QA로 검증한다. 오류/로딩 연결은 소스 대조로 보존을 확인하며 실제 toast 런타임은 기존 미검증으로 남긴다.
  • 두 앱 스킴 빌드 및 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:2314:46:37, SodaLive 14:46:5614: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 생성 없음.

Phase 7: 인증 중 설정 패널 종료와 용어 정리

상태: 2026-10-14 구현·로컬 검증 완료. 실제 VoiceOver 제스처·실서버 검증은 미실행으로 구분하며 이전 대기/실패 기록은 이력으로 보존한다.

Phase 결과: 최초 설정 또는 저장 실패 후 X에서 종료 여부를 선택하고, 종료 시 인증 대기 상태를 폐기하며 정상 저장 흐름은 유지한다.

선행조건: PRD Phase 7 요구사항과 이 계획에 대한 명시적 구현 승인. 현재는 문서 외 변경 금지.

범위: PRD AUTH-012~AUTH-017. 일반 설정·다른 인증 안내·API·mock·프로젝트 설정·공용 팝업 변경은 제외한다.

Task 7.1: 안전한 종료와 V2 팝업 연결

Goal P7-T1: 일곱 진입점의 공용 패널에서 종료 확인을 제공하고, 종료 후 추가 제출이나 오래된 성공 action 실행을 막는다.

  • 시작 조건: Phase 7 구현 승인. 완료 증거: 실제 VM 기반 RED/GREEN 검사와 공용 UI·caller 연결. 최종 native UI QA와 두 앱 빌드는 후속 P7-QA가 담당한다.
  • Modify: SodaLive/Sources/V2/Component/Modal/AuthContentSettingsSheet.swift — X·고정 헤더·팝업 표시 상태와 full-screen host overlay, preview 연결.
  • Modify: SodaLive/Sources/MyPage/MyPageViewModel.swift — 제출 중이 아니고 패널이 열려 있을 때 기존 clearPendingAuthVerification()을 사용하는 취소 메서드. 실패 toast도 해제해 뒤 화면에 남지 않게 한다.
  • Modify: SodaLive/Sources/I18n/I18n.swift — 기존 인증 전용 ON/OFF 설명 수정, 종료 제목·본문·confirm·cancel 문자열 추가. shared 토글 제목·저장 CTA는 유지한다.
  • Modify: SodaLive/Sources/Main/Home/HomeView.swift, SodaLive/Sources/Home/HomeTabView.swift, SodaLive/Sources/Chat/ChatTabView.swift, SodaLive/Sources/Live/Now/All/LiveNowAllView.swift, SodaLive/Sources/V2/Main/MainView.swift, SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift — 종료 callback에서 각 caller의 pendingAction만 해제한다. 다른 UI·문구·라우팅은 바꾸지 않는다.
  • Read: SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift, SodaLive/Sources/MyPage/MyPageView.swift, 독립 Settings/Content/ContentSettingsView.swift. My Page는 대기 action이 없어 기존 modifier 호출을 유지한다.
  • Create/Test: work/tests/check-auth-content-settings-exit.sh, work/tests/AuthContentSettingsExitCheck.swift — 실제 VM·모델과 격리 의존성으로 검사한다. production dependency injection 리팩터링이나 XCTest target 추가는 하지 않는다.

Interfaces:

  • VM: cancelAuthContentSettings() -> Void. 기존 main-thread 관례와 제출 중/패널 표시 guard를 지키며 영수증·대기 요청·성공 closure를 정리한다. 서버 요청·UserDefaults 변경·재시작은 하지 않는다.

  • Panel: 필수 onClose: () -> Void 추가. X는 팝업만 연다.

  • Modifier: authContentSettings(viewModel:onCancel:)에 기본값 {}인 취소 closure를 추가한다. 여섯 callback caller는 { pendingAction = nil }을 전달한다.

  • Confirm: guard 재확인 → 팝업 닫기 → VM 취소 → caller 취소 1회. Continue/dim은 팝업 상태만 해제한다. 패널 닫힘/host 종료 시 팝업 상태를 초기화한다.

  • 소비 컴포넌트: 변경 없는 SodaV2ActionModal과 PRD 확정 문구. 전체 host 위에 올리고 뒤 패널 입력·접근성을 차단한다.

  • 보존 계약: 0.25초 패널 퇴장, Reduce Motion, host onDismiss의 기존 제출 호출. 종료 시 기존 guard로 제출 0회, 저장 시 닫힘 완료 후 제출 1회.

  • RED: 실제 VM 검사로 최초 종료·POST 실패 후 종료·fresh receipt 재진입·이전 성공 closure 미실행·제출 중 취소 무시를 고정한다. 종료의 추가 POST/GET·저장·성공·재시작은 모두 0회로 assert한다.

  • RED 확인: bash work/tests/check-auth-content-settings-exit.sh에서 취소 메서드 부재 또는 요구 동작 실패를 기록한다. 이미 통과하는 serializer를 일부러 실패시키지 않는다.

  • GREEN: 위 최소 VM·패널·번역·여섯 caller 연결을 구현한다. xmark 16pt medium, 44×44pt hit 영역, 중앙 제목과 동일한 좌우 영역, VoiceOver 레이블을 적용한다. 헤더·CTA는 고정하고 본문만 overflow scroll한다.

  • GREEN 확인: 같은 검사 exit 0. 실패 transport/HTTP/envelope/decode별 취소, 중복 종료, 새 ON/ALL 시작, 제출 시작 후 취소 무시와 정상 저장 1회 계약을 확인한다.

  • REFACTOR: 이번 변경의 중복만 정리하고 focused 검사와 아래 직접 영향 회귀를 재실행한다. 무관한 UI·shared modal·API 수정은 하지 않는다.

TDD 예외: SwiftUI overlay·VoiceOver·모션은 현행 앱 테스트 번들에서 자동 재현할 수 없다. 현재 실제 패널/modifier/V2 팝업 소스를 사용하는 임시 native QA host로 X·dialog·host-dismiss를 실행하고 screenshot 및 요청/callback 카운터를 기록한다. 이전 임시 QA 경로가 남아 있다고 가정하지 않는다.

Task 7.2: 기능·접근성·빌드 Gate

Goal P7-QA: 종료와 기존 저장의 차이를 실제 실행 증거로 검증한다.

  • 시작 조건: P7-T1 구현·focused 검사 완료. 완료 증거: 아래 자동·native 검증과 기록. 미실행 검증은 완료로 표시하지 않는다.

  • Confirm/Test: Task 7.1 대상 파일, 기존 work/scripts/check-auth-content-settings.swift, work/tests/check-auth-verification-mock.sh, 현재 simulator destination과 두 앱 스킴.

  • TDD 예외: 검증 전용 Task다. 실제 실행 결과와 캡처·카운터를 대체 증거로 사용한다.

  • 최초 및 POST 실패 후 X → 계속하기/바깥 탭은 draft·대기 action 보존, 종료하기는 패널 닫힘·대기 상태 폐기·추가 전송 0회. 본문의 명시적 줄바꿈을 캡처한다.

  • 일곱 caller에서 종료 후 action A가 실행되지 않고 새 인증 action B만 정상 성공 후 1회 실행된다. 실패 toast 잔류, 빠른 연속 X/종료, dialog 뒤 저장 터치, 정상 저장/실패 재표시를 검사한다.

  • 작은 iPhone·가로·최대 글자·VoiceOver·Reduce Motion에서 제목/X/CTA 접근과 dialog 입력 차단을 확인한다. 고정 backdrop과 패널 단독 퇴장을 대조한다.

  • focused·기존 auth mock·serializer 검사와 두 Debug 앱 빌드를 exit 0으로 확인한다. 변경 Swift diagnostics 도구가 없으면 실제 compiler/build 결과로 대체하고 한계를 기록한다.

  • 이 문서와 PRD에 결과를 누적한다. 구현 후 필요한 리뷰는 같은 디렉터리의 reviews/phase7-auth-settings-exit.md에 기존 리뷰 샘플을 따라 남긴다. 기존 실서버 P3-QA는 별도 [blocked] 유지.

실행 명령 (승인 후):

bash work/tests/check-auth-content-settings-exit.sh
bash work/tests/check-auth-verification-mock.sh
swiftc SodaLive/Sources/MyPage/Auth/AuthVerifyRequest.swift SodaLive/Sources/Settings/Content/ContentType.swift work/scripts/check-auth-content-settings.swift -o "$QA_DIR/check-auth-content-settings"
"$QA_DIR/check-auth-content-settings"
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -destination 'generic/platform=iOS Simulator' -derivedDataPath "$QA_DIR/SodaLive-build" CODE_SIGNING_ALLOWED=NO -quiet build
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -destination 'generic/platform=iOS Simulator' -derivedDataPath "$QA_DIR/SodaLive-dev-build" CODE_SIGNING_ALLOWED=NO -quiet build
  • $QA_DIR은 승인 후 존재를 확인한 임시 작업 폴더다. native QA destination은 당시 설치된 simulator에서 확인하고 과거 UUID·아키텍처를 고정하지 않는다. 두 앱 빌드는 순차 실행한다.
  • 전체 unrelated 회귀는 이번 범위에서 제외한다. 실제 VM·공용 패널·여섯 caller·auth mock·serializer의 직접 영향 회귀를 검사하며 추가 전체 회귀가 필요한 근거가 생기면 기록한다.
  • 서버에 이미 도달했을 수 있는 실패 POST rollback은 검사나 보장 범위가 아니다. 승인된 실서버 QA 환경 없이 실제 인증·회원 상태를 변경하지 않는다.

승인과 완료 조건

  • Phase 7 PRD/계획의 명시적 구현 승인을 받는다. 2026-10-13 사용자 “구현 승인”.
  • P7-T1과 P7-QA의 로컬 실행 증거를 확보한다. 미실행 VoiceOver 제스처·7개 실서버 진입 및 실제 toast 검증은 완료 주장에 포함하지 않는다.

순서: 계획 승인 대기 → P7-T1 → P7-QA. 커밋·푸시는 별도 요청 없이는 하지 않는다.

P7-PLAN 문서 작성 — 2026-10-13

  • 완료: 사용자 확정 범위·V2 재사용·두 문장 사이 줄바꿈을 PRD와 이 계획에 반영했다. 새 작업 문서나 앱 코드는 만들지 않았다.
  • 근거: 샘플/정책, 실제 패널·VM·V2 modal·caller·I18n·일반 설정·기존 auth 검사 스크립트 대조와 읽기 전용 계획 검토. shared 제목은 그대로 사용하며 인증 전용 설명만 수정하도록 범위를 한정했다.
  • 검증: git diff --check exit 0, git diff --stat에서 기존 문서 두 개만 변경됨을 확인했다. 추가 섹션을 다시 읽어 확정 문구·줄바꿈·승인 gate·일반 설정 제외와 대조했다. 기능 테스트·빌드·native 실행·서버 요청은 계획 단계라 실행하지 않았다.
  • 남은 항목: Phase 7 구현 승인. 이전 Phase의 승인·완료 기록은 보존하며 이번 구현 승인으로 재사용하지 않는다.

P7-T1 구현 시작 — 2026-10-13

  • 사용자 “구현 승인” 확인. 승인된 인터페이스로 VM/실제 소스 회귀 검사와 UI/caller 작업을 분리하며 수정 파일이 겹치지 않게 한다.
  • 현재 작업 디렉터리를 유지한다. 기존 두 계획 문서 변경은 보존한다. 신규 worktree·외부 의존성·앱 테스트 타깃은 추가하지 않는다.
  • 시나리오: 최초/POST 실패 후 종료, 계속하기·dim 값 보존, 새 인증 재진입, 기존 저장·모션 유지, native 접근성·줄바꿈, 두 앱 빌드 및 기존 회귀 검사.

P7-T1 구현 및 자동 검증 — 2026-10-13

  • 구현 완료: VM에 guarded cancel 13줄 추가, 공용 패널의 X·고정 헤더·V2 modal overlay·취소 callback, 여섯 caller의 pendingAction 정리와 인증 전용 다국어 문구를 반영했다. 공용 modal·일반 설정·API·프로젝트 파일은 변경하지 않았다.
  • RED: 구현 담당자가 실제 VM 컴파일에서 has no member 'cancelAuthContentSettings' exit 1을 확인했다. GREEN: coordinator가 새 검사 스크립트를 직접 실행해 exit 0을 확인했다. 실제 VM·DTO·UserDefaults extension/native Combine이며 저장소·AppState·메모리 저장소는 doubles다.
  • 실제 검사: 최초 MyPage/callback 종료, transport/HTTP/envelope/decode/missing-data/invalid-DTO 6종 실패 뒤 종료·새 인증, 이전 closure 해제·미실행, 새로운 callback 동기화 후 1회, 제출 중 취소 무시, MyPage 저장·재시작, main dispatch 및 무관한 notice subscription 보존 모두 PASS.
  • 기존 전체 for test in work/tests/check-*.sh; do bash "$test" || exit $?; done를 변경 전·후 직접 실행해 exit 0을 확인했다. 사용자 ulw의 전체 suite 요구에 따라 계획의 focused 기본 범위보다 검증만 확대했으며 생산 수정은 늘리지 않았다. 기존 실제 serializer 4조합도 exit 0.
  • 두 앱 Debug simulator 빌드 exit 0. 명령은 기존 workspace와 각 scheme, -destination 'generic/platform=iOS Simulator', cached derivedData(SodaLive-dev-commit-build/SodaLive-commit-build), -disableAutomaticPackageResolution CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES ARCHS=x86_64 -quiet build다. 현재 호스트는 x86_64/Xcode26.0이다.
  • 로그: /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-exit-dev-build-final.log, auth-exit-standard-build.log. 최초 dev 빌드는 120초 tool timeout으로 중단됐으며 잔류 프로세스 없음 확인 후 600초 한도로 재실행해 통과했다. 기존 중복 Compile Sources·Pods 의존성·LiveRoom 미사용 변수·Crashlytics dSYM 경고는 범위 밖이라 수정하지 않았다.
  • lsp_diagnostics 도구 미제공. 실제 compiler/typecheck/link 성공과 Swift parse로 대체하며 LSP 통과로 표현하지 않는다. git diff --check exit 0, coordinator의 수정 파일 읽기·연결 대조 완료.
  • 남은 검증: 현재 production UI를 직접 참조하는 auth-exit-qa native 실행, 독립 기능/시각 검토, QA 자원 정리. 실제 계정·실서버는 변경하지 않았다.

P7-QA native 실행 및 최종 gate 보류 — 2026-10-13

  • 핵심 기능 PASS: native dark 10개·light 4개·375pt SE3 narrow 3개·가로 최대 글자 1개·Reduce Motion 2개 및 추가 반복 종료 1개가 기능 assertion을 통과했다. X44pt, continue/dim draft 보존, 최초/실패 후 종료, fresh ON/ALL, normal save host-dismiss 순서, 뒤 패널 hit 차단을 실제 SwiftUI 컴포넌트에서 실행했다.
  • 접근성은 공개 UIKit API로 실제 host 노드를 순회해 팝업 title/body/두 버튼만 노출됨을 확인했다. 실제 VoiceOver swipe/focus 세션은 실행하지 않았으며 노드 검사를 그 증거로 대체하지 않는다.
  • coordinator 재실행: AuthExitQA의 -only-testing:AuthExitQAUI/AuthExitUITests/testKoreanFemaleContinueDimAndExit를 직접 실행해 1 test/0 failures/exit 0. ParentCore01.xcresult, parent-core-01.log에 cancelled=1;onCancel=1;dismissed=1;post=0;success=0;restart=0;pending=false;receipt=false;save=false를 확인했다. 팝업과 패널 모두 사라지고 원래 QA host가 남았다.
  • 증거 부모: /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/auth-exit-qa. EVIDENCE.md에 명령/한계, capture-inventory.json에 PNG74개와 tree/hash, 각 run에 xcresult·source-before/after를 보존했다. 같은 production13개 hash 유지. regular ko ON/OFF baseline diff는 0.54%/0.64%, similarity99이나 시각 성공 판정은 아니다.
  • 시각 FAIL REV-P7-001: 가로 + 최대 Dynamic Type에서 변경 없는 shared modal이 경고 본문과 버튼명을 생략한다. phase7-landscape-02/modal-upright.png를 coordinator도 직접 열어 확인했다. 기능 test는 전체 accessibility label을 읽어 통과하므로 시각 gate와 분리한다. 정상 크기 한국어 팝업은 두 문장 줄바꿈이 온전히 표시된다.
  • Oracle 코드·VM 검토는 APPROVE(해당 범위 한정), 코드 수정 요구 없음. native 시각 FAIL과 실제 VoiceOver/일반 모션 정량 검증 미완료 때문에 전체 Phase7 gate는 완료하지 않는다. 일본어 modal의 단어 중간 줄바꿈은 추가 시각 검토 사항이다.
  • QA harness 보정: XCTest .exists는 VoiceOver 노드 의미와 달라 공개 UIKit 노드 검사로 교체했고 hit/disabled 검사를 유지했다. 44pt의 1e-6 부동소수점 오차만 허용했다. Reduce Motion 설정 복원 오류는 YES/NO 변환 후 원값 검증으로 정리했으며 해당 영상은 일반 모션 증거로 사용하지 않는다. production 수정/검사 약화 없음.
  • 정리: worker의 teardown-receipt.json에 앱/runner6건 삭제·설정/portrait 복원·두 소유 기기 Shutdown·recorder 종료 증거가 있다. coordinator 핵심 재실행으로 재설치한 앱/runner2개도 uninstall exit0 후 소유17Pro shutdown exit0으로 정리했다. 증거 파일은 보존한다.
  • 최종 상태: [blocked] 공용 팝업의 큰 글자 대응 수정 범위 승인 필요. 현재 구현은 보존하며 커밋/푸시하지 않았다.

Task P7-R1: 종료 팝업의 큰 글자 경고·동작명 보존 (완료)

Goal P7-R1: 가로·최대 글자에서도 경고 두 문장과 ‘계속하기’·‘종료하기’를 생략 없이 읽고 조작할 수 있다.

  • Review: reviews/phase7-auth-settings-exit.md, REV-P7-001.
  • 시작 조건: 사용자 범위 확대 승인. 기존 공용 modal 무변경 제약 때문에 승인 전 production 수정 금지.
  • Files: 승인 시 SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift의 필요한 최소 레이아웃과 실제 사용 caller들 확인. 별도 새 팝업이나 글자 크기 강제 축소로 우회하지 않는다.
  • RED: 현재 actual modal의 가로 + 최대 글자에서 경고·버튼 ellipsis를 native 캡처로 재현했다.
  • 수정 범위 승인 후 PRD 제약과 Task를 확정한다. 사용자 “최소한 수정”: 공용 modal 한 파일의 크기·줄바꿈·필요한 overflow만 보정하고 기존 기본 디자인·동작·문구를 유지한다.
  • 최소 레이아웃 수정으로 두 경고 문장·버튼명 전체 표시 및 조작을 검증한다.
  • 해당 UI QA와 공용 컴포넌트 영향 회귀(실제 copy의 단일/두 버튼 fixture·caller 정적 연결), 두 앱 빌드·집중 독립 시각 gate를 통과시킨다. 모든 caller 실계정 화면을 실행한 것은 아니다.
  • 실제 결과·QA 자원 정리를 본 문서와 리뷰에 누적한다.

P7-R1 승인 후 재개

  • 기존 native RED 캡처를 재사용하며 새 QA 프로젝트를 만들지 않는다. 가로 최대 글자·세로 최대 글자·기본 크기·기존 공용 팝업 한/두 버튼의 영향을 중심으로 검증한다.
  • 회귀 원인은 shared modal의 고정 폭/제목 높이와 확대 글자의 공간 부족이다. 글자 강제 축소, 신규 팝업·설정·API·추상화 추가는 하지 않는다.
  • 검증: 기존 auth-exit-qa의 해당 native 시나리오와 실제 screenshot, 기존 종료 회귀 script/전체 shell suite, 두 앱 cached build. 실제 VoiceOver 조작·실서버 검증과 접근성 노드 검사 범위는 구분한다.

P7-R1 수정 및 최종 로컬 검증 — 2026-10-14

  • 공용 SodaV2ActionModal.swift만 +46/-22줄로 보정했다. 기본340pt 자연 높이·간격·색상·button2→button1 순서/단일 primary/callback 보존. 접근성 크기에서 가용 폭을 사용하고 제목/본문/버튼은 세로 압축하지 않으며 넘치는 글만 스크롤한다. 글자 축소·새 API·의존성 없음.
  • RED는 기존 실제 가로 최대 글자 ellipsis 캡처다. GREEN은 auth-exit-qa/R1-EVIDENCE.md: 가로최대1, 375pt세로최대2, 기본ko/quick/ja3, 단일/두 버튼fixture1 모두 PASS. fixture의 상태바 dim 탭 좌표 실패는 scratch 좌표만 보정해 callback primary1/secondary1/dim1로 통과했다. production 변경은 한 번이었다.
  • coordinator가 PHASE7_READY=1 bash run_phase7.sh r1-landscape r1-parent-landscape-01을 직접 실행해 1 test/0failures/46.864초/exit0을 확인했다. 실제 최대 글자 가로에서 전체 경고가 스크롤로 보이고, 고정된 두 버튼명·종료 동작이 통과했다. 종료 후 cancel1/onCancel1/hostDismiss1/post0/success0/restart0.
  • 현재 소스 read와 최대 글자 가로/세로 PNG 직접 확인 완료. 기본 한국어 popup의 이전/수정 후 visual-qa.mjs image-diff는 1206×2622 일치, 2336 pixels/0.0007 diffRatio, similarity100, alpha정상이며 hotspot은 상단 시계 영역이다. 기본 popup 배치가 보존됐다.
  • 최신 전체 work/tests/check-*.sh exit0. 최신 두 Debug simulator workspace build exit0: tmp/opencode의 auth-exit-r1-dev-build.log, auth-exit-r1-standard-build.log. 이전과 같은 cached derivedData·x86_64·signingNO 옵션. Swift parse/diff check exit0. LSP 도구는 여전히 제공되지 않아 실제 compiler로 대체했다.
  • 독립 최종 기능 Oracle APPROVE, 독립 시각 reviewer PASS. 시각 reviewer는 최신 핵심 캡처11장과 수정 전2장을 직접 비교했고, 중복 프레임27장 전체 또는 모든 실계정 화면을 검사했다는 주장은 하지 않았다. 확정 차단 발견사항 없음. 기존 일본어 必/要 줄바꿈은 의미를 숨기지 않는 경미 사항으로 미수정.
  • worker r1-teardown-receipt.json에 두 기기 설정/portrait복구와 앱/runner4건 삭제·shutdown 증거가 있다. coordinator 재실행 후 light/large/ReduceMotion0을 직접 확인하고 재설치 앱/runner2개 uninstall,17Pro shutdown exit0. 기록과 PNG는 보존한다.
  • 남은 한계: live VoiceOver 제스처, 실제 toast lifecycle, 7개 실계정/실서버 통합과 모든 공용 popup 사용 화면 runtime은 미실행이다. 관련 원래 체크박스는 완료로 위장하지 않는다. 완료 범위는 요청 기능 구현·현재 소스의 로컬 기능/시각/빌드 gate다.