docs(event): SDK 조사와 딥링크 리뷰를 보존한다
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# 이벤트 상세 OneLink SDK 검증 게이트
|
||||
|
||||
## 1. 검토 정보
|
||||
- 일자: 2026-10-29. 대상: P1-T1/P1-GATE, 제품 구현 전 feasibility 확인.
|
||||
- 기준: [PRD](../prd.md), [계약](../api-contract.md), [계획](../plan-task.md).
|
||||
- 검토자: 독립 SDK 검증 에이전트 및 Sisyphus 근거 확인.
|
||||
- 판정: **BLOCKED**. 제품 기능 미완료이며 불가능하다고 단정한 결과는 아니다.
|
||||
|
||||
## 2. 목적과 범위
|
||||
앱 내부에 이미 보유한 short/long HTTPS OneLink를 기존 UDL과 공존하는 SDK 경로로 해석할 수 있는지 확인한다. 일반 푸시의 공식 SDK 경로와 URI parser는 별개이며, 이 결과만으로 앱 코드 구현을 시작하지 않는다.
|
||||
|
||||
## 3. 판정 기준
|
||||
공개 지원 계약 또는 안전하게 검증된 실제 SDK 동작으로 URL 입력, callback, `deep_link_value`/`deep_link_sub5` 반환을 입증해야 한다. query-only 파싱, 가짜 push attribution, SDK와 무관한 callback 모형은 증거가 아니다.
|
||||
|
||||
## 4. 확인한 근거
|
||||
- 설치 xcframework의 iOS artifact `CFBundleVersion`: **6.17.5**. `CFBundleShortVersionString=1.0`은 SDK 버전이 아니다.
|
||||
- `AppsFlyerLib.h:623-629`: `performOnAppAttributionWithURL:`은 `onAppOpenAttribution`을 강제 호출하며 OneLink/UniversalLink/Deeplink를 해석한다. re-engagement/session/launch는 집계하지 않는다고 명시한다.
|
||||
- 같은 헤더: UDL callback `didResolveDeepLink`(167행), `deepLinkDelegate`(331행), `continueUserActivity`(578-584행), `handlePushNotification`(587-595행), `addPushNotificationDeepLinkPath`(747-752행).
|
||||
- 바이너리 SHA-256: `1d9e5d9b5b30e0436e602a0e5f8be8b49cadf21ef39b7a7f9ea45a4689aab5f7`.
|
||||
- 헤더 SHA-256: `83dff1f888b5d75f2cdef81fcc2770a968e2c51633c652d6a91b50d9afc76eb6`.
|
||||
|
||||
공식 근거:
|
||||
- [SDK URL resolver](https://dev.appsflyer.com/hc/docs/ios-sdk-reference-appsflyerlib#performonappattribution)
|
||||
- [앱 실행과 무관한 수동 attribution callback](https://dev.appsflyer.com/hc/docs/ios-sdk-reference-appsflyerlibdelegate#performonappattribution)
|
||||
- [Legacy short/long 링크 흐름](https://dev.appsflyer.com/hc/docs/dl_ios_gcd_legacy)
|
||||
- [UDL 콜백과 전달 계약](https://dev.appsflyer.com/hc/docs/dl_ios_unified_deep_linking)
|
||||
|
||||
## 5. 실행한 확인과 발견 사항
|
||||
공개 계약 조사에서 URL-only legacy resolver는 확인했다. 그러나 `deepLinkDelegate`를 함께 설정한 현재 구성에서 수동 resolver가 어떤 callback을 보장하는지는 확인되지 않았다. 일반적인 legacy/UDL 콜백 배타성만으로 수동 API의 실제 동작을 단정하지 않는다.
|
||||
|
||||
두 번째 접근으로 `/usr/bin/sandbox-exec -p '(version 1) (allow default) (deny network*)' /usr/bin/true`를 실행해 exit 0을 확인했다. 이는 실제 iOS SDK 프로세스 및 위임된 네트워크 작업의 fail-closed 격리 증거가 아니므로 SDK 실행은 하지 않았다. SDK/앱 자격증명 로드, 시뮬레이터 생성, OneLink 해석 요청은 없다.
|
||||
|
||||
## 6. 재개 조건
|
||||
AppsFlyer에 다음 지원 계약을 확인하거나, 별도 승인된 제한적 실제 SDK 검증으로 공존 동작을 확인한다:
|
||||
> iOS SDK 6.17.5에서 deepLinkDelegate가 설정된 상태로 performOnAppAttribution(with:)를 호출하면 어떤 callback이 보장되는가? short/long HTTPS OneLink의 deep_link_value/deep_link_sub5 반환 및 re-engagement/session/launch 미집계 계약이 유지되는가?
|
||||
|
||||
## 7. 계획 전환 및 종료
|
||||
Phase 2/3은 차단 유지. 지원되지 않은 메서드, 합성 activity, SDK 교체/업그레이드를 추정 적용하지 않는다. 실제 SDK callback과 전달은 미검증이며, 후속 허용 범위가 결정되기 전 제품 코드 작업을 중단한다.
|
||||
@@ -0,0 +1,50 @@
|
||||
# AppsFlyer 실제 SDK 콜백 검증
|
||||
|
||||
## 1. 검토 정보
|
||||
- 일자: 2026-10-29. 대상: 제한적으로 승인된 P1-T2, 제품 구현 전 SDK 검증.
|
||||
- 승인 범위: AppsFlyer 테스트 OneLink 해석만. 이벤트 API·앱 서버·푸시 발송·운영 앱 실행은 제외.
|
||||
- 실제 SDK: 설치된 iOS Simulator artifact **6.17.5 (build 2)**, x86_64, iOS 17.5의 작업 소유 임시 시뮬레이터.
|
||||
- 현재 결과: 긴 링크의 수동 API와 UDL 공존 동작 확인. 단축 링크의 정상 해석은 아직 입증하지 못함.
|
||||
|
||||
## 2. 실행 방식
|
||||
UIKit과 AppsFlyer만 연결한 별도 임시 앱에서 `AppsFlyerLibDelegate`와 `DeepLinkDelegate`를 설정한 뒤 `performOnAppAttribution(with:)`를 호출했다. 실제 SDK callback만 JSONL로 기록했다. SodaLive 앱 소스나 이벤트 API를 실행하지 않았으며 `start`, `logEvent`, 푸시 관련 API는 호출하지 않았다.
|
||||
|
||||
입력:
|
||||
- 긴 링크: `https://voiceon.onelink.me/RkTm?pid=sdk-validation&deep_link_value=event-detail&deep_link_sub5=123`
|
||||
- 단축 샘플: `https://onelink-basic-app.onelink.me/H5hv/6d66214a`, AppsFlyer [공식 legacy 문서](https://dev.appsflyer.com/hc/docs/dl_ios_gcd_legacy)의 공개 예시. 현재 유효성을 별도로 보장하는 링크는 아니다.
|
||||
|
||||
실행은 매번 새 앱 프로세스로 분리했다. 설정 없는 실행과 합성 SDK 설정 실행을 구분하고, UDL delegate 없는 legacy-only 대조군도 확인했다. 실제 앱 자격증명은 로드하지 않았다.
|
||||
|
||||
## 3. 실제 관찰
|
||||
| 구성 | 입력 | 실제 manual 호출 이후 callback | 결과 |
|
||||
|---|---|---|---|
|
||||
| UDL + legacy delegate | 긴 링크 | `didResolveDeepLink` | found, event-detail, ID 123 |
|
||||
| UDL + legacy delegate + 합성 설정 | 긴 링크 | `didResolveDeepLink` | found, event-detail, ID 123 |
|
||||
| legacy delegate만 | 긴 링크 | `onAppOpenAttribution` | success, event-detail, ID 123 |
|
||||
| UDL + legacy delegate | 단축 샘플 | `didResolveDeepLink` | failure, NSError domain com.appsflyer.sdk.deeplink, code 0 |
|
||||
| UDL + legacy delegate + 합성 설정 | 단축 샘플 | `didResolveDeepLink` | 같은 failure, 이벤트 값 없음 |
|
||||
| legacy delegate만 | 단축 샘플 | `onAppOpenAttributionFailure` | 같은 오류 domain/code |
|
||||
|
||||
SDK 헤더의 enum은 notFound=0, found=1, failure=2다. 긴 링크의 실제 기록:
|
||||
```json
|
||||
{"callback":"delegate-check","status":"legacySame-true-udlSame-true-udlPresent-true"}
|
||||
{"callback":"performOnAppAttribution","status":"invoking"}
|
||||
{"callback":"didResolveDeepLink","status":"AFSDKDeepLinkResultStatus(rawValue: 1)","eventvalue":"event-detail","ID":"123","errorclass":"none"}
|
||||
```
|
||||
위 JSON은 동일 실행의 허용된 필드만 발췌한 기록이다. 원래 callback을 테스트 코드가 직접 호출한 것이 아니라 실제 SDK가 delegate에 전달했다.
|
||||
|
||||
## 4. 혼동 방지
|
||||
- 첫 harness는 UDL delegate setter 직후 동기 identity assertion이 실패해 URL 해석 호출 전에 종료됐다. 이는 SDK URL 실패 사례로 집계하지 않는다. 이후 실제 호출 직전 양 delegate 등록을 관찰하여 검증했다. 테스트의 대기 시간은 제품 코드에 적용할 요구사항이 아니다.
|
||||
- UDL 설정 직후 manual 호출 이전의 notFound/failure callback도 관찰됐다. 이 callback은 manual 요청 결과와 구분했다. manual 긴 링크 결과는 그 뒤의 found + event-detail/123이다.
|
||||
- 긴 링크 파라미터는 SDK가 URL 자체에서 추출할 수 있으므로, 이 성공을 AppsFlyer 서버의 링크 존재/설정 검증 성공이라고 표현하지 않는다.
|
||||
- 단축 샘플의 실패만으로 SDK가 단축 링크를 지원하지 않는다고 결론내리지 않는다. 정상 단축 fixture의 성공 결과는 확보되지 않았다.
|
||||
|
||||
## 5. 코드·증거 확인
|
||||
상위 실행자는 임시 Probe.swift, build/run 스크립트, 실제 JSONL 결과 및 SDK의 AppsFlyerDeepLinkResult.h enum을 직접 읽었다. 제품 소스 변경이나 신규 앱 기능 완료를 의미하지 않는다. 리소스 정리 및 최종 gate 판정은 후속 기록한다.
|
||||
|
||||
## 6. 최종 판정과 정리
|
||||
- 콜백 공존과 긴 URL 파라미터 전달: 확인. 정상 short 해석: 미입증. 전체 P1-GATE는 유지하며 유효한 이벤트용 단축 OneLink가 다음 검증 입력이다.
|
||||
- SDK simulator 바이너리 SHA-256: `4c940fc1670de4e05bc55d8e0436130c57ff4878ddd1b6778029a5d99d69205c`. 이전 검토의 디바이스 slice와는 다른 바이너리이며 버전은 동일하다.
|
||||
- 소유 bundle `local.sdkprobe.eventlink.20261029.a` 제거, 소유 iOS17.5 simulator shutdown/delete 모두 exit 0. 상위 실행자도 UUID `0E44E774-8669-4513-A12D-C8FA4F331A03`의 simulator inventory 부재와 잔여 앱 프로세스 부재를 직접 확인했다. 기존 사용자 기기는 변경하지 않았다.
|
||||
- SDK 실제 callback 로그 7개에 대한 사후 검증은 모든 케이스에서 관찰된 callback 선택과 필드 유무가 일치했다. 이는 short 성공 7건이라는 뜻이 아니라 성공/실패를 포함한 실제 로그의 정합성 검사다.
|
||||
- 테스트 앱·시뮬레이터 제거 확인 후 일회성 harness·binary·cache·raw 로그가 있던 소유 임시 root를 제거했고 부재를 확인했다. 실제 관찰 결과와 제한은 이 문서와 계획에 보존했다.
|
||||
@@ -0,0 +1,52 @@
|
||||
# 이벤트 상세 딥링크 연결 리뷰
|
||||
|
||||
## 1. 리뷰 정보
|
||||
- 대상: Phase 2 / P2-T1, P2-T2의 현재 작업 트리 변경.
|
||||
- 기준: [PRD](../prd.md), [계약](../api-contract.md), [계획](../plan-task.md).
|
||||
- 일자: 2026-10-29. 리뷰어: Oracle 단일 읽기 전용 리뷰.
|
||||
- 최초 판정: REQUEST_CHANGES. 제품 source 3개와 테스트/runner 4개, 연결된 MainView 호출을 검토했다. 기존 외부 project.pbxproj 재정렬은 제외했다.
|
||||
|
||||
## 2. 목적과 판정 기준
|
||||
event-detail URI/OneLink/push/알림 목록이 유효한 ID로 기존 화면을 한 번 열고, 잘못된 값·중복·취소·비동기 경합이 다른 목적지 또는 이전 pending을 훼손하지 않아야 한다. 실제 정상 short URL 성공은 사용자 지시로 비차단 후속 항목이다.
|
||||
|
||||
## 3. 검토 근거와 실행 증거
|
||||
- `AppDeepLinkHandler.swift`의 origin/ID 검증, SDK 위임, UDL 입력, event in-flight guard와 live 확인 흐름.
|
||||
- `AppDelegate.swift`의 SDK 시작 전 push path 등록, Firebase/Notifly hook 이후 단일 owner 처리, 기존 GCD/마케팅 흐름 보존.
|
||||
- `AppState.setAppStep`은 main queue에서 비동기로 route를 적용한다. `requestExternalNavigation`의 모든 production 호출자(MainView, 신규 handler)와 교체 취소 closure를 확인했다.
|
||||
- parent 로컬 검사: 이벤트 325 assertion, 기존 deep-link 279 assertion, 전체 검사 15개 PASS. 전체 실행 timeout 후 잔여 2개를 재실행했다.
|
||||
- parent 양 스킴 Debug Simulator 빌드 exit 0. Oracle는 해당 제공 증거를 사용했고 빌드나 테스트를 재실행하지 않았다.
|
||||
|
||||
## 4. 발견 사항
|
||||
| ID | 심각도 | 상태 | 내용 | 소유 Task |
|
||||
|---|---|---|---|---|
|
||||
| REV-P2-001 | High | 수정 검증 완료 | 현재 이벤트 판정과 실제 route 적용 사이의 비동기 경합 | P2-R1 |
|
||||
|
||||
### REV-P2-001
|
||||
`AppDeepLinkHandler.swift:133-153`의 current/guard 검사는 `AppState.swift:113-139`의 비동기 stack 변경보다 먼저 실행된다. 예약된 이전 이동은 실행 시점에 최신 event intent를 다시 확인하지 않는다.
|
||||
|
||||
재현 순서(Oracle의 FIFO 소스 추적; 수정 전 실제 RED 검사를 수행할 것): 현재 이벤트 45, live OFF에서 main queue를 비운 뒤 `44 → 45`를 중간 settle 없이 전달하면 최종 화면이 44가 된다. `44 → 45 → 44`는 44를 두 번 append할 수 있다. 최신 의도와 현재 목적지 비교를 실제 mutation과 같은 main queue 블록에서 수행해야 한다.
|
||||
|
||||
## 5. 계획 전환
|
||||
P2-R1에서 두 warm 혼합-ID 사례의 실제 RED를 확인하고 최소 수정 후 집중 회귀·기존 딥링크·양 스킴 빌드를 다시 확인한다. 기존 기록/테스트를 삭제하거나 약화하지 않는다.
|
||||
|
||||
## 6. 검증 한계
|
||||
실제 short 서버 해석, 실제 push/Notifly 전달, 이벤트 API 및 앱 화면 실행은 검증하지 않았다. 로컬 검사는 실제 production handler/AppState를 실행하고 SDK 경계만 대역으로 대체한다.
|
||||
|
||||
## 7. 후속 판정
|
||||
수정 및 delta 재검토 결과를 누적한다.
|
||||
|
||||
### P2-R1 구현자 재현 - 2026-10-29
|
||||
- 수정 전 production handler/AppState 검사에서 `mixed-current`, `mixed-repeat`가 각각 Oracle의 `44 → 45`, `44 → 45 → 44` assertion으로 exit 1을 반환했다. 입력 사이 main queue를 비우지 않았다.
|
||||
- 같은 Task의 live 하위 경계도 재현했다. current event 뒤에 player가 있는 경우 `current-live` 검사에서 `current event behind live still requests leave confirmation` assertion이 실패했다. Handler의 early current return이 기존 live 종료 확인을 건너뛰므로 current 검사를 실제 guarded mutation으로 이동하는 수정에 포함한다.
|
||||
- 최초 REQUEST_CHANGES 판정은 유지한다. 구현 후 집중 GREEN은 별도 기록하고, 양 타깃 재빌드 및 동일 Oracle의 delta 판정은 parent가 수행한다.
|
||||
|
||||
### P2-R1 구현자 수정 및 로컬 증거 - 2026-10-29
|
||||
- `AppState.setAppStep(step:ifCurrentStep:completion:)`의 기존 main async 블록에 optional predicate를 추가했다. predicate와 실제 stack mutation 사이에 추가 enqueue/await가 없으며 skipped 요청도 defer completion을 실행한다. Handler는 그 predicate 안에서 최신 event ID와 current event를 비교하고, current early return을 제거해 live 확인을 먼저 거친다.
|
||||
- `bash work/tests/check-event-deep-link.sh mixed-current mixed-repeat current-live` → assertions=21 PASS, 전체 집중 검사 → assertions=345 PASS, 기존 gift/community → assertions=279 PASS. 기존 EventDetailCheck 전체와 변경 Swift 4개 parse도 exit 0이다. 세 RED의 명령/오류 및 GREEN 세부는 P2-R1 계획에 누적했다.
|
||||
- 구현자 로컬 수정 상태이며 Oracle 승인으로 간주하지 않는다. parent 양 스킴 재빌드 및 같은 reviewer의 delta 판정은 미실행이다. 실제 SDK/서버/푸시 검증 범위는 확대하지 않았다.
|
||||
|
||||
### 최종 delta 판정 및 parent 검증 - 2026-10-29
|
||||
- Oracle: **REV-P2-001 ADDRESSED / APPROVE**. predicate와 mutation 사이에 추가 enqueue가 없어 최신 intent/현재 목적지를 원자적으로 검사한다. skipped completion과 current-event 뒤의 live 확인/취소도 보존되며 기본값을 사용하는 기존 setAppStep caller는 종전 동작을 유지한다. 새 차단 결함 없음.
|
||||
- parent 전체 15개 회귀 script를 timeout 없이 완료: FAILED_SCRIPTS=0, exit 0. 이벤트 345 및 기존 deep-link 279 assertion PASS.
|
||||
- parent 수정 후 SodaLive와 SodaLive-dev의 Debug generic iOS Simulator 빌드 모두 BUILD SUCCEEDED, exit 0. 기존 build 경고는 이번 범위 밖으로 유지했다.
|
||||
- 최종 결론: 클라이언트 구현 승인. 실제 short URL 서버 성공·실제 push/Notifly 전달·앱 API·기기 UI 검증은 이 승인과 구분되는 미실행 후속 항목이다.
|
||||
Reference in New Issue
Block a user