From 8f51b2a57a3c99c7711ab8dd2848978d13f71ce3 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Tue, 13 Oct 2026 16:01:15 +0900 Subject: [PATCH] =?UTF-8?q?docs(ads):=20Yandex=20=EA=B4=91=EA=B3=A0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=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=EC=9D=84=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan-task.md | 281 ++++++++++++++++++ .../prd.md | 134 +++++++++ .../phase1-yandex-reference-removal.md | 116 ++++++++ 3 files changed, 531 insertions(+) create mode 100644 docs/20261013_Yandex_광고_잔여_참조_제거/plan-task.md create mode 100644 docs/20261013_Yandex_광고_잔여_참조_제거/prd.md create mode 100644 docs/20261013_Yandex_광고_잔여_참조_제거/reviews/phase1-yandex-reference-removal.md diff --git a/docs/20261013_Yandex_광고_잔여_참조_제거/plan-task.md b/docs/20261013_Yandex_광고_잔여_참조_제거/plan-task.md new file mode 100644 index 00000000..c05dfd27 --- /dev/null +++ b/docs/20261013_Yandex_광고_잔여_참조_제거/plan-task.md @@ -0,0 +1,281 @@ +# Yandex 광고 잔여 참조 제거 계획 + +| 문서 항목 | 내용 | +|---|---| +| 상태 | 코드 구현·자동 검증 완료, 수동 QA 외부 조건 대기 | +| 작성일 | 2026-10-13 | +| 요구사항 기준 | [prd.md](prd.md) | +| API 기준 | PRD §11, 기존 캔 충전 계약 유지 | +| 현재 Phase | Phase 1 수동 QA 대기 | +| 현재 활성 Goal | 없음, P1-GATE [blocked] | + +## 목표 + +Yandex SDK와 상수가 제거된 앱의 잔여 참조를 없애고, 광고 없이 기존 화면과 캔 충전을 사용할 수 있도록 한다. + +## 현재 상태 + +| Phase | 상태 | 완료 Task | 다음 Goal | 남은 조건 | +|---|---|---|---|---| +| 1 | 외부 조건 대기 | 3/3, Gate 부분 검증 | P1-GATE [blocked] | 11개 화면 및 테스트 계정 캔 구매 QA | + +사용자 선행 작업인 Pod 의존성·상수 제거는 완료 기준선이며 이 계획에서 재수행하지 않는다. 구현 시작 시 남아 있던 SDK·상수 참조는 P1-T1~T3에서 제거했다. + +## 범위 + +### 포함 + +- 11개 배너 소비 코드, 보상형 광고 소비 코드, 광고 전용 enum·문구 제거. +- AppDelegate의 Yandex 연결, 공용 광고 파일 및 두 타깃 프로젝트 참조 제거. +- 보존 경계 대조, 정적 검사, 두 스킴 빌드, 수동 회귀 확인과 구현 후 리뷰 기록. + +### 제외 + +- 두 Info.plist 전체, ATT/IDFA 및 다른 SDK, 캔 구매 UI·Repository·API 계약 변경. +- Pod 설치·업데이트·재정리, Constants 재수정, 미사용 Pods 생성물 직접 삭제. +- Project Format 변경, worktree·브랜치 생성, 커밋, 과거 문서 재작성, 새 테스트 인프라. + +## 기술적 제약 + +- 현재 작업 디렉터리에서만 순차 실행한다. 사용자의 기존 변경을 덮어쓰지 않는다. +- 모든 구현은 문서 승인 후 시작하며, 광고에만 속하는 부분만 삭제한다. +- `objectVersion = 77`과 운영·개발 타깃 구성은 유지한다. +- 신규 로직을 추가하지 않는 삭제 작업이며 기존 테스트 타깃이 확인되지 않았다. 아래 RED/GREEN은 명시된 정적 부재 검사로 수행하고 빌드·수동 검증을 보완한다. 테스트 인프라·영구 검증 스크립트는 추가하지 않는다. +- 부재 검사의 `rg` 종료 코드는 1만 성공이다. 0은 금지 참조 잔존, 2는 검사 오류다. 단순 `! rg`로 검사 오류까지 성공으로 처리하지 않는다. + +## Phase 1. 잔여 광고 참조 제거 + +**Phase 결과:** Yandex 없이 앱을 빌드하고 기존 화면과 캔 충전을 사용할 수 있다. + +**선행조건:** 사용자의 PRD·계획 승인. 승인 전에는 아래 체크박스를 실행하지 않는다. + +**Phase 완료 조건:** `P1-T1`~`P1-T3` 및 `P1-GATE` 완료 증거와 실제 검증 기록. + +### Task 1.1 배너 소비 코드 제거 + +**Goal 실행 `P1-T1`:** 11개 화면에서 광고 호출을 없애고 주변 콘텐츠를 보존한다. + +- **시작 조건:** 문서 승인, `YAN-001`, `YAN-005` 확인. +- **완료 증거:** 11개 대상 파일의 배너 참조 부재와 변경 대조. 화면 QA는 Gate에서 수행. +- **범위 밖:** 다른 화면·스타일 개편, 공용 광고 파일 삭제. + +**Files (Modify, 모두 `SodaLive/Sources/` 기준):** + +```text +Live/LiveView.swift +Live/Room/Detail/LiveDetailView.swift +Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift +Content/Series/Main/Home/SeriesMainHomeView.swift +Content/Series/Main/DayOfWeek/SeriesMainDayOfWeekView.swift +Content/Series/Main/ByGenre/SeriesMainByGenreView.swift +Notification/List/PushNotificationListView.swift +Settings/Notification/NotificationSettingsView.swift +Chat/Character/CharacterView.swift +Chat/Original/OriginalTabView.swift +Chat/Talk/TalkView.swift +``` + +**Interfaces:** 기존 화면 진입·콘텐츠·탭·스크롤은 유지하고 `YandexInlineBannerView` 소비만 제거한다. + +- [x] **RED:** 아래 명령을 배너 참조 0건 수용 검사로 사용한다. +- [x] **RED 확인:** 종료 코드 0과 기존 배너 호출 11건을 확인한다. +- [x] **GREEN:** 배너 및 광고 전용 modifier·광고만 감싼 조건문을 삭제한다. 주변 공용 간격은 유지한다. +- [x] **GREEN 확인:** 같은 명령이 종료 코드 1인지 확인한다. +- [x] **REFACTOR:** 삭제로 생긴 빈 블록만 정리하고 11개 파일을 대조해 관련 없는 변경이 없는지 기록한다. + +```bash +rg -n 'YandexInlineBannerView' SodaLive/Sources --glob '!YandexAdSupport.swift' +``` + +### Task 1.2 채팅 보상형 광고 잔여 코드 제거 + +**Goal 실행 `P1-T2`:** 광고 사전 로딩·표시·보상 경로를 제거하고 캔 구매 계약은 그대로 둔다. + +- **시작 조건:** `P1-T1` 완료, `YAN-002~003`, `YAN-005` 확인. +- **완료 증거:** 광고 소비 심볼 부재, `.ad` 제거 및 기존 캔 구매 코드 보존. +- **범위 밖:** 구매 함수 재설계, optional 제거, Repository/API·충전 UI 수정. + +**Files:** + +- Modify: `SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift` +- Modify: `SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift` +- Modify: `SodaLive/Sources/I18n/I18n.swift` +- Verify only: `SodaLive/Sources/Chat/Talk/Room/ChatRoomRepository.swift`, `SodaLive/Sources/Chat/Talk/TalkApi.swift`, `SodaLive/Sources/Chat/Talk/Room/ChatRoomView.swift`, `SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaNoticeItemView.swift` + +**Interfaces:** `purchaseChatQuota(canOption:)`, `CAN`, `CAN_10`, `CAN_20`, `container = "ios"`, optional `canOption`, 쿼터·캔 갱신 및 `quotaChatCount` 유지. + +- [x] **RED:** 아래 두 명령을 광고 전용 심볼·enum 부재 검사로 사용한다. +- [x] **RED 확인:** 각 명령이 종료 코드 0으로 현재 참조를 찾는지 확인한다. +- [x] **GREEN:** `showRewardedAdForChatQuota`, `prepareRewardedAdIfNeeded`와 호출, `.ad`, `quotaAdAction`, `rewardedAdLoadFailed`만 제거한다. +- [x] **GREEN 확인:** 두 명령 모두 종료 코드 1인지 확인하고 기존 캔 요청 필드·값을 코드 대조한다. +- [x] **REFACTOR:** 삭제로 발생한 미사용 코드만 정리한다. 구매·오류·로딩·잔액 갱신 로직은 재구성하지 않는다. + +```bash +rg -n 'YandexRewardedAdManager|showRewardedAdForChatQuota|prepareRewardedAdIfNeeded|quotaAdAction|rewardedAdLoadFailed' SodaLive/Sources --glob '!YandexAdSupport.swift' +rg -n 'case[[:space:]]+ad[[:space:]]*=[[:space:]]*"AD"' SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift +``` + +### Task 1.3 공용 SDK 연결과 프로젝트 참조 제거 + +**Goal 실행 `P1-T3`:** 소비 코드가 제거된 Yandex 공용 구현과 SDK 연결을 제거한다. + +- **시작 조건:** `P1-T1`, `P1-T2` 완료, `YAN-004` 확인. +- **완료 증거:** 소스·프로젝트·의존성 명세에서 Yandex 및 전용 하위 라이브러리 참조 부재. +- **범위 밖:** Pods 생성물 수동 정리, plist·Project Format 변경. + +**Files:** + +- Modify: `SodaLive/Sources/App/AppDelegate.swift` +- Delete: `SodaLive/Sources/Common/YandexAdSupport.swift` +- Modify: `SodaLive.xcodeproj/project.pbxproj` +- Verify only: `Podfile`, `Podfile.lock`, `Pods/Manifest.lock`, `Pods/Pods.xcodeproj/project.pbxproj` + +**Interfaces:** AppDelegate의 다른 SDK 초기화와 두 앱 타깃 구성은 유지한다. 신규 인터페이스는 없다. + +- [x] **RED:** 아래 명령을 SDK·공용 참조 부재 검사로 사용한다. +- [x] **RED 확인:** 종료 코드 0과 AppDelegate·공용 파일·프로젝트의 잔존을 확인한다. +- [x] **GREEN:** Yandex import·초기화·전용 주석, 공용 파일, 파일 참조·그룹 항목·두 타깃 build-file/Sources 등록을 제거한다. +- [x] **GREEN 확인:** 같은 명령이 종료 코드 1인지 확인하고 두 타깃에 누락 파일 참조가 없는지 확인한다. +- [x] **REFACTOR:** 프로젝트 전체 재직렬화 없이 해당 항목만 정리하고 Gate로 진행한다. + +```bash +rg -n -i 'yandex|appmetrica|divkit|vgsl' SodaLive/Sources SodaLive.xcodeproj/project.pbxproj Podfile Podfile.lock Pods/Manifest.lock Pods/Pods.xcodeproj/project.pbxproj +``` + +### Phase 1 Gate + +**Goal 실행 `P1-GATE`:** 제거 완료와 보존 조건, 빌드·화면·충전 회귀를 실제 결과로 판정한다. + +- **시작 조건:** 구현 Task 3개 완료. +- **TDD 예외 사유:** 구현 후 검증·기록만 수행하는 Task다. +- **대체 검증:** 정적 검사, 승인 후 구현 직전의 보존 파일 기준선 대조, 아래 빌드와 수동 QA. +- **완료 증거:** 명령 종료 코드·수동 결과·리뷰 경로를 Progress에 기록. 환경 차단은 성공으로 처리하지 않는다. + +승인 직후 구현 전에 두 Info.plist, Podfile/lock, 두 Constants, 캔 구매 Repository/API/UI, `AppViewModel.swift`, `SodaLiveApp.swift`의 상태를 읽기 전용으로 기록한다. 변경 후 사용자 기존 상태와 대조한다. 두 plist는 전체 내용이 동일해야 하며 `objectVersion = 77`은 유지해야 한다. + +```bash +plutil -lint SodaLive.xcodeproj/project.pbxproj SodaLive/Resources/Info.plist SodaLive/Resources/Debug/SodaLive-dev-Info.plist +xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build +xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build +``` + +**Expected:** 모든 Task 부재 검사는 종료 코드 1, plist/프로젝트 문법 검사와 두 빌드는 종료 코드 0. 이 계획 단계에서 실행한 결과가 아니다. + +- [x] Task 3개의 정적 검사를 재실행한다. 과거 문서·보존 plist·미사용 Pods 리소스 파일은 부재 검사에서 제외한다. +- [x] 보존 파일과 캔 계약, Project Format을 구현 직전 기준선과 대조한다. +- [x] 두 스킴 빌드와 위 문법 검사를 통과한다. 제공되는 파일 진단 도구가 있으면 수정 Swift 파일도 검사한다. +- [ ] 11개 화면의 콘텐츠·스크롤·이동, 광고 전용 빈 공간 제거를 수동 확인한다. 승인된 테스트 계정으로 10캔·20캔 구매의 쿼터·잔액 갱신과 기존 잔액 부족·실패 처리를 확인한다. 운영 비용이 발생하는 검증은 별도 승인 없이는 수행하지 않는다. +- [x] `docs/sample/sample-review.md`를 읽고 `reviews/phase1-yandex-reference-removal.md`를 작성한다. 확정 수정 사항은 코드 수정 전에 이 계획에 신규 Task로 추가한다. + +**[blocked] 수동 QA:** 사용 가능한 기기는 새 전용 시뮬레이터였고, 이 세션에 네이티브 탭/화면 탐색 자동화와 승인된 테스트 계정·구매용 데이터가 없다. 앱 실행만으로 11개 화면과 유료 거래를 검증했다고 판정하지 않는다. 계정·데이터와 수동 조작 가능한 환경을 준비한 후 같은 Gate를 이어서 검증한다. + +## 실행 순서와 의존성 + +`사용자 승인 → P1-T1 → P1-T2 → P1-T3 → P1-GATE` + +동시에 하나의 Task만 진행하고 현재 작업 디렉터리를 사용한다. 각 부분 삭제 단계에서는 빌드 가능 상태라고 주장하지 않는다. 전체 연결 제거 후 두 스킴을 검증한다. + +## 변경 금지 항목과 중단 규칙 + +PRD의 Non-Goals를 따른다. 승인 전 구현, worktree 생성, 자동 커밋, 생성물 직접 편집을 금지한다. 예상 밖의 API·UX 결정을 요구하는 문제가 나오면 임의로 확장하지 말고 PRD에 기록한 뒤 한 가지 질문으로 확인한다. 환경상 QA가 불가능하면 사유를 명시하고 해당 Gate를 미완료로 남긴다. + +## Progress + +### 문서 준비 1차 실행 - 2026-10-13 + +- 상태: 문서 작성, 구현 승인 대기. +- 무엇을: 사용자 완료 범위와 잔여 참조, 보존 경계를 정리하고 PRD·계획을 작성했다. +- 왜: 다중 화면의 동작 변경은 구현 전 문서가 필요하며, 사용자가 문서 작성 후 승인 대기를 요청했다. +- 어떻게: 문서 정책·샘플·빌드 가이드를 읽고 `rg`로 현재 참조를 확인했다. 계획 검토는 읽기 전용으로 수행했다. +- 실행 근거: `rg -n 'Yandex|YANDEX_|quotaAdAction|rewardedAdLoadFailed|case ad = "AD"|SKAdNetworkItems|NSAdvertisingAttributionReportEndpoint|objectVersion =' SodaLive/Sources SodaLive.xcodeproj/project.pbxproj Podfile Podfile.lock SodaLive/Resources/Info.plist SodaLive/Resources/Debug/SodaLive-dev-Info.plist`에서 잔여 코드·보존 키·`objectVersion = 77`을 확인했다. +- 결과: 신규 제거 작업용 PRD·계획이 기존에 없는 것을 확인했다. 과거 배치·부분 제거 문서는 완료 이력으로 보존한다. 앱 코드·프로젝트 설정·Pods 수정과 빌드·수동 QA는 수행하지 않았다. +- 문서 검증: 생성한 두 문서를 다시 읽고 요구사항 ID·Task·상호 링크를 대조했다. `rg`로 미치환 템플릿과 미실행 완료 체크가 없음을 확인했다. 승인 대기·worktree 미사용·두 plist 보존·캔 계약·Pods 제외 조건도 두 문서에서 확인했다. +- 남은 항목: 사용자 문서 승인 후 `P1-T1`부터 실행. + +### 구현 준비 - 2026-10-13 + +- 사용자 승인 후 구현 시작 상태로 전환했다. 두 plist·Pod 명세·Constants·캔 구매 경계·마케팅 파일 총 13개의 SHA-256 기준선을 기록했다. +- 사용자가 변경한 프로젝트의 `objectVersion = 77`, 기존 항목 정렬, Copy Pods Resources 단계 제거는 보존한다. +- `P1-T1` 정적 RED 검사에서 배너 소비 11건을 확인했다. SwiftUI 삭제는 해당 11개 파일만 대상으로 진행한다. +- 실제 `ChatQuotaPurchaseRequest.swift`와 임시 Swift 드라이버를 컴파일·실행한 기준선 검사에서 `CAN_10`, `CAN_20`, nil `canOption` 직렬화 3개 시나리오가 통과했다. 영구 테스트 파일이나 테스트 타깃은 추가하지 않는다. +- 실행 환경에 `lsp_diagnostics` 도구가 제공되지 않아 Swift 구문 검사·컴파일 및 두 스킴 빌드로 보완하며, LSP 통과를 주장하지 않는다. + +### P1-T1 실행 결과 - 2026-10-13 + +- 상태: 구현·정적 검증 완료, 실제 화면 QA는 Gate에서 별도 확인. +- 무엇을: 11개 View의 배너 호출과 광고 전용 modifier 및 장르 화면의 광고 전용 조건문을 삭제했다. +- 왜: Yandex 공용 파일 삭제 전에 모든 배너 소비 경로를 제거하기 위해서다. +- 어떻게: 배너 부재 검사가 변경 전 11건(종료 코드 0)에서 변경 후 0건(종료 코드 1)으로 전환됐다. `xcrun swiftc -frontend -parse`에 11개 수정 파일을 전달한 구문 검사는 종료 코드 0이었다. 11개 파일 본문과 diff를 확인해 콘텐츠·라우팅·기존 공용 간격이 유지됨을 확인했다. +- 남은 항목: P1-T2, P1-T3, P1-GATE. 구문 검사는 전체 타입 검사나 화면 QA를 대체하지 않는다. + +### P1-T2 실행 결과 - 2026-10-13 + +- 상태: 구현·정적 검사·캔 요청 직렬화 회귀 검증 완료. +- 무엇을: 광고 표시/사전 로딩 함수와 호출, `.ad` case, 광고 전용 문구 2개를 삭제했다. 구매 함수·optional·요청 필드·잔액 갱신은 유지했다. +- 왜: SDK 제거 후 남은 채팅 광고 의존 경로를 제거하면서 기존 유료 충전을 보존하기 위해서다. +- 어떻게: 두 정적 검사가 각각 종료 코드 0에서 1로 전환됐다. 수정 Swift 파일 3개의 parser 검사는 종료 코드 0이었다. 실제 DTO를 임시 드라이버와 다시 컴파일·실행하여 `CAN_10`, `CAN_20`, nil `canOption` 3개 검사 모두 통과했다. 변경 diff에는 계획한 삭제 외 구매 로직 수정이 없다. +- 남은 항목: P1-T3 및 Gate. 실서버 구매는 실행하지 않았다. + +### P1-T3 실행 결과 - 2026-10-13 + +- 상태: 구현·정적 검사 완료, 빌드 및 수동 Gate 진행 예정. +- 무엇을: AppDelegate의 Yandex import·초기화와 공용 파일을 제거하고, 프로젝트에서 전용 등록 6줄만 삭제했다. +- 왜: 모든 소비 경로가 제거된 SDK 연결과 삭제 상수 참조를 최종 정리하기 위해서다. +- 어떻게: SDK 부재 검사가 종료 코드 0에서 1로 전환됐다. `plutil -lint`는 프로젝트 및 두 plist에서 OK, AppDelegate Swift parser는 종료 코드 0이었다. `ruby -rxcodeproj` 읽기 전용 검사로 두 앱 타깃에 Yandex 소스 항목이 없고 `objectVersion = 77`임을 확인했다. +- 남은 항목: P1-GATE. 생성물·보존 plist·사용자 선행 변경은 수정하지 않았다. + +### P1-GATE 검증 결과 - 2026-10-13 + +- 상태: 코드 구현·자동 검증·독립 코드 리뷰 완료, 화면 전체 및 실제 구매 QA는 외부 조건 대기. +- 무엇을: 두 앱 빌드, 보존 파일 대조, 실제 DTO 실행, 최종 참조 검색, 결과물 검사와 네이티브 시작 smoke를 수행했다. +- 왜: SDK를 제거한 앱의 실제 컴파일과 실행 가능 여부를 확인하면서 캔 구매·설정 보존을 검증하기 위해서다. +- 어떻게: + - 모든 Task의 정적 부재 검사: 종료 코드 1. 수정 Swift 15개 파일의 `xcrun swiftc -frontend -parse`: 종료 코드 0. LSP 도구는 미제공으로 미실행. + - `plutil -lint SodaLive.xcodeproj/project.pbxproj SodaLive/Resources/Info.plist SodaLive/Resources/Debug/SodaLive-dev-Info.plist`: 모두 OK. + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build`: 종료 코드 0, BUILD SUCCEEDED. + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`: 종료 코드 0, BUILD SUCCEEDED. + - `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build`: 종료 코드 0, BUILD SUCCEEDED. 기기용 빌드 검증 뒤 native smoke 목적으로 추가한 실행 옵션이며 프로젝트 설정 변경은 없다. + - `shasum -a 256 -c /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/yandex-ios-preserved.sha256`: 13개 보존 파일 모두 OK. + - 실제 `ChatQuotaPurchaseRequest.swift`와 임시 Swift 드라이버 컴파일·실행: CAN_10, CAN_20, nil canOption 직렬화 및 수치 검사 3/3 통과. 실행 기록은 `yandex-ios-quota-check.log`로 보존했다. + - 두 `Debug-iphoneos` 앱의 `Frameworks/`, 앱 루트 및 `xcrun otool -L` debug dylib 출력에서 Yandex/AppMetrica/DivKit/VGSL 링크·번들이 없음을 확인했다. + - `git diff --check`: 종료 코드 0. 두 공유 scheme의 TestAction에 Testables가 없으며 Yandex/채팅 쿼터용 기존 XCTest는 확인되지 않았다. 실행 가능한 XCTest 전체 suite 통과로 표기하지 않는다. +- 네이티브 smoke: + - iPhone 17 Pro / iOS 26.0 전용 기기 `F14B9E9B-F166-4694-ACBD-9804E4B7AD54`를 생성하고 boot/bootstatus에 성공했다. 기존 사용자 기기는 건드리지 않았다. + - `xcrun simctl install F14B9E9B-F166-4694-ACBD-9804E4B7AD54 /Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/SodaLive-dev.app`: 종료 코드 0. + - `xcrun simctl launch F14B9E9B-F166-4694-ACBD-9804E4B7AD54 kr.co.vividnext.sodalive.debug2`: PID 61239, 종료 코드 0. + - `xcrun simctl io F14B9E9B-F166-4694-ACBD-9804E4B7AD54 screenshot /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/yandex-ios-launch-smoke.png`: PNG 저장 후 직접 열어 홈 콘텐츠와 보존된 ATT 팝업 표시를 확인했다. 이는 11개 변경 화면의 시각 QA가 아니다. +- 독립 리뷰: Oracle `bg_16ac91d2` / `ses_ecc05fbd9ffe65w6Gb41Ii1HYZ`, 코드 APPROVE·확정 코드 결함 없음. 전체 Gate는 수동 QA가 남아 미완료라는 판정. [리뷰 보고서](reviews/phase1-yandex-reference-removal.md)에 기록했다. +- 정리: 전용 앱 terminate → 해당 기기 shutdown → delete 모두 종료 코드 0. 기기 이름 검색에서 0개를 확인했다. 임시 DTO 드라이버·바이너리를 삭제하고 부재를 확인했다. 로그·스크린샷·체크섬 기준선만 증거로 보존했다. +- 기존 경고: 중복 Compile Sources, 관련 없는 미사용 지역 변수, Firebase Debug dSYM, AppIntents 메타데이터, Alamofire 의존성 스캔 경고가 있었다. 이번 삭제와 무관한 항목은 수정하지 않았다. +- 남은 항목: [blocked] 11개 변경 화면의 실제 탐색/레이아웃과 승인된 테스트 계정의 캔 구매·실패 QA. 전체 visual QA 또는 P1-GATE 통과를 주장하지 않는다. + +증거 파일 디렉터리: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/` + +| 증거 | 파일 | +|---|---| +| 운영 빌드 | `yandex-ios-standard-debug-build.log` | +| 개발 빌드 | `yandex-ios-dev-debug-build.log` | +| 개발 시뮬레이터 빌드 | `yandex-ios-simulator-debug-build.log` | +| 캔 요청 실행 | `yandex-ios-quota-check.log` | +| 보존 파일 기준선 | `yandex-ios-preserved.sha256` | +| 앱 시작 화면 | `yandex-ios-launch-smoke.png` | + +## Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 | +|---|---|---|---|---|---| +| 2026-10-13 | PLAN-001 | 확정 | 문서 작성만 수행하고 구현 승인 대기 | 사용자 직접 지시, PRD DEC-003 | 전체 | +| 2026-10-13 | PLAN-002 | 제안 | 기존 정적 검사·빌드·수동 QA로 삭제 검증, 신규 테스트 인프라 없음 | 빌드 가이드, 최소 변경 | P1-GATE | +| 2026-10-13 | PLAN-003 | 제안 | 비활성 Pods 생성물은 그대로 두고 활성 참조만 검사 | PRD DEC-004 | P1-T3, P1-GATE | +| 2026-10-13 | PLAN-004 | 확정 | 사용자 승인으로 구현 착수, PLAN-002~003 적용 | PRD DEC-005 | 전체 | +| 2026-10-13 | PLAN-005 | 확정 | 현재 디렉터리·기존 Progress를 사용, worktree/자동 커밋/추가 실행용 저장소 구조 없음 | 승인 계획을 스킬 기본 절차보다 우선 | 전체 | +| 2026-10-13 | PLAN-006 | 확정 | 코드 리뷰와 전체 수동 Gate를 분리, 미검증 UI·실구매는 차단으로 남김 | 실제 검증 범위와 독립 리뷰 판정 | P1-GATE | + +## 발견된 문제 + +| ID | 상태 | 관찰 | 처리 | +|---|---|---|---| +| YAN-STATE-001 | 잔여 참조 확인 | SDK·상수는 제거됐으나 앱 소비 코드가 남음, 빌드는 아직 미실행 | 승인 후 P1-T1~T3 | +| YAN-STATE-002 | 범위 제외 | 과거 Pods 리소스 스크립트·목록 10개에 참조 잔존, 활성 Copy Pods Resources 단계 없음 | 직접 편집하지 않음 | +| YAN-STATE-001-FOLLOWUP | 해결 | 잔여 참조 제거 및 운영·개발 Debug 빌드 성공 | P1-T1~T3 완료 | +| YAN-QA-001 | 외부 조건 대기 | 11개 화면 전체와 실서버 캔 구매 미검증 | P1-GATE [blocked], 테스트 계정·데이터 및 native 조작 환경 필요 | diff --git a/docs/20261013_Yandex_광고_잔여_참조_제거/prd.md b/docs/20261013_Yandex_광고_잔여_참조_제거/prd.md new file mode 100644 index 00000000..23bbec61 --- /dev/null +++ b/docs/20261013_Yandex_광고_잔여_참조_제거/prd.md @@ -0,0 +1,134 @@ +# Yandex 광고 잔여 참조 제거 PRD + +## 문서 정보 + +| 항목 | 내용 | +|---|---| +| 문서 상태 | 코드 구현 완료, 수동 QA 대기 | +| 작성일 / 최종 수정일 | 2026-10-13 | +| 대상 제품 | SodaLive iOS 운영·개발 타깃 | +| 작성자·결정권자 | 에이전트 작성 / 사용자 승인 | +| 관련 API Contract | 신규 작성 없음, 기존 캔 충전 계약 유지 | +| 관련 구현 계획 | [plan-task.md](plan-task.md) | +| 관련 review | [Phase 1 리뷰](reviews/phase1-yandex-reference-removal.md) | + +## 1. Overview + +사용자가 Yandex SDK 의존성과 광고 ID 상수를 제거한 상태에서, 앱에 남은 초기화·배너·보상형 광고 코드와 프로젝트 참조를 제거한다. 캔 충전과 광고 유입 측정 설정은 유지한다. 문서 작성 후 사용자 승인을 받아 코드 제거를 수행했으며, 전체 수동 QA와 구현 완료 판정은 구분한다. + +## 2. Problem Statement + +아래는 구현 전 확인한 문제 상태다. 구현 후 결과는 §14와 계획의 Progress에 기록한다. + +- `Podfile`, `Podfile.lock`, `Pods/Manifest.lock`에서 Yandex와 전용 하위 의존성은 제거됐고, 두 Constants 파일의 `YANDEX_*` 선언도 제거됐다. +- 앱에는 SDK import, 초기화, 삭제된 상수 참조 12곳, 배너 호출 11곳과 채팅 광고 사전 로딩이 남아 있다. +- 채팅 광고 표시 함수의 호출부는 없지만 잔여 채팅이 1회 이하일 때 사전 로딩하는 경로는 남아 있다. +- 이 상태는 제거 작업의 중간 상태다. 이번 문서 작성 단계에서 앱 빌드 실패를 실제 실행으로 확인한 것은 아니다. + +## 3. Goals + +### 3.1 제품 목표 + +- 앱 코드와 활성 프로젝트 설정에서 Yandex 의존 참조를 없앤다. +- 운영·개발 두 스킴의 빌드를 검증하고, 기존 캔 충전 동작을 보존한다. + +### 3.2 UX 목표 + +- 11개 화면의 광고와 광고 전용 여백을 제거하되 콘텐츠·스크롤·이동 동작은 유지한다. +- 광고를 다른 UI로 대체하거나 주변 디자인을 개편하지 않는다. + +## 4. Non-Goals + +- 두 Info.plist 전체를 수정하지 않는다. 특히 `SKAdNetworkItems`, `NSAdvertisingAttributionReportEndpoint`는 명시적인 보존 대상이다. +- ATT/IDFA, 추적 권한 문구, AppsFlyer·Meta·Firebase 및 기타 SDK를 변경하지 않는다. +- 사용자가 완료한 Pod 의존성·상수 제거를 되돌리거나 재수행하지 않는다. `pod install`, 패키지 업데이트, 캐시 삭제는 계획에 포함하지 않는다. +- `Pods/**`, `generated/**`, 빌드 산출물을 직접 정리하지 않는다. 미사용 Yandex 리소스 스크립트·목록 10개는 현재 활성 복사 단계에서 참조되지 않으므로 제외한다. +- 현재 `objectVersion = 77`, 기존 작업물, 과거 광고 문서, 서버의 광고 보상 API와 운영 콘솔은 변경하지 않는다. worktree·브랜치 생성 및 커밋도 수행하지 않는다. + +## 5. Target Users and Permissions + +기존 라이브·커뮤니티·시리즈·알림·채팅 이용자에게 적용한다. 로그인, 권한, 구매 자격과 서버의 오류 정책은 변경하지 않는다. + +## 6. 핵심 사용자 흐름 + +1. 사용자가 기존 화면에 진입하면 광고 없이 기존 콘텐츠를 탐색한다. +2. 채팅 쿼터가 부족하면 기존 10캔·20캔 구매 버튼을 사용한다. +3. 기존 구매 응답에 따라 쿼터와 캔 잔액을 갱신한다. 광고 사전 로딩·표시·광고 보상 구매 경로는 실행하지 않는다. + +## 7. 정보 구조와 라우팅 + +신규 화면이나 라우팅 변경은 없다. 기존 View 파일에서 광고 소비 코드만 제거한다. 정확한 대상 파일은 계획의 `P1-T1`~`P1-T3`에 기록한다. + +## 8. 기능 요구사항 + +| ID | 상태 | 요구사항 | 수용 기준 | Goal 연결 | +|---|---|---|---|---| +| YAN-001 | 확정 | 배너 호출 11곳 제거 | 각 호출과 광고에만 붙은 modifier·빈 조건문을 제거하고 다른 콘텐츠는 유지 | P1-T1 | +| YAN-002 | 확정 | 보상형 광고 소비 코드 제거 | `showRewardedAdForChatQuota`, `prepareRewardedAdIfNeeded` 및 호출부 제거, 쿼터 갱신 유지 | P1-T2 | +| YAN-003 | 확정 | 광고 전용 선언 제거 | `ChatRoomQuotaChargeType.ad`, `quotaAdAction`, `rewardedAdLoadFailed`만 제거 | P1-T2 | +| YAN-004 | 확정 | SDK 연결과 공용 광고 코드 제거 | AppDelegate의 Yandex import·초기화·전용 주석 제거, 공용 파일 삭제, 두 타깃 프로젝트 참조 제거 | P1-T3 | +| YAN-005 | 확정 | 기존 충전·설정 보존 | 캔 구매 요청·UI와 두 plist, 마케팅 코드, Project Format 불변 | P1-T2, P1-GATE | + +요구사항의 확정은 대화에서 합의한 범위를 뜻하며, 구현 착수 승인을 뜻하지 않는다. 문서 승인 전 코드 변경은 금지한다. + +## 9. 반응형 기능 범위 + +기존 iOS 화면 지원 범위를 유지한다. 신규 크기 대응은 추가하지 않고 기존 지원 기기에서 광고 삭제 후 레이아웃을 확인한다. + +## 10. UI/UX Expectations + +- `LiveView`, `CharacterView`의 광고 전용 음수 padding과 라이브 상세·알림 설정의 광고 전용 frame/padding은 광고와 함께 제거한다. +- `SeriesMainByGenreView`의 광고만 감싼 조건문은 남기지 않는다. 다른 콘텐츠를 감싼 컨테이너나 공용 간격은 임의 변경하지 않는다. +- `ChatQuotaNoticeItemView`의 10캔/15채팅, 20캔/40채팅 버튼과 `quotaChatCount`를 유지한다. 화면 접근성·오류 안내·로딩 동작의 추가 변경은 없다. + +## 11. API 계약 + +`TalkApi.purchaseChatQuota`의 `POST /api/chat/rooms/{roomId}/quota/purchase`와 Repository는 유지한다. 기존 `ChatQuotaPurchaseRequest`의 `container = "ios"`, `chargeType = "CAN"`, `canOption = "CAN_10" | "CAN_20"` 직렬화와 optional 선언을 변경하지 않는다. 광고 전용 enum case와 그 호출 경로만 제거하며, DTO나 구매 함수를 재설계하지 않는다. + +신규 API 계약, 서버 변경, 별도 `api-contract.md`는 필요하지 않다. + +## 12. 보안과 데이터 취급 + +인증·결제·잔액 저장 로직은 변경하지 않는다. 문서·검증 로그에 토큰이나 SDK 키를 복사하지 않는다. 수동 충전 QA에는 승인된 테스트 계정과 재화를 사용하며, 실제 운영 계정에서 비용을 발생시키는 검증은 별도 동의 없이 수행하지 않는다. + +## 13. 성능과 품질 요구사항 + +- 저장소에 확인된 테스트 번들 타깃이 없으므로 이번 삭제만을 위한 테스트 인프라는 만들지 않는다. 정적 부재 검사, 보존 파일 대조, 두 스킴 빌드와 수동 QA를 사용한다. +- `rg` 결과는 종료 코드 1만 부재 성공으로 해석한다. 0은 잔존, 2는 검사 오류다. +- 미실행 빌드·수동 QA는 통과로 기록하지 않는다. 계정·기기 등 환경이 없으면 차단 사유와 미검증 범위를 남긴다. + +## 14. 성공 기준 + +- [x] 앱 소스·활성 프로젝트·의존성 명세에 Yandex 참조가 없고 광고 전용 메서드·문구·enum case가 없다. +- [x] 두 plist와 기존 캔 구매 계약, 마케팅 흐름, 사용자 선행 변경이 보존된다. +- [x] `SodaLive`, `SodaLive-dev` Debug 빌드가 성공한다. +- [ ] 11개 화면의 콘텐츠·스크롤·이동과 광고 전용 빈 공간 제거, 두 캔 구매 경로를 확인한다. +- [x] 실제 검증 결과 및 구현 후 리뷰를 계획 문서에 연결한다. 미검증 항목은 명시한다. + +2026-10-13: 코드 리뷰 APPROVE, 두 기기용 Debug 빌드와 개발용 시뮬레이터 Debug 빌드 성공. 실제 DTO 직렬화 3개 시나리오 통과, 보존 파일 13개의 SHA-256 일치. 전용 시뮬레이터에서 홈 화면과 ATT 팝업 표시를 확인했지만, 11개 수정 화면 전체와 실서버 캔 구매는 검증하지 않았다. 이 수동 QA 항목은 [blocked]로 남긴다. + +## 15. Open Questions + +추가 인터뷰가 필요한 요구사항 미결정은 없다. 2026-10-13 사용자가 문서를 승인하고 문서대로 진행하도록 요청했다. 테스트 계정·기기 접근 가능 여부는 구현 후 수동 검증의 환경 조건으로 기록한다. + +## 16. 요구사항 추적표 + +| 요구사항 | API Contract | Phase / Goal | 자동 검증 | 수동 검증 | +|---|---|---|---|---| +| YAN-001 | 해당 없음 | 1 / P1-T1, P1-GATE | 배너 호출 부재·두 빌드 | 11개 화면 레이아웃·탐색 | +| YAN-002~003 | 기존 계약 유지, §11 | 1 / P1-T2, P1-GATE | 광고 경로 부재·구매 코드 대조 | 두 캔 옵션 성공·기존 실패 처리 | +| YAN-004~005 | 기존 계약 유지, §11 | 1 / P1-T3, P1-GATE | SDK·프로젝트 참조 부재·보존 파일 대조·두 빌드 | 앱 시작·충전·마케팅 변경 없음 | + +## 17. Decision Log + +| 날짜 | ID | 상태 | 결정 | 근거 | 영향 | +|---|---|---|---|---|---| +| 2026-10-13 | DEC-001 | 확정 | SDK·상수 제거는 사용자 선행 작업으로 취급 | 사용자 보고와 현재 파일 확인 | 전체 범위 | +| 2026-10-13 | DEC-002 | 확정 | 두 SKAdNetwork 관련 키를 제거 대상에서 제외 | 사용자 직접 지시 | YAN-005 | +| 2026-10-13 | DEC-003 | 확정 | 문서만 작성 후 승인 대기, worktree 미사용 | 사용자 직접 지시 | 전체 실행 | +| 2026-10-13 | DEC-004 | 제안 | 두 plist 전체 및 미사용 Pods 생성물은 수정하지 않음 | 최소 변경·생성물 직접 수정 금지, 문서 승인 시 적용 | YAN-005 | +| 2026-10-13 | DEC-005 | 확정 | 문서 승인에 따라 구현 착수, DEC-004 보존 방안 승인 | 사용자 “승인”, “문서대로 진행한다” | 전체 실행 | + +## 18. 변경 관리 + +범위 변경이나 추가 미결정이 생기면 이 문서의 Decision Log와 요구사항을 먼저 보강하고 `plan-task.md`에 반영한다. 기존 완료 기록은 덮어쓰지 않는다. 이전 배치·부분 제거 문서는 완료 이력으로 남기고, 이번 전체 잔여 참조 제거는 이 문서 쌍에서 이어서 관리한다. diff --git a/docs/20261013_Yandex_광고_잔여_참조_제거/reviews/phase1-yandex-reference-removal.md b/docs/20261013_Yandex_광고_잔여_참조_제거/reviews/phase1-yandex-reference-removal.md new file mode 100644 index 00000000..6fbd3531 --- /dev/null +++ b/docs/20261013_Yandex_광고_잔여_참조_제거/reviews/phase1-yandex-reference-removal.md @@ -0,0 +1,116 @@ +# Phase 1 Yandex 잔여 참조 제거 리뷰 + +## 1. 리뷰 정보 + +| 항목 | 내용 | +|---|---| +| 리뷰 대상 | Phase 1 / P1-T1~T3 및 P1-GATE 증거 | +| 기준 working tree | 사용자 선행 SDK·상수·프로젝트 형식 변경 위에 잔여 참조 삭제 적용, 커밋 없음 | +| 리뷰 일자 | 2026-10-13 | +| 리뷰어 | Oracle 독립 읽기 전용 검토, 주 에이전트 실행 증거 대조 | +| 기준 문서 | [PRD](../prd.md), [계획](../plan-task.md) | +| 리뷰 상태 | 코드 판정 완료, 전체 수동 QA 외부 조건 대기 | + +## 2. 리뷰 목적과 범위 + +### 목적 + +승인 범위의 Yandex 참조가 제거되고, 광고와 무관한 캔 구매·마케팅·프로젝트 설정이 보존되는지 확인한다. 코드 승인과 실제 화면·구매 QA 완료는 구분한다. + +### 포함 범위 + +- 계획에 열거한 배너 View 11개, ChatRoomViewModel, ChatQuotaPurchaseRequest, I18n, AppDelegate, 삭제된 YandexAdSupport 및 프로젝트 전용 등록 6줄. +- 캔 구매 UI → ViewModel → Repository → TalkApi 연결과 요청 직렬화. +- 보존 파일 13개 체크섬, 정적 검사, parser·빌드·실제 DTO 실행, 앱 시작 smoke 기록. + +### 제외 범위 + +사용자 선행 Pod/Constants/Project Format 변경 자체의 재설계, 비활성 Pods 생성물 정리, 다른 SDK·인증·결제 구조 개선. 실행하지 않은 화면·거래를 검증 통과로 추정하지 않는다. + +## 3. 판정 기준 + +`YAN-001~005` 및 PRD §14 성공 기준을 사용한다. 삭제 누락·캔 계약 변경·보존 설정 수정은 코드 차단 사유다. 환경 때문에 실행하지 못한 수동 검증은 결함으로 단정하지 않고 별도 미완료 Gate로 남긴다. + +## 4. 검토한 근거 + +### 문서와 코드 + +- PRD YAN-001~005, 계획 P1-T1~T3/P1-GATE. +- `SodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swift`: 광고 함수 삭제 후 `updateQuota`의 잔액 저장·표시 조건 및 기존 캔 구매 성공/실패 흐름 유지. +- `SodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaPurchaseRequest.swift`: `.ad`만 제거, 기존 CAN payload와 두 옵션 수치 유지. +- 배너 View 11개의 diff·주변 코드: 광고 전용 modifier와 장르 광고 전용 조건문만 제거, 콘텐츠·공용 간격 유지. +- `SodaLive/Sources/App/AppDelegate.swift`, `SodaLive.xcodeproj/project.pbxproj`: Yandex 연결만 삭제, 다른 SDK 및 사용자 선행 변경 유지. +- Oracle 세션 `ses_ecc05fbd9ffe65w6Gb41Ii1HYZ` / 작업 `bg_16ac91d2`: 코드 APPROVE, 수정 차단 사항 없음. 실제 수동 QA와 기록 연결을 전체 Gate의 잔여 조건으로 제시했다. + +### 실행 환경 + +macOS, Xcode 26.0, CocoaPods 1.16.2. XCTest 타깃·Testables 및 세션의 LSP 진단 도구 없음. 기기용 두 Debug 빌드와 개발 시뮬레이터 빌드를 수행했다. Native smoke는 이번에 생성한 iPhone 17 Pro / iOS 26.0 전용 시뮬레이터에서 수행했다. + +### 실행한 검증 + +| 명령 또는 검증 | 결과 | 핵심 증거 | +|---|---|---| +| 계획의 배너·광고 함수·enum·SDK 부재 `rg` | 성공 | 변경 전 일치(0), 변경 후 부재(1); Oracle도 재확인 | +| `xcrun swiftc -frontend -parse` 수정 Swift 15개 | 성공 | 종료 코드 0, 전체 Xcode 컴파일로 보완 | +| `plutil -lint` 프로젝트·두 plist | 성공 | 모두 OK | +| `ruby -rxcodeproj` 읽기 검사 | 성공 | 두 타깃에 Yandex 참조 없음, objectVersion 77, Oracle도 누락 소스 확인 | +| `shasum -a 256 -c .../yandex-ios-preserved.sha256` | 성공 | 13개 파일 모두 OK, Oracle도 기준선 대조 | +| `xcodebuild ... -scheme SodaLive -configuration Debug build` | 성공 | BUILD SUCCEEDED, 종료 코드 0 | +| `xcodebuild ... -scheme SodaLive-dev -configuration Debug build` | 성공 | BUILD SUCCEEDED, 종료 코드 0 | +| 개발 스킴 simulator build | 성공 | BUILD SUCCEEDED, 종료 코드 0 | +| 실제 DTO와 임시 Swift 드라이버 실행 | 성공 | CAN_10, CAN_20, nil canOption 및 옵션 수치 3/3 통과 | +| 두 기기용 앱 루트·Frameworks와 `otool -L` | 성공 | Yandex/AppMetrica/DivKit/VGSL 및 MobileAdsBundle 잔존 없음 | +| `git diff --check` | 성공 | 종료 코드 0 | +| `simctl install`, `launch`, `io screenshot` | 시작 smoke 성공 | PID 61239, 홈 콘텐츠·ATT 팝업 직접 확인 | +| 11개 변경 화면 전체·캔 구매 실제 거래 | 미실행 | native 화면 탐색 자동화·승인 테스트 계정 및 데이터 없음 | +| XCTest 전체 suite / LSP | 미실행 | 실행 가능한 Testables / 세션 도구 미제공, 통과로 표기하지 않음 | +| 전용 simulator terminate/shutdown/delete, 임시 driver·binary 삭제 | 성공 | 종료 코드 0, 기기·파일 부재 재확인 | + +전체 명령과 로그 경로는 [계획의 Gate 검증 결과](../plan-task.md)에 기록했다. 증거는 `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/`의 `yandex-ios-*.log`, `yandex-ios-preserved.sha256`, `yandex-ios-launch-smoke.png`에 있다. + +## 5. 발견 사항 요약 + +**확정 코드 결함 없음.** 독립 코드 판정은 APPROVE다. 다음은 코드 수정 요구가 아닌 미완료 검증 항목이다. + +| ID | 심각도 | 상태 | 제목 | 소유 Task | 후속 | +|---|---|---|---|---|---| +| QA-P1-001 | Medium | 보류 | 11개 변경 화면의 전체 native QA 미실행 | P1-GATE | 수동 환경 준비 후 검증 | +| QA-P1-002 | Medium | 보류 | 캔 구매 성공·실패의 실서버 회귀 미실행 | P1-GATE | 승인된 테스트 계정·재화 준비 후 검증 | + +## 6. 발견 사항 상세 + +### QA-P1-001: 전체 화면 검증 공백 + +- 관련 요구사항: YAN-001, PRD §14. +- 관찰: 11개 호출과 광고 전용 레이아웃 삭제는 코드·빌드로 검증했지만, 실제 화면은 홈 시작과 ATT 팝업까지만 확인했다. 해당 홈은 11개 변경 화면의 검증을 대체하지 않는다. +- 재개 조건: native 조작 가능한 환경에서 11개 화면의 데이터 유무·스크롤·이동·빈 공간을 확인하고 화면별 증거를 기록한다. 담당은 사용자 또는 해당 QA 환경을 제공받은 에이전트다. +- 판정: 수동 visual QA 미완료. 캡처가 없는 화면에 대한 독립 시각 리뷰·픽셀 비교 통과를 주장하지 않는다. + +### QA-P1-002: 실제 캔 구매 검증 공백 + +- 관련 요구사항: YAN-005, PRD §11·§14. +- 관찰: 실제 요청 DTO 직렬화는 변경 전후 통과하고 구매 코드 경로는 그대로지만, 서버 거래와 잔액/쿼터 결과를 직접 실행하지 않았다. +- 재개 조건: 승인된 테스트 계정·재화로 두 옵션의 성공 결과, 잔액 부족·실패 상태를 확인한다. 실운영 비용이 발생하는 검증은 별도 승인이 필요하다. +- 판정: 안전 경계를 유지하며 거래를 임의 실행하지 않았다. 정적·DTO 검증만으로 실제 구매 성공을 추정하지 않는다. + +## 7. 확정 항목의 plan·goal 전환 + +코드 회귀 수정 Task로 전환할 항목 없음. QA-P1-001~002는 기존 P1-GATE의 미완료 체크와 `YAN-QA-001`로 추적한다. 실제 QA에서 결함이 확정되면 코드 수정 전에 신규 회귀 Task를 추가한다. + +## 8. 리뷰 종료 판정 + +| 판정 항목 | 결과 | 근거 | +|---|---|---| +| 코드 리뷰 범위 확인 | 충족 | 독립 Oracle APPROVE, 차단 코드 결함 없음 | +| 보존 범위 확인 | 충족 | 13개 SHA-256 일치, 프로젝트 참조 대조 | +| 자동 검증·빌드 | 충족 | 두 기기용 스킴 + 개발 시뮬레이터 성공 | +| 실제 화면·거래 전체 QA | 미충족 | QA-P1-001~002 보류 | +| 미완료 조건·재개 조건 기록 | 충족 | P1-GATE [blocked]와 본 문서 | + +**최종 결론:** 코드 승인, 전체 Gate는 외부 QA 조건 대기. + +**남은 항목:** 11개 화면 native QA 및 승인된 테스트 계정의 캔 구매 회귀. 기존 중복 소스·Firebase dSYM·AppIntents·관련 없는 미사용 변수 경고는 별도 범위로 유지한다. + +## 9. 수정 후 검증 기록 + +리뷰로 인한 코드 수정 없음. 향후 수동 QA 또는 확정 회귀 수정 결과를 이 절에 누적한다.