docs(ads): Yandex 광고 제거 요구사항과 검증을 기록한다

This commit is contained in:
Yu Sung
2026-10-13 16:01:15 +09:00
parent 9478565a04
commit 8f51b2a57a
3 changed files with 531 additions and 0 deletions
@@ -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 조작 환경 필요 |
@@ -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`에 반영한다. 기존 완료 기록은 덮어쓰지 않는다. 이전 배치·부분 제거 문서는 완료 이력으로 남기고, 이번 전체 잔여 참조 제거는 이 문서 쌍에서 이어서 관리한다.
@@ -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 또는 확정 회귀 수정 결과를 이 절에 누적한다.