Repository navigation
feat(platform): App Links URI 계약·인텐트 수신·인증 후 목적지 재개 #924
Copy link
Copy link
Closed as not planned
1 / 11 of 1 issue completedClosed as not planned
1 / 11 of 1 issue completed
Copy link
Labels
area:platformCI·빌드·릴리스·저장소 운영 영역CI·빌드·릴리스·저장소 운영 영역enhancementNew feature or requestNew feature or request
Description
2026-09-08 범위 분리·착수 조건
수신자 경로의 외부 식별자·인증 관문 선행 계약은 #1951로 분리했다. 이 이슈는 Android intent 수신·인증 후 재개 구현을 계속 소유한다. 미확정 수신자 경로·관문 구현 계획은 제거하고 #1951에서 결정 근거만 추적한다. 이 이슈는 확정된 비수신자 경로의 수신·재개만 소유한다. 공통 파서 #1857의 비수신자 부분까지 카카오 서버 준비로 묶지는 않는다.
작업 유형
enhancement — 신규 기능·기존 기능 개선
주 담당 모듈
platform — Android 앱·CI·빌드·릴리스·저장소 운영
개요
목적
검증된 App Link가 앱의 정확한 목적지로 진입하고, 로그인·지문 게이트가 필요하면 원래 목적지를 보관했다가 인증 후 재개하도록 만든다.
플랫폼 소관 로컬 그래프 이관은 #1698, 최종 앱 루트 교체는 #1702로 분리했다. 이 이슈는 Nav2/Nav3 타입에 결합하지 않는 URI → 엔진 중립 목적지 계약, Android intent 수신, 인증 후 재개만 소유한다.
현재 확인된 상태
- 앱링크 도메인은
afternote.kro.kr로 확정되어 있고assetlinks.json내용 수정은 Afternote/Afternote-BE#228에서 완료됐다. - 앱
MainActivity에는 아직 앱링크용 VIEW/BROWSABLE intent-filter가 없다. - 알림 PendingIntent·warm 재진입·FCM 목적지 배선은
#1111이 별도로 소유한다. - 로컬 그래프 이관·최종 루트 전환·회귀 기준은 각각
#1698,#1702,#1601에서 추적한다.
범위
1. 확정된 공통 URI 계약 재사용
- feat(core): App Link URI 계약과 엔진 중립 목적지 파서를 세운다 #1857 / PR #1859의 기존 엔진 중립 파서·비수신자 목적지·입력 검증을 재사용한다. 새 sealed 목적지 타입이나 URI 후보 표를 중복 설계하지 않는다.
- 현재 확정된 비수신자 목적지만 Android 수신과 인증 후 재개에 연결한다.
- 수신자 식별자·관문은 #1951에서 결정 근거만 추적한다. FCM의 미확정 data 키·link 필드를 여기서 가정하지 않는다.
2. Android intent 수신
MainActivity에 필요한 VIEW/BROWSABLE/https intent-filter를 추가하고android:autoVerify를 사용한다.- cold start의 최초 intent와
onNewIntentwarm 재진입을 동일한 입력 계약으로 정규화한다. - parser와 실제 destination consumer가 준비되기 전에는 브라우저 링크를 가로채 홈으로 보내는 반쪽 활성화를 머지하지 않는다.
3. 인증 게이트 후 재개
NavigationTarget으로부터 필요한 로그인·지문 게이트를 판정한다.- 통과하지 못한 첫 게이트에서 목표를 보관하고, 성공 후 원래 목표를 정확히 한 번 재개한다.
- pending target은 프로세스 사망과 Activity 재생성을 견디며, 로그아웃·취소·만료 때 안전하게 폐기된다.
- 최종 back stack 구성은
#1702의 루트 navigator adapter가 소유하고 이 이슈는 특정 navigation engine 자료형을 노출하지 않는다.
완료 조건
- 기존 파서가 지원하는 비수신자 목적지에 대해 URI parsing·ID validation·gate 판정 단위 테스트가 있다.
- cold start와 warm
onNewIntent에서 같은 목적지로 이동한다. - 로그인·지문 각각에서 게이트 통과 후 원래 목적지를 한 번만 재개한다.
- 프로세스 재생성 후 pending target이 복원되고, 취소·로그아웃·잘못된 링크에서는 폐기된다.
- Digital Asset Links 검증과 실제 https 링크 진입을 adb 또는 실기기에서 확인한다.
- App Link parser가 Nav2/Navigation 3 구현 타입을 import하지 않는다.
- 관련 유닛·Robolectric·androidTest와 전체 CI가 통과한다.
범위 밖
- 플랫폼 소관 로컬 그래프 이관:
#1698 - 앱 루트
NavDisplay교체:#1702 - 수신자 경로·인증 관문의 미확정 구현: #1951에서 결정만 추적하고 확정 후 필요한 작업을 별도로 정한다.
- FCM payload 키·PendingIntent·알림 탭 목적지 연결:
#1111, [feat] FCM 푸시 발송 계약 확정 — payload 형태(notification vs data-only)와 목적지 data 키 Afternote-BE#261 - 각 feature 내부 Navigation 3 구현:
#1695,#1696,#1697,#959
자식 이슈
No response
참고
- Navigation 3 분리 이슈: refactor(platform): 소관 그래프를 Navigation 3 로컬 스택으로 이관 #1698, refactor(platform): 앱 루트를 Navigation 3 NavDisplay로 전환 #1702
- 알림 목적지: 알림 탭이 목적지로 갈 수 없는 구조 — MainActivity onNewIntent 부재, PendingIntent 빌더 2벌 중복 #1111
- App Links 인프라: [fix] assetlinks.json 패키지명 불일치 — 패스키 검증 불가 + 앱링크 relation 부재 Afternote-BE#228
- 이 본문은 2026-09-01 역할 분리 이후의 정본이다. 이전 댓글의 Nav3 비용·버전·전환 순서 기록은 역사적 근거로만 남긴다.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:platformCI·빌드·릴리스·저장소 운영 영역CI·빌드·릴리스·저장소 운영 영역enhancementNew feature or requestNew feature or request
Activity
1hyok commentedon Aug 23, 2026
본문 사실 검증 실측 (커밋
104462fd기준 전수 대조)결과: 정정 1건 외 전부 일치.
일치 확인 —
AppNavigationActions.kt577줄 /override fun101개, 분류 46·27·11·17(물리적 한 줄 기준으로 정확히 재현), popUpTo 11곳 전부 1~2줄 해설 주석 동반, 인증 게이트 3중 라인 참조(MainActivity#L45 · AfternoteNavGraph#L35 · ReceiverNavGraph#L88), FCMgetLaunchIntentForPackage()L78–93 · 목적지 없음,navDeepLink0건(VIEW 필터는 카카오 SDK 별도 액티비티뿐), nestednavigation<>()6개, 그래프 스코프 공유 VM 7종(AfternoteHost · DeliveryVerificationFlow · SignUp · FindId · Setting · TimeLetterWrite · Timeletter — 에디터 VM은 자기 엔트리 스코프라 제외가 타당), MindRecord 파일럿 적합성(popUpTo 0 · 공유 VM 0), Manifest L64–68, toml L18.정정 1건 —
hiltViewModel()59곳 → 58곳 (무인자 49 + 부모/엔트리 인자 9. 주석 언급 3줄 중 하나를 세면 59가 나오는 집계 착오). 본문에 반영.확인 필요 항목 해소 — Navigation 3 은 1.0.0 stable 2025-11 출시, 현 stable 1.1.6 (2026-08-12). "alpha 면 3단계 보류" 조건 미발동. 본문 체크박스에 반영.
참고: "나머지 17" 중 8개는 여러 줄로 포맷된 navigate 위임이라 순수 위임 비율은 실제 80%(81/101) — 본문 72% 주장은 보수적으로 성립.
1hyok commentedon Aug 23, 2026
1단계(mindrecord 파일럿)를 #959 로 분리
이유: 마음의 기록은 모듈 소관이 분리돼 있어(0823 이관) 파일럿 대상 코드의 소유자와 이 이슈의 담당자가 다르다. #924 본체(앱링크 · 인증 게이트 3중 · FCM 목적지 · 전면 전환 설계)는 코어/내비게이션 소관으로 남기고, 파일럿 실행만 해당 모듈 소관으로 넘긴다.
인계한 것 — 착수 전 전환 가능성을 확인하려고 프로토타입을 만들어 에뮬레이터로 돌려봤고, 그 결과를 #959 본문에 정리해 뒀다.
NavDisplay.onBack시그니처,entryDecorators기본값 대체,DiaryWriteViewModel의 SavedStateHandle → assisted 주입 전환미검증 1건: predictive back 미리보기 애니메이션 — 실기기 확인 필요.
2단계(그래프 스코프 VM 7곳 이관 설계)는 #959 결과를 재료로 이 이슈에서 계속 판단한다.
1hyok commentedon Aug 23, 2026
0단계 — URI 스키마 설계안 (FE 안, BE 협의 대기 항목 명시)
1단계가 #959 로 분리돼 이 이슈에 남은 선행 작업이 0단계다. 착수하며 FCM 현황을 BE 까지 실측했고, 본문 전제 하나가 실제와 달랐다.
실측 정정 — "FCM 이 반쪽으로 굴러간다" 가 아니라 "아직 안 굴러간다"
Afternote-BEHEAD, 381개 java 전수)getLaunchIntentForPackage)AdminPushTestService1곳뿐 —Map.of("type","TEST"), 관리자 테스트용DeliveryNotificationService→JavaMailSender)FcmPushSender.sendToUser(userId, title, body, Map<String,String> data)즉 사용자에게 도달하는 실서비스 푸시가 아직 없다. 반대로
data맵 통로는 이미 있어서, 목적지를 싣는 데 BE 인프라 변경은 필요 없다 — 키 계약만 합의하면 된다. 이 이슈의 "첫 수혜자" 논거는 유지되지만, 순서는발송 지점 신설(BE)→목적지 파싱(FE)이다.설계 1 — 앱링크와 FCM 이 같은 URI 파서를 공유한다
FCM
data에 목적지를 타입·ID 쌍으로 싣지 않고 URI 문자열 하나로 싣는다.{ "link": "https://<domain>/afternote/42", "type": "AFTERNOTE_DELIVERED" }link— 앱링크로 열리는 것과 완전히 동일한 URI. FE 는 인텐트 출처(브라우저 VIEW / 알림 탭)와 무관하게 파서 하나만 유지한다.type— 분기 아닌 분석·로깅용 선택 필드. 목적지 판단은link단독으로 끝낸다(두 필드가 갈라지면 진실이 둘이 된다).link부재·파싱 실패 시 현행처럼 홈으로 (하위호환).설계 2 — 경로 표
도메인은 미확정(BE 협의). 경로는 화면 IA 기준.
/afternote/afternote/{id}/received/received/senders/{senderId}/received/afternote/{id}/timeletter/{id}/mindrecord/daily-question/settings/notification게이트 열은 #924 본문의 인증 3중 구조를 그대로 반영한 것으로, Nav2 로는 이 표의 굵은 항목을 표현할 수단이 없다는 게 Nav3 논거의 핵심이다.
설계 3 — 게이트 통과 후 재개 (Nav3 전제)
pendingTarget은 프로세스 사망을 넘겨야 하므로rememberSaveable/SavedStateHandle보관 대상이다.설계 4 — 매퍼는 테스트가 필수다
Nav2
navDeepLink<T>의 타입 매칭이 사라지는 대가로, 라우트 이름이 바뀌어도 컴파일 에러 없이 런타임에 조용히 깨진다. 경로 표의 모든 행에 대해uri → 백스택왕복 단위 테스트를 매퍼와 같은 PR 에 넣는다.BE 협의 항목 (이 이슈의 실제 블로커)
assetlinks.json을 어느 호스트/.well-known/에 올릴지. 현행 API 호스트(afternote.kro.kr) 재사용 여부.data키 계약 — 위link/type.data.link동봉.3번이 없으면 FE 가 목적지 파싱을 붙여도 검증할 대상이 없다. BE 이슈로 분리해
blocked_by로 건다.FE 선행 가능 (도메인 무관)
MainActivityVIEW/BROWSABLE intent-filter 골격 — 현재 LAUNCHER 하나뿐. 호스트만 나중에 채움. (카카오 OAuth 필터는 별도 액티비티라 무관)1hyok commentedon Aug 23, 2026
0단계 블로커 실측 —
assetlinks.json은 이미 떠 있고, 대신 내용이 틀렸다본문의 "
assetlinks.json호스팅 → 백엔드·인프라 의존, 도메인 확정 필요" 를 실측했다. 호스팅·도메인은 이미 해결돼 있었다 — 패스키(Afternote/Afternote-BE#133)가 같은 파일을 필요로 해서 먼저 올라갔다.즉 도메인 확정 대기는 해제다.
afternote.kro.kr을 그대로 쓰면 된다.다만 내용에 두 가지 문제가 있어 그대로는 앱링크가 동작하지 않는다:
package_name이com.afternote.app— 실제는com.afternote.afternote_fe(applicationId·adb pm list2중 확인). 이 상태로는 패스키(패스키 로그인 — Credential Manager 통합 인증 연동 #764)도 검증이 깨진다get_login_creds뿐 — 앱링크에 필요한handle_all_urls없음→ Afternote/Afternote-BE#228 로 분리해 걸었다. 이 이슈의 0단계는 그 결과에
blocked_by.갱신된 0단계 상태
afternote.kro.kr확정assetlinks.json호스팅data키 계약link— BE 인프라 변경 불필요(sendToUser가 이미Map수신)FE 선행 가능해진 것
도메인이 확정됐으므로
MainActivityVIEW/BROWSABLE intent-filter 와 URI↔NavKey 매퍼(+테스트)를 BE#228 과 병렬로 작성할 수 있다. 다만 매퍼가 만드는 백스택을 실제로 태우려면 Nav3 전환(2·3단계)이 선행돼야 하므로, 매퍼 단독 머지는 죽은 코드가 된다 — BE#228 착지 + #959 결과를 보고 순서를 정한다.1hyok commentedon Aug 27, 2026
2026-08-28 최신 공식 가이드 기준 결정 보강
결론
1.1.7이고, 백스택을 앱 상태로 직접 모델링하는 이점이 마이그레이션 비용 없이 바로 적용된다.popUpTo정책·그래프 스코프 ViewModel·TestNavHostController테스트의 이관 비용과, 아래 공식 가이드 미지원 범위가 겹치기 때문이다.새 공식 마이그레이션 가이드와 현재 코드 대조
공식 가이드: https://developer.android.com/guide/navigation/navigation-3/migration-guide
compileSdk >= 36,minSdk >= 23은 현재 프로젝트(37/26)가 충족한다.Root → Route.Afternote → EditorFlow,Root → Route.Receiver → DeliveryVerificationFlow구조와 flow-scoped ViewModel을 사용하므로 공식 절차를 기계적으로 적용할 수 없다.전환 전 회귀 기준으로 추가할 테스트
pendingTarget복원버전 참고:
1.1.7: https://developer.android.com/jetpack/androidx/releases/navigation32.10.0(현재 프로젝트2.9.8): https://developer.android.com/jetpack/androidx/releases/navigation1hyok commentedon Aug 27, 2026
블로커와 무관한 단순
back()위임 통합은 #1301로 분리했습니다. #924에서는 전환 판단과 의존 관계만 유지하고, 독립 구현·검증은 #1301에서 추적합니다.1hyok commentedon Aug 27, 2026
단순 back 위임 작업 소유권 분리
이전 코멘트에서 #1301 하나로 연결한 범위를 실제 담당자 기준으로 바로잡았습니다.
세 기능을 함께 구현했던 #1308은 독립적인
1hyok범위가 남지 않아 미병합으로 닫고, 원격 브랜치와 로컬 worktree·브랜치도 삭제했습니다. 해당 diff는 재사용하지 않으며 #924에서는 #1301과 #1311을 독립 작업으로 추적합니다.16 remaining items
1hyok commentedon Sep 13, 2026
이 PR 이 닫는 몫과 떼어낸 몫
브랜치
feat/924-app-links-receive의 커밋c896a857이 이 이슈의 코드 범위를 끝낸다. PR 은Closes #924로 연다. 대표 이슈는 그 형태로만 걸 수 있으므로(#1748), 이 PR 이 닫지 못하는 몫은 산문에 남기지 않고 이슈로 뗐다.이 PR 이 닫는 것
AfternoteAppLinkParserTest16건,NavigationTargetContractTest7건onNewIntent가 같은 목적지AppLinkIntentContract하나로 합치고AppLinkIntentContractTest4건MainViewModelAppLinkTest8건,AppLinkResumeEffectTest3건MainViewModelAppLinkTest의 SavedState·로그아웃 케이스core:common의 deeplink 패키지 import 는java.net.URI하나다목적지는 다섯이다.
/,/afternote,/timeletter/{id},/mindrecord/daily-question,/settings/notification. 관문 판정은 하드코딩하지 않고 목적지가 싣고 온requiredGates를 읽는다. 로그인은 앱 셸이, 지문은 목적지 그래프 자신이 지킨다.떼어낸 것
assetlinks.json을 고쳐야 한다. 지금 그 파일은 HTTP 200 이고 패키지명과 relation 둘 다 맞지만(2026-09-13 22:36 KST 조회), 게시된 서명 지문 두 개가 [fix] assetlinks.json 패키지명 불일치 — 패스키 검증 불가 + 앱링크 relation 부재 Afternote-BE#228 이 고치기 전 값 그대로다. 그 이슈는 지문 재확인을 FE 몫으로 남긴 채 닫혔다./afternote/{id}목적지를 갖는다. 이 PR 은 그 경로를 계약에서도 파서에서도 intent-filter 에서도 열지 않고, 파서 테스트가UNKNOWN_PATH거절을 못으로 박아 뒀다. 자리를 열려면AfternoteNavHost가 초기 엔트리를 받아야 하는데, 그 파일을 열린 PR 세 건(refactor(afternote): 편집기 외 ViewModel 8개 MVI 전환 (#1968) #2021, refactor(core): 내비게이션 콜백 이름 전환 분리 (#1999) #2001, refactor(setting): 설정 로컬 Navigation 3 스택 이관 (#1695) #1962)이 물고 있어 이 PR 의 범위에서 뺐다.둘 다 이 이슈를
blocked_by로 걸어 뒀다. 후속 분리분이라 자식으로 매달지는 않았다.두 몫을 한 이슈로 묶지 않은 이유는 걸리는 지점이 다르기 때문이다. #2087 의 선행은 서명 지문과 기기이고, #2088 의 선행은 열린 PR 세 건이다. 묶으면 둘 중 하나가 다른 하나를 기다리게 된다.
refactor(platform): App Link intent 계약에서 던지지 않는 getter 의 runCatching …
docs(platform): App Link 파서의 userinfo 거절 근거를 위장 방어에서 정규형으로 바로잡는다 (#924)
refactor(platform): App Link 거절 사유에서 userinfo·포트를 host 와 떼어 낸다 (#924)
docs(platform): 세그먼트 파싱의 null 과 빈 목록이 뜻하는 바를 KDoc 에 적는다 (#924)
refactor(platform): App Link ID 의 0 검사를 앞자리 0 거절 하나로 합친다 (#924)
refactor(platform): 발급처가 없는 타임레터 상세 App Link 경로를 계약에서 뺀다 (#924)
fix(platform): 인자가 다른 같은 화면 위에서도 App Link 목적지를 새로 쌓는다 (#924)
docs(platform): App Link KDoc 에서 서버가 정하지 않은 FCM 링크 전제를 걷어 낸다 (#924)
1hyok commentedon Sep 26, 2026
보류로 닫는다. 무엇을 만들지가 서버와 PM 결정에 달려 있다.
origin/main7c961ec 기준 서버가 만드는 링크는 초대 링크/invitations/{token}하나).구현 PR #2093 은 머지하지 않고 닫았다(브랜치
feat/924-app-links-receive보존). 결정이 나면 그 계약으로 남은 범위만 새 이슈로 연다. 그때는 파서를 직접 짜지 않고 루트 엔진의 공식 딥링크 API(Nav2navDeepLink또는 Navigation 3 1.2.0DeepLinkMatcher)로 받는다. 이 이슈를 기다리던 #2087, #2088 도 같은 이유로 닫는다.