Files
sodalive-ios/docs/20261029_추천_서브_배너/reviews/evidence/native/README.md
T

11 KiB
Raw Blame History

추천 서브 배너 Native QA

결과: 실행한 native 배치·비율·empty·자동 순환·이미지 실패 격리 시나리오 PASS. 물리 탭/swipe, Reduce Motion ON, 실제 앱 전체 통합은 NOT RUN이다. 제품 파일, 부모 PRD/계획은 수정하지 않았다.

실행 Surface와 출처

  • 부모 소유 iPhone SE3/iOS 18.3, UDID 482F7827-2E3A-4559-BAF4-48B68CC5725B, 375×667pt, 2x. Xcode 26 SDK로 x86_64-apple-ios16.6-simulator 대상 독립 UIKit UIHostingController 앱을 컴파일했다. iOS 16.6 런타임 실행은 아니다.
  • production SubBanner.swift, SubBannerLinkOpener.swift, SubBannerCarousel.swift, MainHomeRecommendationView.swift, MainContentRecommendationView.swift를 수정·추출·복제하지 않고 원본 경로 그대로 컴파일했다. 실제 spacing/color/hex/font 소스도 동일하게 포함했다.
  • Kingfisher는 prebuilt Kingfisher.o와 x86_64 module을 링크했다. workspace/project Package.resolved는 7.12.0이다. KFImage를 대역으로 바꾸지 않았다. dependency-hashes.json에 링크 입력 해시가 있다.
  • Kingfisher의 실제 downloader에 URLSessionConfiguration.ephemeral.protocolClasses = [FixtureProtocol.self]를 설정했다. QA host만 생성 PNG로 응답하고 그 외 요청은 로컬에서 실패시킨다. 인터넷·서버·계정은 사용하지 않았다. 1200×400 teal A, 1200×600 orange B는 explicit QA 이미지이며 이미지 안 글자는 fixture 내용이다.
  • source-hashes-before.json, source-hashes-after.json, source-hashes-final.json은 production 입력 9개 모두 동일하다. source-stability.json의 unchanged=true. 따라서 이번 실행 중 재캡처가 필요한 제품 변경은 없었다.

명시적인 대역과 한계

source/Doubles.swift에 대체 목록 전체가 있다. 두 recommendation View composition 자체는 실제 production이며, 하위 섹션은 QA ...라고 적힌 48pt 회색 bar 또는 EmptyView로 대체했다. VM은 네트워크를 호출하지 않는 fixture VM, DTO는 최소 FixtureResponse, refresh wrapper는 native ScrollView, toast/purchase/media player는 무효 대역이다. 실제 기존 메인 배너 carousel은 이 하네스에서 실행하지 않았다.

따라서 원문 View의 조건부 삽입 위치·VStack(s24)·부모 수평 padding 없음은 검증했지만, 실제 추천 카드 높이/로딩/refresh/toast/구매·API·인증·추천 response 필터 및 콘텐츠 isEmpty는 이 QA로 검증하지 않았다. FixtureResponse.eligibleSubBanners는 isEligible만 사용한다. placement 분리·실서버 목록은 부모 focused checks 범위다. 오류 격리에서 주변 대역 bar 유지를 관찰했으며 실제 콘텐츠의 탭 동작을 증명하지 않는다.

증거와 판정

captures/actions.json에 monotonic timestamp, 입력 id 전체, 이미지 요청, scene phase, OS Reduce Motion 값, 주변 섹션/direct carousel 실제 geometry와 callback driver 결과가 있다. 모든 41개 native PNG는 앱 window drawHierarchy 캡처이며 별도 native-os-final.png는 simctl io screenshot이다. 42개 원본은 750×1334 PNG다. contact-1.png~contact-4.png는 원본을 축소 배치한 검토용 contact sheet이지 별도 UI 구현이 아니다.

시나리오 판정 실제 관측/증거
홈 지정 위치 PASS home-first: QA active creators 다음, debut 이전. 기존 main banner 앞뒤 순서 유지
콘텐츠 지정 위치 PASS content-first: QA main banner 다음, exclusive 이전
전체 폭·원본 비율 PASS 양 화면 A는 x=0,width=375,height=125pt, B는 x=0,width=375,height=187.5pt. home-second, content-second
s24·empty 추가 gap 없음 PASS 이미지 위/아래 각각 24pt. empty는 인접 bar 사이 일반 24pt만 남고 배너 추가 간격 0pt. home-empty, content-empty
실제 5초 sleep 자동 전환 PASS, 정밀도 한정 production sleep 값 변경 없음. Home은 scenario 시작 후 4.928s 기록까지 A, 5.807s 기록의 transition에서 B 이동 중, 6.553s 기록에 B 정착. Content는 4.632s 기록 A, 5.526s 기록 transition, 6.306s 기록 B. 기록은 screenshot 완료 뒤라 transition 시각은 정확한 시작점이 아니다
2항목 wrap PASS home-wrap, content-wrap: 다시 A 01/02
단일 항목 고정 PASS, timer 생성 여부 직접 계측 아님 양 화면 one/one-after-6s PNG SHA-256 동일, 01/01 유지. production count guard도 보존
21개·20개 제한 없음 PASS many-first 01/21 → many-twenty-first는 실제 20/21 → many-wrap는 실제 21/21 → OS final 01/21. 실제 약 106초 기다렸고 timer/선택 state를 직접 바꾸지 않음
컨테이너 resize PASS 양 화면 375→320pt에서 x=0,width=320,height=106.6667pt. home-resize-320, content-resize-320. 기기 회전/실제 화면 resize가 아니라 부모 SwiftUI frame 변경
지연 이미지·현재 비율 격리 PASS 첫 A 요청 8초 지연, 초기 carousel height=0. delayed-next-valid에서 B height187.5. 늦은 A 성공 후 delayed-old-image-arrived도 B/187.5 유지
실패 이미지 국소 처리 PASS A→실패 B에서 height125 회색 placeholder와 02/02, 주변 QA bar 유지. failure-placeholder-other-content. 이후 failure-wrap-valid에서 A 정상
inactive 중지/active 재개 PASS, 주입 환경 SwiftUI scenePhase .inactive 실제 전달 probe 후 6.094초 두 PNG SHA 동일 B 유지. active 전달 후 4.790초 기록 B, 6.276초 기록 A. inactive-after-6s, active-next. OS background 실험은 NOT RUN
unmount/remount·축소 PASS 6초 unmount 후 remount-first A, 이후 remount-next B. 2→1은 shrink-one A/01/01
light/dark PASS, 제품 black 유지 각 화면 light capture와 dark first 동일한 검은 화면/legible indicator. production black 배경과 하네스 명시 override 사용. System theme 전환 전체 앱은 미검증
링크 fallback driver PASS, 실제 탭 아님 production opener에 false completion 주입: sodaqa://banner/900 후 https://subbanner.qa.invalid/destination 각 1회. opener-driver-not-physical-tap 로그. UIApplication.open 실제 실행 없음
physical tap/swipe·hit testing NOT RUN native 좌표 입력 도구 없음. 캐러셀의 onTap callback은 physical input으로 실행하지 않음. gesture 정의 확인을 동작 PASS로 취급하지 않음
Reduce Motion ON NOT RUN accessibilityReduceMotion은 read-only environment라 setter 주입 compile 실패. 해당 modifier를 QA 소스에서 제거 후 성공. OS 값 false trace만 수집. 부모 simulator 접근성 설정은 바꾸지 않음. reduce-* 이름은 ON 증거가 아님

many-twenty-first/many-wrap/reduce-transition 등 이름은 예정 촬영 시점명일 뿐 실제 상태 판정이 아니다. 실제 페이지/상태는 위 관측과 PNG를 따른다. scheduler 및 이미지 준비 시간으로 21번째 예정 시점에는 20번째였고 다음 캡처에 21번째가 확인됐다. 엄밀한 5.000초 정확도, 프레임률, 애니메이션 매끄러움·인터럽트·장시간 중복 timer 부재는 보증하지 않는다.

Geometry와 검증 방법

verification.json은 주변 QA bar의 SwiftUI global frame 차이와 PNG의 양끝 fixture 색상 scan을 교차 검증한다. Home A/B는 y280pt, Content A/B는 y136pt. Home 뒤 bar y429→491.5, Content 뒤 bar y285→347.5. Empty 뒤 bar는 각각 y280/y136으로 올라와 원래 섹션 간격만 남았다. Direct context는 .background(GeometryReader)로 실제 carousel geometry를 기록한다. production carousel에 modifier를 삽입하지 않았다.

source/assess.py 실행 exit0: product 해시 동일, 모든 native PNG signature/750×1334/소스보다 신선함, 두 화면 전체 폭/비율/s24/empty/320pt, single 고정, inactive 고정, 지연 image 높이 격리, 21개 입력, opener 순서 assert 모두 PASS. 원본 주요 PNG와 전체 contact sheet를 직접 열어 겹침·잘림·페이지 번호·fixture 순서를 확인했다. 독립 리뷰는 요청에 따라 호출하지 않았다.

실제 명령과 진단

정확한 확장 인자·stdout/stderr/exit는 commands.json에 있다. 전체 앱 xcodebuild는 실행하지 않았다.

export TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99
export NODE_COMPILE_CACHE="$TMPDIR"
export BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TMPDIR"
python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/run.py
python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/assess.py
python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/cleanup.py

핵심 compile: xcrun --sdk iphonesimulator swiftc -swift-version 5 -target x86_64-apple-ios16.6-simulator -sdk <iPhoneSimulator26.0.sdk> -I <Debug-iphonesimulator> -module-cache-path <own-root>/module-cache <production 9 sources> <Doubles.swift> <Harness.swift> <Kingfisher.o> -o <own-root>/SubBannerQA.app/SubBannerQA. Install/launch는 지정 부모 UDID와 독립 bundle qa.sodalive.subbanner.native.tstn99만 사용했다.

초기 컴파일은 QA의 read-only Reduce Motion setter 사용으로 exit1, modifier 제거 후 동일 native 전략의 두 번째 컴파일 exit0. 실패 명령도 receipt에 보존했다. LSP 진단 도구는 제공되지 않아 실행하지 못했으며 실제 swiftc compile 진단으로 대체했다. QA는 현재 소스를 그대로 소비했고 production 수정은 하지 않았다.

소유권과 정리

ownership.json에 앱 bundle, PID 93117, own temporary root를 launch 직후 기록했다. cleanup.json은 own app 종료·uninstall, PID 부재, own root 삭제, 부모 device Booted 유지, product 해시 동일을 확인한다. 공유 cache 삭제, simulator shutdown/delete/reset, worktree, commit/push, 외부 서비스 호출은 하지 않았다.

남은 확인: physical tap/swipe, Reduce Motion ON, OS background/foreground, 실기기/iOS16.6/Dynamic Type/VoiceOver, 전체 SodaLive 앱 통합·실서버는 미검증이다. 이 결과는 native 부분 수용 증거이며 전체 제품 최종 승인이나 독립 리뷰 PASS를 대신하지 않는다.