docs(event): 딥링크 계약과 검증 기록을 남긴다
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# 이벤트 상세 딥링크 연동 계약
|
||||
|
||||
## 1. URI와 ID
|
||||
- 프로덕션: `voiceon://event-detail/{id}`
|
||||
- 개발: `voiceon-test://event-detail/{id}`
|
||||
- `event` 같은 alias는 허용하지 않는다. `{id}`는 양수 signed 64-bit 정수이며 숫자 아님, 0, 음수, overflow는 거부한다.
|
||||
- 유효한 값은 기존 `AppStep.eventDetail(eventId:)`에 전달한다. 별도 화면/API/라우트는 없다.
|
||||
|
||||
## 2. OneLink
|
||||
- HTTPS hostname은 정확히 `voiceon.onelink.me`여야 한다. short URL과 long URL 모두 AppsFlyer SDK URL 해석 대상이다.
|
||||
- 해석된 이벤트 값은 `deep_link_value=event-detail`, `deep_link_sub5={id}`다.
|
||||
- `deep_link_sub1`~`deep_link_sub4`는 기존 마케팅 의미를 보존한다.
|
||||
- shortlink는 SDK가 해석한 결과만 소비한다. shortlink query를 직접 읽는 우회는 금지한다.
|
||||
- 실제 SDK 6.17.5에서 `performOnAppAttribution(with:)`를 호출하면 UDL 활성 시 `didResolveDeepLink`로 긴 URL의 이벤트 값이 반환됨을 확인했다. 앱 내 HTTPS URL은 이 경로를 사용한다. 실제 정상 short 서버 해석은 미검증이며 사용자 후속 지시에 따라 구현 차단 조건에서는 제외한다.
|
||||
|
||||
## 3. 인입 소유권
|
||||
| 진입 | 입력 계약 | 처리 |
|
||||
|---|---|---|
|
||||
| 커스텀 URL | §1의 앱별 URI | 공통 deep link queue로 전달 |
|
||||
| 일반 push `deep_link` | §1 URI 또는 §2 HTTPS OneLink | custom URI는 앱 경로, HTTPS OneLink는 SDK 처리 |
|
||||
| Notifly `url` | §1 URI 또는 §2 HTTPS OneLink | 기존 Notifly 경로에서 한 번 전달 |
|
||||
| 알림 목록 `deepLink` | §1 URI 또는 §2 HTTPS OneLink | 검증 후 필요한 최소 SDK bridge 연결, 공통 pending 재사용 |
|
||||
| SDK push | HTTPS OneLink만 | SDK 시작 전 `addPushNotificationDeepLinkPath([deep_link])`; 실제 userInfo로 `handlePushNotification` |
|
||||
|
||||
Notifly marker와 URL이 같이 있는 payload는 단일 소유자가 처리한다. 같은 payload를 `deep_link` 및 AppsFlyer push 경로로 중복 라우팅하지 않는다. in-app 알림에서 fake push payload를 만들지 않는다.
|
||||
|
||||
## 4. 상태와 무효 입력
|
||||
- cold start 및 로그인 전 목적지는 기존 공통 pending queue에 보관한다.
|
||||
- invalid ID는 current route와 pending route 어느 것도 바꾸지 않는다.
|
||||
- 동일 pending/current/delivery의 중복은 억제한다. delivery 이후 사용자가 다시 탭한 동일 링크는 새 요청으로 취급한다.
|
||||
- 기존 live leave confirmation을 재사용한다. 취소는 route 변경 없이 끝난다.
|
||||
- 기존 GCD 및 라우팅 전달 규칙을 유지한다.
|
||||
|
||||
## 5. SDK feasibility 판정 이력
|
||||
`P1-GATE` BLOCKED. SDK 6.17.5의 legacy URL-only resolver는 확인했지만 기존 UDL과 공존할 때의 콜백을 입증하지 못했다. 실제 iOS SDK 실행에 대한 fail-closed 네트워크 격리도 확보하지 못해 SDK를 실행하지 않았다. 공급자 지원 확인 또는 별도 승인된 실제 SDK 테스트로 미결 사항을 해결하기 전에는 제품 구현을 진행하지 않는다.
|
||||
|
||||
후속 판정: 제한 승인된 실제 SDK 실행에서 긴 URL의 `performOnAppAttribution(with:)`는 UDL이 설정돼 있으면 `didResolveDeepLink`로 event-detail/123을 반환함을 확인했다. 단축 링크의 실패 callback도 관찰했으나 정상 단축 fixture의 성공 해석은 미입증이다. 현재 남은 gate 조건은 해당 양성 증거이며, 이를 확보하기 전 전체 계약 구현 완료를 주장하지 않는다.
|
||||
|
||||
최종 사용자 결정(2026-10-29): 단축 URL이 없는 상태로 요구사항 구현을 진행한다. 위 구현 차단 조건은 해제하고 실제 단축 URL 서버 성공 검증만 미실행 후속 항목으로 남긴다. 입력 short/long URL 모두 SDK에 위임하며 앱이 임의 query 해석으로 대체하지 않는다.
|
||||
@@ -0,0 +1,191 @@
|
||||
# 이벤트 상세 딥링크 연동 실행 계획
|
||||
|
||||
| 문서 항목 | 내용 |
|
||||
|---|---|
|
||||
| 상태 | 클라이언트 구현·전체 로컬 검증·리뷰 승인 완료 |
|
||||
| 작성일 | 2026-10-29 |
|
||||
| 요구사항 기준 | [prd.md](prd.md) |
|
||||
| API 기준 | [api-contract.md](api-contract.md) |
|
||||
| 현재 Phase | Phase 3 로컬 검증 및 완료 Gate |
|
||||
| 현재 활성 Goal | 없음 |
|
||||
|
||||
## 목표와 현재 상태
|
||||
OneLink 및 기존 앱 인입점으로 이벤트 상세에 안전하게 진입한다. P1의 실제 SDK 공존 증거와 단축 URL 없이 진행하라는 사용자 후속 결정으로 구현 진입 조건을 충족했다. 과거 gate 차단 기록은 아래에 보존한다.
|
||||
|
||||
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 조건 |
|
||||
|---:|---|---:|---|---|
|
||||
| 1. SDK feasibility | 완료 / 제한 기록 | 2/2 | 구현 진행 승인 | 실제 short 양성 검증은 비차단 후속 항목 |
|
||||
| 2. 인입 구현 및 리뷰 수정 | 완료 | 3/3 | 없음 | 없음 |
|
||||
| 3. 로컬 검증 | 완료 | 1/1 | 없음 | 실제 short 양성 검증은 비차단 후속 항목 |
|
||||
|
||||
## 범위와 제약
|
||||
기존 `AppStep.eventDetail(eventId:)`, 공통 pending queue, SDK 초기화/Notifly bridge/GCD 경로를 재사용한다. 신규 alias, push payload 가공, SDK 업그레이드, router 재작성, 새 화면/API, 실백엔드 및 실제 공급자/AASA/push 전달 검증은 제외한다. 현재 branch에서 수행하며 commit/worktree 생성은 승인되지 않았다.
|
||||
|
||||
## Phase 1: SDK URL-only HTTPS OneLink feasibility
|
||||
**Phase 결과:** URL-only HTTPS short OneLink가 지원되는 실제 SDK 경로로 이벤트 값을 제공할 수 있는지 증거로 판정한다.
|
||||
**선행조건:** 없음. offline probe는 sibling이 수행 중이며 결과가 아직 없다.
|
||||
|
||||
### Task 1.1 `P1-T1` SDK URL resolution 근거 확인
|
||||
**Goal:** 실제 설치 SDK에서 short HTTPS OneLink의 URL-only 해석 경로와 callback/API 계약을 확인한다.
|
||||
**Files:** 소스 변경 없음. 확인 대상은 기존 AppsFlyer SDK 문서/설치 SDK metadata 및 sibling probe 결과다. 앱 코드·테스트·project config는 수정하지 않는다.
|
||||
**TDD 예외 사유:** 읽기 전용 외부/SDK feasibility 확인이다.
|
||||
**대체 검증:** 근거 URL/API 이름, SDK 버전, 입력 형태, 관찰된 callback과 해석된 `deep_link_value`/`deep_link_sub5`를 결과에 기록한다. 실제 SDK와 독립된 mock callback이나 합성 activity 생성만으로는 통과하지 않는다.
|
||||
- [x] SDK 전문 검증 결과 및 실제 설치 아티팩트/헤더 증거를 확인했다.
|
||||
- [x] URL-only HTTPS 해석과 UDL 공존 콜백이 입증되지 않아 PASS를 부여하지 않았다.
|
||||
- [x] `P1-GATE`를 BLOCKED로 판정하고 공급자 지원 확인 또는 별도 승인된 실 SDK 테스트가 필요한 상태로 중단했다. 임의 SDK API는 구현하지 않았다.
|
||||
|
||||
### Phase 1 Gate `P1-GATE`
|
||||
**선행조건:** `P1-T1` 완료 증거.
|
||||
**통과:** 실제 지원 경로로 HTTPS shortlink를 처리하고 이벤트 키 값을 얻는 증거가 명확하다. 이때만 Phase 2를 시작한다.
|
||||
**실패/불명확:** Phase 2와 3을 모두 차단한다. 안전한 offline 증거만으로 확인되지 않는 실제 SDK 동작은 공급자 확인 또는 별도 승인으로 해결한다.
|
||||
- [x] 근거 및 BLOCKED 판정을 이 문서 Progress에 기록했다. 이 체크는 gate 통과가 아니라 판정 기록 완료다.
|
||||
|
||||
## Phase 2: 조건부 TDD 인입 구현
|
||||
**선행조건:** 실제 SDK 공존 증거와 PRD DEC-004의 사용자 후속 승인. 정상 short URL fixture가 없다는 이유로 이 Phase를 다시 차단하지 않는다.
|
||||
|
||||
### Task 2.1 `P2-T1` 공통 URL parse, queue, 중복 및 취소
|
||||
**Goal:** 허용 URI/OneLink만 기존 `AppStep.eventDetail(eventId:)`로 전달하며 잘못된 입력과 중복으로 route를 손상하지 않는다.
|
||||
**Files, gate 통과 뒤 실제 필요를 확인:** `SodaLive/Sources/App/AppDeepLinkHandler.swift`, `SodaLive/Sources/App/SodaLiveApp.swift`, `SodaLive/Sources/App/AppState.swift`(필요한 경우만), `work/tests/AppDeepLinkGiftDetailCheck.swift`, `work/tests/`의 신규 집중 Swift check. 기존 라우트 정의와 파일 구조를 먼저 확인한다.
|
||||
**Interfaces:** consumes `AppStep.eventDetail(eventId:)` 및 기존 공통 pending queue. Produces 기존 deep link ingress로 전달되는 양수 `Int` 목적지만.
|
||||
- [x] **RED:** URI 2종, 허용 HTTPS host 및 ID 경계/overflow, invalid 입력이 current/pending을 보존하는 최소 fixture를 추가한다.
|
||||
- [x] **RED 확인:** 저장소의 실제 Swift check 실행으로 요구 assertion 실패를 확인한다.
|
||||
- [x] **GREEN:** 최소 parser/기존 queue 연결과 identical pending/current/delivery dedupe를 구현한다. 후속 새 탭은 억제하지 않는다.
|
||||
- [x] **GREEN 확인:** 재탭·cold start·로그인 전 pending·live confirmation 취소 테스트 통과.
|
||||
- [x] **REFACTOR:** 이번 변경에서 생긴 중복만 정리하고 집중 검사 및 기존 route 회귀를 기록한다.
|
||||
|
||||
### Task 2.2 `P2-T2` push, Notifly, notification-list ingress 소유권
|
||||
**Goal:** 각 기존 입력이 계약대로 하나의 owner만 거쳐 공통 route queue에 도달한다.
|
||||
**Files, gate 통과 뒤 확인:** `SodaLive/Sources/App/AppDelegate.swift`, `SodaLive/Sources/Notification/List/PushNotificationListViewModel.swift`, 필요한 최소 V2 SDK bridge, 관련 `work/tests/` Swift check.
|
||||
**Interfaces:** consumes Task 2.1의 기존 deep link ingress. Produces SDK 시작 전 push path 등록과 실제 userInfo 처리 연결, 중복 없는 Notifly/알림목록 전달.
|
||||
- [x] **RED:** SDK 경계 fixture에서 HTTPS OneLink `handlePushNotification(userInfo:)`, owner 단일성 및 목록 pending gate를 확인하는 실패 검사를 추가했다. pre-start path 등록은 AppDelegate 실제 빌드/리뷰에서 확인한다.
|
||||
- [x] **RED 확인:** focused production-source Swift check에서 미연결 호출/중복 owner 실패를 확인한다.
|
||||
- [x] **GREEN:** 실제 SDK API와 `P1-GATE`에서 증명된 입력 경로만 연결한다. custom URI는 기존 앱 경로, HTTPS OneLink는 SDK 경로를 따른다.
|
||||
- [x] **GREEN 확인:** 일반 push, Notifly marker+URL, 알림 목록 URL이 한 번만 queue에 들어가는지 검사한다. in-app fake push는 만들지 않는다.
|
||||
- [x] **REFACTOR:** production 코드만 최소 정리하고 focused 및 직접 영향 회귀 결과를 기록한다.
|
||||
|
||||
## Phase 3: 로컬 검증 및 완료 Gate
|
||||
**선행조건:** Phase 2 구현 완료.
|
||||
### Gate `P3-GATE`
|
||||
**Files:** 앞서 명시한 변경 파일 및 `work/tests/` 검사만.
|
||||
- [x] 이벤트 전용 검사와 push ownership, callback pending/dedup/cancel 회귀를 실행한다.
|
||||
- [x] 기존 14개와 신규 이벤트 딥링크 검사를 포함한 `work/tests` 15개를 모두 실행했다.
|
||||
- [x] `SodaLive` 및 `SodaLive-dev` Debug iOS Simulator 빌드를 수행했다.
|
||||
- [x] 단일 독립 리뷰와 지적 수정의 delta 승인을 같은 작업 디렉터리 `reviews/`에 기록했다.
|
||||
- [x] P1의 제한된 실제 SDK 콜백 검증과 제품 코드의 로컬 대역 검사를 구분했다. 정상 short 실서버 성공·Notifly/AASA/실푸시·backend 검증은 미실행 후속 항목이다.
|
||||
|
||||
## 의존성 및 Progress
|
||||
```text
|
||||
P1-T1 → P1-GATE(PASS 필수) → P2-T1 → P2-T2 → P3-GATE
|
||||
```
|
||||
|
||||
### `P1-T1` 및 `P1-GATE` 초기 기록: 2026-10-29
|
||||
- 상태: 대기. 지원 여부 판정 전이며 구현 완료가 아니다.
|
||||
- 관찰: `performOnAppAttribution`은 legacy callback으로 알려져 있으며 URL-only HTTPS OneLink resolution을 입증하지 않는다. synthetic `NSUserActivity`도 지원 근거가 아니다.
|
||||
- 진행: sibling이 안전한 offline probe를 수행 중이다. 결과는 아직 확인되지 않았다.
|
||||
- 다음 행동: 결과와 SDK 근거를 대조해 gate 판정. 실패/불명확 시 공급자 지원 확인 또는 별도 승인된 real SDK test 요청 후 정지한다.
|
||||
|
||||
## 변경 금지 및 완료 판정
|
||||
- 제품 구현은 P1 검증 결과와 사용자 후속 승인 범위를 따른다. 실제 short URL 성공 증거가 없음을 숨기지 않는다.
|
||||
- 지원되지 않은 SDK API나 query-only shortlink 해석을 만들어내지 않는다.
|
||||
- 실제 공급자/백엔드 전달 검증을 수행하거나 통과했다고 주장하지 않는다.
|
||||
- Phase 2/3 미완료 상태를 완료로 표시하지 않는다. 커밋은 별도 명시 요청 전까지 하지 않는다.
|
||||
|
||||
### P1-T1 결과 및 P1-GATE 판정 - 2026-10-29
|
||||
- 실제 설치 artifact `CFBundleVersion`과 정적 바이너리 문자열은 6.17.5다. workspace lock은 6.17.5, project lock은 6.18.1이며 이번 작업에서 변경하지 않았다.
|
||||
- 설치 헤더의 `performOnAppAttribution(with:)`는 `onAppOpenAttribution`을 강제 호출하며 re-engagement/session/launch를 집계하지 않는 URL resolver로 문서화돼 있다. URL-only API가 없다는 뜻은 아니다. 기존 `deepLinkDelegate` 설정과 공존할 때의 callback 선택은 미입증이다.
|
||||
- `sandbox-exec` deny-network 프로필 아래 `/usr/bin/true`는 exit 0이었지만 실제 iOS SDK와 위임된 네트워크 작업의 격리를 증명하지 않는다. SDK 로드/실행, 실제 OneLink 해석, 시뮬레이터 생성은 하지 않았다.
|
||||
- 결과: [SDK 검증 기록](reviews/phase1-onelink-sdk-feasibility.md), BLOCKED. 승인된 게이트 정책에 따라 Phase 2/3 제품 구현 및 완료 검증은 미착수다.
|
||||
- baseline: `bash work/tests/check-app-deep-link-gift-detail.sh` PASS assertions=279; `bash work/tests/check-event-detail.sh` 전체 그룹 PASS. 기존 production handler/API/VM 검사이며 새 딥링크 기능 검증이 아니다.
|
||||
- 문서 점검: 초안의 프로덕션/개발 scheme 표기를 사용자 확정 계약인 `voiceon`/`voiceon-test`로 교정했다. 앱 소스는 변경하지 않았다.
|
||||
- 작업 중 `project.pbxproj`에 참조 블록 순서 이동이 관찰됐다. 이번 실행에서는 해당 파일을 쓰거나 Xcode 빌드를 실행하지 않았으므로 외부 변경으로 보존하고 되돌리지 않았다.
|
||||
- 정리: 근거를 reviews 문서에 보존한 뒤 이번 실행 소유의 두 임시 디렉터리 및 notepad를 제거했고 `test ! -e`를 통과했다. SDK 인스턴스·앱·시뮬레이터·잔존 테스트 프로세스는 생성하지 않았다. `git diff --exit-code -- SodaLive/Sources work/tests`와 `git diff --check`는 exit 0이다.
|
||||
|
||||
### Task 1.2 `P1-T2` 제한적으로 승인된 실제 SDK 검증 - 2026-10-29
|
||||
- 사용자 승인: 이벤트 API 호출과 푸시 발송을 제외하고 AppsFlyer 테스트 OneLink의 실제 SDK 해석만 허용했다.
|
||||
- 범위: 현재 설치 SDK 6.17.5를 독립 임시 harness에서 실행하여 UDL delegate와 legacy delegate가 같이 있을 때 manual URL resolver의 실제 callback/이벤트 파라미터를 관찰한다. 제품 앱은 실행하지 않는다.
|
||||
- 금지: 앱 백엔드 요청, 이벤트 상세 API, 푸시 발송, 운영 앱 실행, AppsFlyer 설정 변경/단축 링크 생성, SDK 업그레이드, 자격증명·전체 payload 로그 노출.
|
||||
- TDD 예외: 제품 구현 전 SDK 외부 계약 검증이며 임시 실행 harness만 사용한다. 실제 SDK callback과 네트워크 결과를 기록하고 mock callback을 실증으로 취급하지 않는다.
|
||||
- [x] long/short URL 입력의 실제 callback과 성공/실패를 확인했다. long은 event-detail/123 반환, 공식 short 예시는 실패하여 정상 short fixture 미확보 제한을 기록했다.
|
||||
- [x] 실제 관찰에 따라 P1-GATE를 부분 입증/차단 유지로 재판정했다. 콜백 공존 문제는 확인됐지만 정상 short 응답을 추정하지 않았다.
|
||||
- [x] 소유한 앱/시뮬레이터/프로세스/임시 자원을 정리하고 증거를 reviews에 보존했다.
|
||||
|
||||
### P1-T2 실제 SDK 실행 결과 - 2026-10-29
|
||||
- [실제 SDK 검증 기록](reviews/phase1-onelink-sdk-runtime.md): SDK 6.17.5의 iOS Simulator slice를 링크한 독립 임시 앱으로 실행했다. parent가 최종 Swift 소스, build/run 스크립트, callback JSONL, SDK enum, 버전/서명/정리 영수증을 직접 읽었다.
|
||||
- 긴 URL: UDL+legacy delegate가 실제 등록된 상태에서 `performOnAppAttribution(with:)` 호출 후 `didResolveDeepLink(found)`로 event-detail/123 반환. UDL 없는 대조군은 `onAppOpenAttribution`으로 같은 값 반환. 무설정 및 합성 설정에서도 같은 callback 선택을 관찰했다.
|
||||
- 단축 URL: 공식 문서의 과거 예시만 확보됐다. 실제 SDK는 UDL 활성 시 `didResolveDeepLink(failure)`, legacy-only 시 `onAppOpenAttributionFailure`를 반환했다. 오류 domain `com.appsflyer.sdk.deeplink`, code 0. 원인을 링크 상태/설정/네트워크 중 하나로 단정하지 않는다.
|
||||
- 긴 URL의 성공은 SDK 로컬 해석과 일치하며 서버의 template 검증이나 정상 short resolution 성공을 뜻하지 않는다. 따라서 이전의 callback 공존 불확실성은 해소됐지만 정상 short 양성 증거가 필요한 기존 gate는 유지한다.
|
||||
- 기존 저장소에는 유효한 이벤트용 단축 URL fixture가 없었다. 다음 재개 조건은 정상 이벤트용 단축 OneLink URL 제공이다. 새 hosted link를 만들거나 실제 개발키/SDK start로 범위를 확대하지 않았다.
|
||||
- 제품 소스/API/UI/프로젝트 설정은 이번 검증에서 변경하지 않았다. 이벤트 API·앱 서버·푸시 발송·운영 앱 실행은 없다. 기존 프로젝트 파일의 외부 재정렬은 그대로 보존한다.
|
||||
- 정리: 소유 앱 uninstall 및 simulator shutdown/delete 영수증을 확인하고 parent도 UUID 부재와 앱 프로세스 부재를 검증했다. 결과를 reviews에 보존한 뒤 소유 임시 소스·바이너리·캐시·원시 로그가 있던 두 run root와 notepad를 삭제했다. 두 root 부재 검사 PASS. 공유 SDK/Xcode 캐시 및 사용자 기기는 변경하지 않았다.
|
||||
|
||||
### Phase 2 착수 결정 - 2026-10-29
|
||||
- 사용자 명시 지시로 short 실연결 양성 검증을 비차단 후속 항목으로 전환했다. 추가 fixture 확보 요청 및 SDK 가능성 재조사를 반복하지 않는다.
|
||||
- SDK 6.17.5에서 직접 관찰한 `performOnAppAttribution(with:)` → `didResolveDeepLink`를 앱 내 OneLink resolver로 사용한다. push의 실제 userInfo 경로와 Notifly 단일 owner는 기존 승인 계약을 따른다.
|
||||
- TDD는 실제 production handler/ingress를 컴파일하는 기존 work/tests 패턴을 사용한다. 네트워크/SDK 경계만 대역으로 대체하고 실제 앱 parser, 상태 전이, route 횟수를 검증한다. 기존 279개 deep-link 회귀는 약화하지 않는다.
|
||||
- 코드 변경은 현재 브랜치에서 수행하며 기존 `project.pbxproj` 재정렬을 외부 변경으로 보존한다. commit/push/worktree/SDK 업그레이드/이벤트 API 변경은 하지 않는다.
|
||||
|
||||
### Phase 2 구현 경로 확정 - 2026-10-29
|
||||
- P2-T1/P2-T2 공유 인터페이스: `AppDeepLinkHandler.handle(url:source:)`와 단일 `pendingDeepLinkAction`을 그대로 사용한다. `AppDeepLinkAction.eventDetail(eventId:)`, `handleEventDeepLink(result:)`, `handlePushDeepLink(userInfo:)`를 기존 handler에 추가한다. 신규 production 파일/모듈/프로젝트 membership 변경은 없다.
|
||||
- Ruling: handler에서 AppsFlyerLib를 직접 사용한다. short/long 원본 URL을 `performOnAppAttribution(with:)`에 전달하고 UDL 결과만 이벤트 action으로 바꾼다. 테스트는 기존 Moya 패턴처럼 SDK 경계 대역 모듈을 컴파일하며 기존 gift/community 279 assertion을 유지한다.
|
||||
- Ruling: `AppState.setAppStep(step:completion:)`에 기본값 있는 완료 closure만 추가한다. 이벤트 한 건의 in-flight ID를 실제 비동기 route 적용 완료 또는 live 취소에서 해제한다. current ID 비교만 하거나 영구 last-ID/TTL/SDK 요청 상관관계를 만드는 대안은 사용하지 않는다.
|
||||
- Ruling: push 소유권 정책은 기존 handler의 실행 가능한 함수로 둔다. Notifly marker와 비어 있지 않은 String/URL(NSURL bridge 포함)이 있으면 SDK 소유로 종료하며 malformed nonempty String도 legacy로 흘리지 않는다. Firebase/Notifly hook은 AppDelegate에서 먼저 실행한다. 그 밖의 nonempty deep_link는 URI 공통 경로 또는 검증된 OneLink의 실제 userInfo SDK 경로로 처리하며 실패해도 legacy로 fallback하지 않는다.
|
||||
- 수정 대상 확정: `App/AppDeepLinkHandler.swift`, `App/AppDelegate.swift`, `App/AppState.swift`, `work/tests/AppDeepLinkGiftDetailCheck.swift`, `work/tests/check-app-deep-link-gift-detail.sh`, 신규 `work/tests/EventDeepLinkCheck.swift`, `work/tests/check-event-deep-link.sh`. `SodaLiveApp.swift`, `SplashView.swift`, 알림 목록 ViewModel, UI/API/SDK 설정값은 변경하지 않는다.
|
||||
- 검증: 실제 production handler와 AppState를 함께 컴파일해 async navigation, single-slot pending, live confirm/cancel을 실행한다. SDK 대역은 URL 전달/decoded UDL/push 경계만 담당하며 실제 SDK 네트워크 성공 증거로 취급하지 않는다. 양 scheme 빌드, 전체 회귀 및 독립 리뷰는 parent가 수행한다.
|
||||
- P2-T1 RED: production handler/AppState + 새 Swift harness로 `bash work/tests/check-event-deep-link.sh` 실행, exit 1 및 `FAIL: event URI accepted: voiceon, 1` 확인. 컴파일 오류가 아니라 미구현 URI 수락 assertion이 실패했다. 변경 전 gift/community baseline은 `PASS AppDeepLinkGiftDetailCheck assertions=279`다.
|
||||
- URI/HTTPS 최소 구현 후 추가 RED: 테스트를 먼저 확장하고 callback/push 경계를 false로 둔 상태에서 `bash work/tests/check-event-deep-link.sh callback` → exit 1 `FAIL: decoded UDL event accepted`, `bash work/tests/check-event-deep-link.sh push` → exit 1 `FAIL: HTTPS push consumed`. 같은 실제 소스의 `uri onelink`는 assertions=188 PASS, 기존 gift/community는 assertions=279 PASS다.
|
||||
- 상태 교체 회귀 RED: `bash work/tests/check-event-deep-link.sh lifecycle` → exit 1 `FAIL: superseded live event releases in-flight guard for deliberate reopen`. live 대기 이벤트를 기존 다른 외부 navigation이 대체하면 이전 cancel closure가 호출되지 않아 이벤트 guard가 남는 근본 원인을 확인했다.
|
||||
- Ruling: `AppState.requestExternalNavigation`에서 대기 요청을 교체할 때 이전 cancel closure를 호출한다. 기존 single-slot replacement를 명시적 취소로 마무리하는 한 줄 변경이며, 오래된 이벤트 guard를 추측해서 해제하거나 영구 기록을 만드는 대안은 배제한다. 기존 외부 탐색도 교체되면 자신의 취소 정리를 실행한다.
|
||||
|
||||
### Phase 2 완료 및 집중 GREEN - 2026-10-29
|
||||
- `bash work/tests/check-event-deep-link.sh` → exit 0, 5개 그룹 및 `PASS EventDeepLinkCheck assertions=324`. 실제 production AppDeepLinkHandler/AppState를 실행해 두 URI/hostless 기존 관례, 양수 64-bit/Int.max 및 invalid fallback 방지, short/long SDK 전달, decoded UDL/실패 보존, splash/latest pending, async burst/current/back/reopen, live confirm/cancel/다른 외부 요청으로 교체를 검증했다.
|
||||
- push 그룹은 production `handlePushDeepLink(userInfo:)`를 실행해 실제 입력 dictionary 보존, URI/HTTPS SDK 분기, unknown nonempty no-fallback, empty legacy 반환, Notifly String/NSURL/malformed 소유권 및 목록의 공통 pending 진입을 확인했다. Notifly SDK URL opening/cold replay/click tracking 자체는 실행하지 않았다.
|
||||
- `bash work/tests/check-app-deep-link-gift-detail.sh` → exit 0, 기존 `assertions=279` 그대로 PASS. SDK 모듈 경계와 추가 AppState signature 대역만 보완했으며 기존 assertion은 수정·삭제하지 않았다.
|
||||
- `bash work/tests/check-event-detail.sh` → exit 0, 실제 API/repository/DTO/VM 전체 그룹 및 기존 7개 ID-only navigation call-site 검사 PASS. 앱 서버 요청 없이 기존 transport 대역을 사용했다.
|
||||
- `GIT_MASTER=1 git diff --check` → exit 0. `AppDelegate`의 UDL은 기존 sub1~4/legacy 종류/logUtm 처리를 유지한 뒤 공통 이벤트 수신 함수를 호출한다. GCD에 이벤트를 추가하지 않았다. Firebase/Notifly callback 이후 push 정책을 호출하고 pre-start 초기화에서 `["deep_link"]` 경로를 등록했다.
|
||||
- 단일 SDK URL 요청/콜백 상관관계는 만들지 않았다. notFound/failure는 pending을 지우지 않으며 같은 URL도 새 입력마다 SDK로 전달할 수 있다. navigation만 queued/live/current 범위에서 중복 억제하고 completion/cancel 시 guard를 해제한다.
|
||||
- 양 target 실제 SDK 링크 빌드, 전체 work/tests 및 독립 리뷰는 parent의 P3-GATE이며 아직 완료 표시하지 않는다. 실제 정상 short 성공은 비차단 후속 검증이고, 이번 검사의 synthetic short URL 및 decoded UDL은 로컬 경계 대역 데이터다.
|
||||
- runner 독립성: `env -u TMPDIR bash work/tests/check-event-deep-link.sh` → exit 0/assertions=324, `env -u TMPDIR bash work/tests/check-app-deep-link-gift-detail.sh` → exit 0/assertions=279. 두 runner는 자체 mktemp/trap 정리를 사용하며 TMPDIR 설정 유무에 의존하지 않는다.
|
||||
- 문법 검증: `swiftc -frontend -parse SodaLive/Sources/App/AppDeepLinkHandler.swift SodaLive/Sources/App/AppDelegate.swift SodaLive/Sources/App/AppState.swift work/tests/AppDeepLinkGiftDetailCheck.swift work/tests/EventDeepLinkCheck.swift` 및 `bash -n work/tests/check-event-deep-link.sh work/tests/check-app-deep-link-gift-detail.sh` 모두 exit 0. 이 세션에는 lsp_diagnostics 도구가 없으며 AppDelegate 실제 SDK 링크/type-check는 parent 빌드로 남긴다.
|
||||
- 변경 파일은 production 3개, 테스트/runner 4개 및 이 계획 문서뿐이다. `SodaLive.xcodeproj/project.pbxproj`의 기존 외부 재정렬은 쓰지 않았고, UI/알림 목록/Splash/SDK lockfile은 변경하지 않았다. 네트워크 요청, push 발송, 실제 SDK 실행, commit, reviewer/subagent 실행은 없다.
|
||||
- P2-T1 추가 상태 경계 RED: live에서 다른 이벤트를 기다리다가 이미 current인 이벤트를 최신 intent로 탭하는 fixture를 추가했다. `bash work/tests/check-event-deep-link.sh lifecycle` → exit 1 `FAIL: latest intent for already-current event replaces live pending without stacking current event`. 기존 대기 요청은 교체하되 실제 navigation 직전에 current 목적지를 다시 확인해 중복 stack 없이 완료하도록 수정한다.
|
||||
|
||||
### P3-GATE 상위 실행자 검증 1차 - 2026-10-29
|
||||
- production handler/AppDelegate/AppState 및 변경/신규 테스트·runner를 읽고 기존 MainView의 외부 이동 호출도 대조했다. 신규 테스트는 실제 AppState의 비동기 navigationPath와 live 확인/취소를 실행한다.
|
||||
- 전체 `work/tests/check-*.sh` 15개 중 첫 13개 PASS 후 300초 실행 제한으로 received-gift 검사 도중 중단됐다. `bash work/tests/check-received-gift-detail.sh && bash work/tests/check-user-creator-chat-date-presentation.sh`를 별도로 재실행해 둘 다 exit 0, 합계 15개 모두 PASS를 확인했다.
|
||||
- 이벤트 딥링크 `assertions=325`, 기존 gift/community `assertions=279` PASS. 실제 OneLink 서버/Notifly 전달은 이 로컬 검사로 입증되지 않으며 경계 대역 사용 사실을 유지한다.
|
||||
- `xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -destination 'generic/platform=iOS Simulator' -disableAutomaticPackageResolution CODE_SIGNING_ALLOWED=NO build`: exit 0. 동일 명령의 SodaLive-dev도 exit 0. 구현자 완료 후 최종 snapshot에서 두 스킴을 다시 빌드하여 모두 BUILD SUCCEEDED를 확인했다.
|
||||
- 단일 Oracle 리뷰 진행 중. 리뷰 전 결과이며 차단 지적이 있으면 신규 회귀 Task를 기록하고 관련 검사·빌드를 다시 수행한다. LSP 도구는 제공되지 않아 실제 양 타깃 컴파일과 Swift parse로 대체한다.
|
||||
|
||||
### Task 2.3 `P2-R1` 비동기 이벤트 이동 경합 회귀 수정
|
||||
- Review: [REV-P2-001](reviews/phase2-event-detail-links.md), 최초 REQUEST_CHANGES.
|
||||
- Goal: 현재 이벤트를 포함한 연속 혼합-ID 요청이 마지막 목적지를 보존하고, 실제 route 변경 시점에 중복/오래된 요청을 걸러낸다.
|
||||
- Files: `SodaLive/Sources/App/AppDeepLinkHandler.swift`, `SodaLive/Sources/App/AppState.swift`, `work/tests/EventDeepLinkCheck.swift`, 필요 시 기존 mock signature만 보완.
|
||||
- [x] **RED:** current 45에서 중간 settle 없는 `44 → 45`, `44 → 45 → 44`를 추가하고 의도한 assertion 실패를 확인한다.
|
||||
- [x] **GREEN:** 최신 요청 유효성과 현재 목적지 검사를 실제 AppState route mutation과 동일한 main queue 블록에서 수행한다. 시간 지연/TTL이나 전역 router 재작성으로 우회하지 않는다.
|
||||
- [x] **GREEN 확인:** 혼합 ID, 동일 ID burst, live 확인/취소/대체, back/reopen 및 기존 279개 회귀를 재실행한다. 현재 이벤트 뒤에 live가 표시된 경우에도 기존 종료 확인 정책을 지키는지 함께 확인한다.
|
||||
- [x] **REFACTOR:** 관련 테스트/Swift parse/양 스킴 빌드 및 단일 reviewer의 delta 판정을 기록했다. 앱 API/UI/SDK 계약은 변경하지 않았다.
|
||||
- 최종 GREEN: 위 경계 수정 후 `env -u TMPDIR bash work/tests/check-event-deep-link.sh` → exit 0, 5개 그룹 및 assertions=325 PASS. `env -u TMPDIR bash work/tests/check-app-deep-link-gift-detail.sh` → exit 0/assertions=279, `bash work/tests/check-event-detail.sh` → exit 0/전 그룹 PASS. 변경 Swift 5개 parse와 `GIT_MASTER=1 git diff --check`를 다시 실행해 exit 0을 확인했다. parent 통합 gate 항목은 계속 미완료다.
|
||||
|
||||
### P2-R1 RED 및 수정 인터페이스 확정 - 2026-10-29
|
||||
- 실제 production handler/AppState를 컴파일한 `bash work/tests/check-event-deep-link.sh mixed-current` → exit 1, `FAIL: current 45 then queued 44->45 preserves 45 and original stack`.
|
||||
- `bash work/tests/check-event-deep-link.sh mixed-repeat` → exit 1, `FAIL: current 45 then queued 44->45->44 appends latest 44 exactly once`. 두 fixture는 current 45/live OFF에서 입력 사이 settle 없이 실행하며 기존 325개 검사를 변경·삭제하지 않았다.
|
||||
- P2-R1 하위 발견: current event 45가 live player 뒤에 있으면 기존 early current 검사 때문에 승인된 live 종료 확인을 건너뛴다. `bash work/tests/check-event-deep-link.sh current-live` → exit 1, `FAIL: current event behind live still requests leave confirmation`. 이 Task 안에서 같은 current 판정 위치 수정으로 해결하며 UI/policy를 새로 만들지 않는다.
|
||||
- 수정 전 결정: `AppState.setAppStep(step:ifCurrentStep:completion:)`에 기본값 nil인 optional predicate를 추가한다. 기존 `replaceCurrentAppStep` 패턴과 같은 위치, 즉 기존 main async 블록 안에서 mutation 직전에 predicate를 실행한다. 완료 closure는 mutation 여부와 무관하게 한 번 호출해 오래된/no-op 이벤트 guard도 해제한다. 기존 step-only 및 completion caller 의미는 유지한다.
|
||||
- Handler의 current 목적지 early return은 제거하고 기존 live 요청 확인을 먼저 따른다. 최신 event ID와 current 목적지 비교는 위 predicate에서 원자적으로 처리한다. 추가 dispatch/delay, queue, TTL/history 또는 SDK 요청 계약은 추가하지 않는다. 기존 gift/community mock은 동일 predicate signature만 보완한다.
|
||||
|
||||
### P2-R1 집중 GREEN 및 parent delta 대기 - 2026-10-29
|
||||
- `bash work/tests/check-event-deep-link.sh mixed-current mixed-repeat current-live` → exit 0, 각 mixed 순서와 current-live confirm/cancel/reopen 그룹 PASS, assertions=21. 실제 production AppState mutation 직전에 최신 intent/current 비교를 수행해 `44 → 45`는 원래 45/stack을 보존하고 `44 → 45 → 44`는 44 한 건만 추가한다.
|
||||
- `bash work/tests/check-event-deep-link.sh` → exit 0/assertions=345. 기존 325개를 그대로 보존한 상태에서 신규 20개 assertion을 추가했다. obsolete/no-op completion 이후 back/reopen과 live 취소 뒤 같은 ID 재요청도 통과한다.
|
||||
- `bash work/tests/check-app-deep-link-gift-detail.sh` → exit 0, 기존 assertions=279 PASS. 기존 mock의 `setAppStep` optional predicate signature/skip completion만 보완했으며 assertion을 변경·삭제하지 않았다.
|
||||
- `bash work/tests/check-event-detail.sh` → exit 0, 실제 API/repository/DTO/VM 및 7개 기존 navigation call-site 검사 전체 PASS. 로컬 대역만 사용했으며 네트워크 요청은 없다.
|
||||
- `swiftc -frontend -parse SodaLive/Sources/App/AppDeepLinkHandler.swift SodaLive/Sources/App/AppState.swift work/tests/AppDeepLinkGiftDetailCheck.swift work/tests/EventDeepLinkCheck.swift` → exit 0. LSP 도구는 제공되지 않는다. cache/TMPDIR는 지정된 IMPL_TMP를 사용했고 runner의 자체 mktemp/trap 정리를 유지했다.
|
||||
- 변경 경계: handler 이벤트 case, AppState.setAppStep optional guard, 관련 기존 Swift check/mock 및 이 계획/리뷰 기록만 수정했다. AppDelegate/URL 계약/SDK calls/Notifly/마케팅/API/UI/runner는 이번 delta에서 변경하지 않았다. 빌드/전체 검사/reviewer/commit은 실행하지 않았으며, P2-R1의 마지막 체크는 parent 재빌드 및 동일 Oracle delta 판정 후 완료한다.
|
||||
|
||||
### P2-R1 및 P3-GATE 최종 검증 - 2026-10-29
|
||||
- parent가 수정된 handler/AppState와 회귀 fixture를 직접 읽었다. latest/current predicate가 실제 mutation과 동일 main queue 블록에 있고, skip도 completion을 실행하며 live 확인을 먼저 거치는 것을 확인했다.
|
||||
- `for script in work/tests/check-*.sh; do bash "$script"; done`를 600초 제한으로 재실행: **TOTAL_SCRIPTS=15, FAILED_SCRIPTS=0**, exit 0. 이번 최종 실행은 timeout 없이 전체를 완료했다. 새 이벤트 assertion 345 및 기존 deep-link assertion 279 PASS.
|
||||
- 수정 snapshot에서 `xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -destination 'generic/platform=iOS Simulator' -disableAutomaticPackageResolution CODE_SIGNING_ALLOWED=NO build`: **BUILD SUCCEEDED, exit 0**. 동일 명령의 SodaLive-dev도 **BUILD SUCCEEDED, exit 0**.
|
||||
- 동일 Oracle 리뷰 작업의 수정분 판정: **REV-P2-001 ADDRESSED / APPROVE**, 새 차단 사항 없음. 실제 source와 테스트 실행 경계를 독립 확인했으며 테스트·빌드 결과는 parent 실행 증거를 사용했다.
|
||||
- 실제 SDK 서버 호출/푸시 발송/앱 서버/운영 앱 실행은 구현·최종 검증 단계에서 하지 않았다. UI는 변경하지 않았으며 실제 iOS 전체 앱 조작을 검증했다고 주장하지 않는다. LSP 도구는 없어 Swift parse와 실제 양 타깃 컴파일로 대체했다.
|
||||
- 현재 브랜치에서 작업했고 commit/push/worktree/SDK 업그레이드는 수행하지 않았다. 기존 외부 project.pbxproj 재정렬을 보존했다.
|
||||
- 정리: 이번 구현의 xcodebuild/Swift 검사 프로세스가 남지 않았음을 확인하고 소유 run root 2개 및 notepad를 제거했다. 두 root 부재 검사 PASS. 원시 빌드 로그·컴파일 임시 산출물은 함께 제거했으며 실행 명령/결과/리뷰 판정은 이 문서에 보존했다. 공유 Xcode/SDK 캐시, 사용자 기기 및 외부 project.pbxproj 변경(기존 38행 이동)은 보존했다.
|
||||
@@ -0,0 +1,65 @@
|
||||
# 이벤트 상세 딥링크 및 OneLink 연동 PRD
|
||||
|
||||
## 문서 정보
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서 상태 | 클라이언트 구현·로컬 검증 완료, 실제 단축 URL 성공 검증은 비차단 후속 항목 |
|
||||
| 작성일 / 최종 수정일 | 2026-10-29 |
|
||||
| 대상 제품 | SodaLive iOS 이벤트 상세 진입 |
|
||||
| 작성자·결정권자 | 사용자 승인 범위, 실행자는 게이트 이후 구현 |
|
||||
| 관련 API Contract | [api-contract.md](api-contract.md) |
|
||||
| 관련 구현 계획 | [plan-task.md](plan-task.md) |
|
||||
| 관련 review | [최종 구현 리뷰](reviews/phase2-event-detail-links.md), [실제 SDK 검증](reviews/phase1-onelink-sdk-runtime.md) |
|
||||
|
||||
## 1. Overview와 문제
|
||||
OS/SDK가 전달하는 이벤트 URI·OneLink와 앱 내부 알림 입력을 기존 `AppStep.eventDetail(eventId:)`로 연결한다. 비로그인 및 cold start에서도 기존 pending 경로를 재사용한다. SDK 6.17.5에서 `performOnAppAttribution(with:)`가 UDL 활성 시 `didResolveDeepLink`로 긴 URL의 이벤트 값을 반환함을 실제로 확인했고 이를 구현에 사용한다. 정상 단축 URL의 서버 해석은 사용자 결정에 따라 비차단 후속 검증으로 남긴다.
|
||||
|
||||
## 2. 목표와 성공 기준
|
||||
- 커스텀 URL, OneLink, push, Notifly URL, 알림 목록 진입을 한 이벤트 ID 처리 경로로 연결한다.
|
||||
- cold start 및 로그인 전에는 기존 공통 pending queue를 사용하고, 잘못된 ID가 현재 화면이나 pending 목적지를 변경하지 않게 한다.
|
||||
- 동일 pending/current/delivery의 중복만 억제한다. 이후 사용자가 다시 탭한 동일 링크는 새 탐색으로 허용한다.
|
||||
- 실제 SDK 공존 증거와 DEC-004의 사용자 후속 결정으로 구현 진입 조건을 충족했다. 단축 URL의 실제 서버 성공을 로컬 테스트 결과와 혼동하지 않는다.
|
||||
|
||||
## 3. Non-Goals
|
||||
새 이벤트 화면/API, 새 이벤트 ID 라우트 별칭, `pushEventId`, 전역 router 재작성, SDK 업그레이드, share button, OneLink 생성·AppsFlyer/AASA/실제 push 전달 설정은 제외한다. 실백엔드 검증과 실제 공급자 연동 검증도 이 작업에서 하지 않는다.
|
||||
|
||||
## 4. 라우팅 및 동작 요구사항
|
||||
| ID | 상태 | 요구사항 | 수용 기준 | Contract / Goal |
|
||||
|---|---|---|---|---|
|
||||
| LINK-001 | 확정 | 프로덕션 URI는 `voiceon://event-detail/{id}`, 개발 URI는 `voiceon-test://event-detail/{id}`만 허용한다. | 이벤트 alias 없음. ID는 양수 signed 64-bit 정수. | Contract §1, P2-T1 |
|
||||
| LINK-002 | 확정 | OneLink는 정확한 HTTPS host `voiceon.onelink.me`의 short/long URL이며 `deep_link_value=event-detail`, `deep_link_sub5={id}`를 사용한다. | `deep_link_sub1`~`deep_link_sub4` 기존 마케팅 값은 건드리지 않는다. | Contract §2, P2-T1 |
|
||||
| LINK-003 | 확정 | 표준 push `deep_link`, Notifly `url`, 알림 목록 `deepLink`는 커스텀 URI 또는 HTTPS OneLink를 받는다. | shortlink는 SDK가 해석해야 한다. query만 직접 읽어 shortlink를 우회하지 않는다. | Contract §3, P2-T2 |
|
||||
| LINK-004 | 확정 | SDK push 경로는 `addPushNotificationDeepLinkPath([deep_link])`를 SDK 시작 전에 등록하고 실제 `userInfo`로 `handlePushNotification`을 호출한다. | HTTPS OneLink만 SDK push handler로 전달한다. Notifly marker와 URL이 함께 있으면 소유자 하나만 라우팅한다. 같은 payload를 `deep_link`나 AppsFlyer push로 중복 전달하지 않는다. | Contract §3, P2-T2 |
|
||||
| LINK-005 | 확정 | 목적지는 기존 `AppStep.eventDetail(eventId:)`다. | 기존 상세 UI/API 계약과 route 이름은 유지한다. 공통 pending queue를 사용하며 cold start 및 로그인 전에도 보존한다. | Contract §4, P2-T1 |
|
||||
| LINK-006 | 확정 | 비양수, 숫자 아님, overflow ID는 무시한다. | 잘못된 입력은 기존 current 또는 pending 목적지를 바꾸지 않는다. 같은 pending/current/delivery만 억제하며 이후 새 사용자 탭은 허용한다. | Contract §1, P2-T1 |
|
||||
| LINK-007 | 확정 | 기존 live 종료 확인을 보존한다. | 확인 취소 시 탐색하지 않는다. 기존 GCD와 route는 유지한다. | Contract §4, P2-T1 |
|
||||
|
||||
## 5. 초기 SDK 게이트 기록(후속 DEC-004로 구현 차단 해제)
|
||||
- `P1-GATE`가 `P2` 전체의 선행조건이다. 설치된 SDK 6.17.5의 공개 계약 조사와 실행 격리 사전 점검을 마쳤으나 gate를 통과하지 못했다. 제품 코드는 수정하지 않는다.
|
||||
- `performOnAppAttribution(with:)`는 URL-only legacy resolver이며 공개 계약이 존재한다. 다만 UDL delegate가 동시에 설정된 상태에서의 콜백 선택을 확인하지 못했다. 합성 activity나 가짜 SDK callback의 성공만으로 실제 SDK 동작을 입증한 것으로 취급하지 않는다.
|
||||
- gate 실패 시 지원 API를 추측하거나 구현하지 않는다. 공급자 지원 확인 또는 별도 승인된 실제 SDK 테스트를 요청하고 정지한다.
|
||||
- 기존 SDK 초기화, GCD 처리, 라우트 및 pending queue를 존중한다. 새 alias·의존성·프로젝트 설정 변경을 만들지 않는다.
|
||||
|
||||
## 6. 추적 및 결정
|
||||
| 날짜 | ID | 상태 | 결정 | 영향 |
|
||||
|---|---|---|---|---|
|
||||
| 2026-10-29 | DEC-001 | 확정 | 사용자 승인 범위는 딥링크/OneLink이며 SDK feasibility gate 통과 전 구현 금지 | P1-GATE, P2 |
|
||||
| 2026-10-29 | DEC-002 | 확정 | 기존 `AppStep.eventDetail(eventId:)`와 공통 queue 재사용 | LINK-005 |
|
||||
| 2026-10-29 | DEC-003 | 확정 | 현재 branch에서 작업, 승인 없는 commit 및 worktree 생성 없음 | 실행 범위 |
|
||||
|
||||
## 7. 제외 범위
|
||||
기존 완료 문서 [이벤트 상세 API 연동](../20261029_이벤트_상세_API_연동/prd.md)은 별개이며 수정하지 않는다. 실제 AppsFlyer/Notifly/AASA/push 전달과 backend 검증은 별도 승인 대상이다.
|
||||
|
||||
### 추가 승인 - 2026-10-29
|
||||
사용자가 AppsFlyer 테스트 OneLink의 실제 SDK 해석에 한해 검증을 허용했다. 기존 UDL과 manual URL resolver의 공존 동작 확인만 수행한다. 이벤트 API/앱 서버 호출, 푸시 발송, 운영 앱 실행, 공급자 콘솔 설정 변경은 계속 제외한다. 제품 구현 gate는 검증 결과 전까지 유지한다.
|
||||
|
||||
### 실제 SDK 검증 결과 - 2026-10-29
|
||||
긴 HTTPS OneLink는 현재 SDK 6.17.5에서 UDL delegate가 설정된 상태의 수동 resolver 호출 후 `didResolveDeepLink(found)`로 event-detail/123을 반환했다. legacy-only 대조군도 확인해 콜백 선택의 이전 불확실성을 해소했다. 공식 단축 URL 예시는 해석에 실패했으며 정상 short fixture의 양성 검증은 남았다. 당시 gate 유지 판정과 제한은 [실행 기록](reviews/phase1-onelink-sdk-runtime.md)에 보존한다.
|
||||
|
||||
### 사용자 후속 결정: 단축 URL 없이 구현 진행 - 2026-10-29
|
||||
- 사용자: "단축 URL을 만들어 둔 것이 없다. 요구사항대로 진행한다."
|
||||
- DEC-004: 정상 단축 URL의 실연결 양성 검증을 구현 차단 조건에서 제외한다. 이미 실제로 확인한 `performOnAppAttribution(with:)` → UDL 경로를 사용해 긴/단축 HTTPS OneLink를 SDK에 전달한다. 실연결 성공을 추정하거나 단축 링크를 직접 생성하지 않는다.
|
||||
- 로컬 SDK 경계 대역으로 short/long 전달, UDL 성공/실패, 이벤트 ID, 중복·cold start·live 취소 및 push owner를 검증한다. SDK 자체 short 서버 해석은 후속 검증으로 명시한다. 추가 인터뷰나 fixture 요청 없이 구현·빌드·리뷰를 진행한다.
|
||||
|
||||
### 최종 클라이언트 검증 - 2026-10-29
|
||||
이벤트 딥링크 345 assertion, 기존 deep-link 279 assertion 및 전체 로컬 회귀 15개가 통과했다. SodaLive/SodaLive-dev Debug 시뮬레이터 빌드도 통과했고, 단일 리뷰의 비동기 경합 지적을 RED/GREEN으로 수정한 뒤 delta APPROVE를 받았다. 실제 정상 short 링크·실제 푸시/Notifly 전달·앱 서버 및 전체 기기 UI 동작은 이번 완료 주장에 포함하지 않는다.
|
||||
Reference in New Issue
Block a user