31 KiB
이벤트 상세 딥링크 연동 실행 계획
| 문서 항목 | 내용 |
|---|---|
| 상태 | 클라이언트 구현·전체 로컬 검증·리뷰 승인 완료 |
| 작성일 | 2026-10-29 |
| 요구사항 기준 | prd.md |
| API 기준 | 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 생성만으로는 통과하지 않는다.
- SDK 전문 검증 결과 및 실제 설치 아티팩트/헤더 증거를 확인했다.
- URL-only HTTPS 해석과 UDL 공존 콜백이 입증되지 않아 PASS를 부여하지 않았다.
P1-GATE를 BLOCKED로 판정하고 공급자 지원 확인 또는 별도 승인된 실 SDK 테스트가 필요한 상태로 중단했다. 임의 SDK API는 구현하지 않았다.
Phase 1 Gate P1-GATE
선행조건: P1-T1 완료 증거.
통과: 실제 지원 경로로 HTTPS shortlink를 처리하고 이벤트 키 값을 얻는 증거가 명확하다. 이때만 Phase 2를 시작한다.
실패/불명확: Phase 2와 3을 모두 차단한다. 안전한 offline 증거만으로 확인되지 않는 실제 SDK 동작은 공급자 확인 또는 별도 승인으로 해결한다.
- 근거 및 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 목적지만.
- RED: URI 2종, 허용 HTTPS host 및 ID 경계/overflow, invalid 입력이 current/pending을 보존하는 최소 fixture를 추가한다.
- RED 확인: 저장소의 실제 Swift check 실행으로 요구 assertion 실패를 확인한다.
- GREEN: 최소 parser/기존 queue 연결과 identical pending/current/delivery dedupe를 구현한다. 후속 새 탭은 억제하지 않는다.
- GREEN 확인: 재탭·cold start·로그인 전 pending·live confirmation 취소 테스트 통과.
- 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/알림목록 전달.
- RED: SDK 경계 fixture에서 HTTPS OneLink
handlePushNotification(userInfo:), owner 단일성 및 목록 pending gate를 확인하는 실패 검사를 추가했다. pre-start path 등록은 AppDelegate 실제 빌드/리뷰에서 확인한다. - RED 확인: focused production-source Swift check에서 미연결 호출/중복 owner 실패를 확인한다.
- GREEN: 실제 SDK API와
P1-GATE에서 증명된 입력 경로만 연결한다. custom URI는 기존 앱 경로, HTTPS OneLink는 SDK 경로를 따른다. - GREEN 확인: 일반 push, Notifly marker+URL, 알림 목록 URL이 한 번만 queue에 들어가는지 검사한다. in-app fake push는 만들지 않는다.
- REFACTOR: production 코드만 최소 정리하고 focused 및 직접 영향 회귀 결과를 기록한다.
Phase 3: 로컬 검증 및 완료 Gate
선행조건: Phase 2 구현 완료.
Gate P3-GATE
Files: 앞서 명시한 변경 파일 및 work/tests/ 검사만.
- 이벤트 전용 검사와 push ownership, callback pending/dedup/cancel 회귀를 실행한다.
- 기존 14개와 신규 이벤트 딥링크 검사를 포함한
work/tests15개를 모두 실행했다. SodaLive및SodaLive-devDebug iOS Simulator 빌드를 수행했다.- 단일 독립 리뷰와 지적 수정의 delta 승인을 같은 작업 디렉터리
reviews/에 기록했다. - P1의 제한된 실제 SDK 콜백 검증과 제품 코드의 로컬 대역 검사를 구분했다. 정상 short 실서버 성공·Notifly/AASA/실푸시·backend 검증은 미실행 후속 항목이다.
의존성 및 Progress
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을 입증하지 않는다. syntheticNSUserActivity도 지원 근거가 아니다. - 진행: 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-execdeny-network 프로필 아래/usr/bin/true는 exit 0이었지만 실제 iOS SDK와 위임된 네트워크 작업의 격리를 증명하지 않는다. SDK 로드/실행, 실제 OneLink 해석, 시뮬레이터 생성은 하지 않았다.- 결과: SDK 검증 기록, BLOCKED. 승인된 게이트 정책에 따라 Phase 2/3 제품 구현 및 완료 검증은 미착수다.
- baseline:
bash work/tests/check-app-deep-link-gift-detail.shPASS 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을 실증으로 취급하지 않는다.
- long/short URL 입력의 실제 callback과 성공/실패를 확인했다. long은 event-detail/123 반환, 공식 short 예시는 실패하여 정상 short fixture 미확보 제한을 기록했다.
- 실제 관찰에 따라 P1-GATE를 부분 입증/차단 유지로 재판정했다. 콜백 공존 문제는 확인됐지만 정상 short 응답을 추정하지 않았다.
- 소유한 앱/시뮬레이터/프로세스/임시 자원을 정리하고 증거를 reviews에 보존했다.
P1-T2 실제 SDK 실행 결과 - 2026-10-29
- 실제 SDK 검증 기록: 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를 반환했다. 오류 domaincom.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 1FAIL: decoded UDL event accepted,bash work/tests/check-event-deep-link.sh push→ exit 1FAIL: HTTPS push consumed. 같은 실제 소스의uri onelink는 assertions=188 PASS, 기존 gift/community는 assertions=279 PASS다. - 상태 교체 회귀 RED:
bash work/tests/check-event-deep-link.sh lifecycle→ exit 1FAIL: 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 1FAIL: 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-*.sh15개 중 첫 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/communityassertions=279PASS. 실제 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, 최초 REQUEST_CHANGES.
- Goal: 현재 이벤트를 포함한 연속 혼합-ID 요청이 마지막 목적지를 보존하고, 실제 route 변경 시점에 중복/오래된 요청을 걸러낸다.
- Files:
SodaLive/Sources/App/AppDeepLinkHandler.swift,SodaLive/Sources/App/AppState.swift,work/tests/EventDeepLinkCheck.swift, 필요 시 기존 mock signature만 보완. - RED: current 45에서 중간 settle 없는
44 → 45,44 → 45 → 44를 추가하고 의도한 assertion 실패를 확인한다. - GREEN: 최신 요청 유효성과 현재 목적지 검사를 실제 AppState route mutation과 동일한 main queue 블록에서 수행한다. 시간 지연/TTL이나 전역 router 재작성으로 우회하지 않는다.
- GREEN 확인: 혼합 ID, 동일 ID burst, live 확인/취소/대체, back/reopen 및 기존 279개 회귀를 재실행한다. 현재 이벤트 뒤에 live가 표시된 경우에도 기존 종료 확인 정책을 지키는지 함께 확인한다.
- 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의setAppStepoptional 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행 이동)은 보존했다.