42 KiB
본인인증 후 콘텐츠 보기 설정 계획
기준 문서
- 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=ALLnormalize. 사용자 확정이 아니다. - receipt/token/payload 원문을 기록하지 않고, 이 흐름의 raw
onDonelog만 제거한다. - 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)
- Modify:
-
Interfaces:
- Consumes: PRD §11.1의
receipt_id,isAdultContentVisible,contentType계약. - Produces:
AuthVerifyRequest의 JSON encoder 출력과 runnable assert check.
- Consumes: PRD §11.1의
-
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_idCodingKey를 보존한다. -
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건을 기록한다. -
SodaLiveworkspace Debug 시뮬레이터 빌드 exit 0을 확인한다. 실제 destination/아키텍처 옵션은 아래 Progress에 기록한다. -
SodaLive-devworkspace 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.mdREV-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-dev14:37:2314:46:37,14:53:05 빌드 exit 0. coordinator가 14:55에 현재 소스로 양쪽 incremental build를 다시 실행해 모두 exit 0을 확인했다.SodaLive14:46:56- 두 빌드 공통 명령:
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.xcresult3개/0실패,IntrinsicOverflow.xcresult1개/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 hash9b88f2e4999f9dfd28f32576385ced5b6078002671f6092f199f243ea43b1a27와 캡처 대상이 일치한다. - 정리: 임시 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 생성 없음.