Repository navigation
알림 탭이 목적지로 갈 수 없는 구조 — MainActivity onNewIntent 부재, PendingIntent 빌더 2벌 중복 #1111
Description
📜 Overview (Required)
알림 2종(일일 알림 · FCM)이 각각 PendingIntent 를 만들지만 둘 다 «앱을 띄우는» 것 말고는 아무것도 못 한다. 목적지를 실어 보낼 수단도, 받을 수단도 없다.
받는 쪽인 MainActivity 에 onNewIntent 가 없고, 보내는 쪽 빌더는 두 모듈에 복붙 2벌로 갈려 있다. #924 (앱링크 전제 Nav3 전환) 가 «FCM 목적지 미연결» 을 근거로 들고 있는데, 그 목적지 배선이 Nav 방식과 무관하게 지금 구조에서는 원천적으로 불가능하다.
현황 1 — 받을 구조가 없다
MainActivity.kt (총 50줄)
| 항목 | 상태 |
|---|---|
onNewIntent 오버라이드 |
없음 |
intent 를 읽는 곳 |
L27 BuildConfig.DEBUG 전용 EXTRA_DEBUG_START_TIMELETTER 하나뿐 |
매니페스트 launchMode (L75-86) |
미지정 = standard |
intent-filter |
MAIN / LAUNCHER 만 (딥링크 없음 — 카카오 OAuth VIEW 는 SDK 액티비티 몫) |
앱이 이미 떠 있는 상태에서 알림을 누르면 새 Intent 는 onNewIntent 로 도착하는데, 그걸 받는 코드가 없다. 즉 목적지 파라미터를 보내더라도 읽을 지점이 존재하지 않는다.
현황 2 — 보내는 쪽이 2벌, 둘 다 목적지 없음
동일 로직이 모듈을 건너 복붙돼 있다.
| 위치 | 함수 |
|---|---|
DailyNotificationWorker.kt#L59-L74 (core:common) |
buildContentPendingIntent() |
AfternoteFirebaseMessagingService.kt#L78-L93 (app) |
buildContentPendingIntent(notificationId) |
양쪽 모두 getLaunchIntentForPackage() + FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TOP + FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT 로 완전히 동일하다. 차이는 requestCode 뿐 (Worker 는 상수 0, FCM 은 messageId.hashCode()).
FCM 쪽은 RemoteMessage.data 를 이미 받고 있으면서도 FcmNotificationContentResolver 가 title · body 만 꺼내 쓰고 목적지 키는 보지 않는다.
현황 3 — standard + CLEAR_TOP 조합 (추론, 착수 시 실측)
MainActivity 는 launchMode 가 standard 인데 알림 인텐트에는 FLAG_ACTIVITY_CLEAR_TOP 이 붙는다. SINGLE_TOP 없이 이 조합이면 대상 액티비티가 finish 후 재생성되는 것이 문서상 동작이고, 이 앱은 단일 Activity + Compose Navigation 이라 재생성 = Compose 백스택 전체 소실이다.
다만 ACTION_MAIN / CATEGORY_LAUNCHER 인텐트는 시스템이 «태스크 재개» 로 달리 처리할 여지가 있어, 이 항목은 코드 정독 기반 추론이다. 착수 시 에뮬레이터 실측으로 확정한다.
현황 1·2 는 실측 불필요한 확정 사실이다.
작업
PendingIntent빌더를 1벌로 통합 — 목적지(route/파라미터)를 extra 로 실을 수 있는 형태로.core:common은app에 의존할 수 없으므로 경계 설계 필요 (현재getLaunchIntentForPackage()우회의 이유)MainActivity에onNewIntent+ 인텐트 → 목적지 해석 경로 추가launchMode와 인텐트 플래그 조합을 실측 결과에 맞춰 확정 (singleTop채택 여부 포함)- FCM
data페이로드의 목적지 키 계약을 BE 와 확정하고FcmNotificationContentResolver확장 requestCode충돌 정리 — Worker 상수0과 FCMhashCode()의 동일 값 충돌 가능성
완료 조건
- 앱이 꺼진 상태 / 백그라운드 / 포그라운드 3개 상태에서 알림 탭 → 지정 목적지 도달을 각각 실측
- 백그라운드에서 탭했을 때 기존 화면 스택이 유지되는지 확인 (현황 3 실측 결과 반영)
- 빌더가 1벌만 남고 두 알림 경로가 그것을 공유
- 목적지 키가 없거나 해석 실패한 알림은 홈으로 폴백 (크래시·빈 화면 금지)
📌 Child Issue (Optional)
No response
📍 Note (Optional)
- base =
develop(기준 리비전c897b708) - feat(platform): App Links URI 계약·인텐트 수신·인증 후 목적지 재개 #924 와의 관계: feat(platform): App Links URI 계약·인텐트 수신·인증 후 목적지 재개 #924 는 «Nav3 로 갈지» 를 검토하는 이슈고, 이 이슈는 어떤 Navigation 을 쓰든 필요한 인텐트 수신 인프라다. feat(platform): App Links URI 계약·인텐트 수신·인증 후 목적지 재개 #924 가 그리는 «URI → 백스택» 의 앞단에 해당하므로 선행으로 두는 편이 자연스럽다 — Nav3 전환 결론이 나기 전에도 독립 진행 가능
- 4번(FCM data 목적지 키)은 BE 계약이 필요하다. 계약 확정 전이면 1·2·3·5 만 먼저 착수하고 4는 분리한다
- 관련: feat(afternote): 추억 노트 미디어 카메라/녹음 즉석 촬영 인텐트 추가 #369 (촬영 인텐트 — 별개 범위)
Activity
OnlyReadOneBook commentedon Aug 27, 2026
선행 인프라 범위는 #1309로 분리했습니다.
BE FCM 목적지 key/value·route parameter schema와 data-only 등 background 전달 방식이 확정되지 않아 해당 값은 추측하지 않았습니다. 실제 시스템 알림 탭 기반 cold/background·Activity 재생성·기존 back stack 보존 실측도 이 이슈의 후속 범위로 남깁니다.
OnlyReadOneBook commentedon Aug 27, 2026
BE 목적지 계약과 무관한 공용 PendingIntent identity·MainActivity cold/warm 수신·Activity 재사용 검증 범위를 #1310으로 분리했습니다. #1111에는 FCM data 목적지 계약과 실제 feature 라우팅을 남깁니다.
[-]fix(app): 알림 탭이 목적지로 갈 수 없는 구조 — MainActivity onNewIntent 부재, PendingIntent 빌더 2벌 중복[/-][+]알림 탭이 목적지로 갈 수 없는 구조 — MainActivity onNewIntent 부재, PendingIntent 빌더 2벌 중복[/+]OnlyReadOneBook commentedon Aug 29, 2026
백그라운드 FCM payload 형태(notification vs data-only)와 목적지 data 키 계약은 BE 쪽에 기록이 없어 Afternote/Afternote-BE#261 로 신설했다(assignee Hwanggyuun). 이 이슈의 잔여 범위 중 «BE 목적지 계약 → 내부 NotificationIntentContract 변환» 은 그 결정이 나온 뒤에야 착수 가능하다.
근거(2026-08-29 BE main
aba24df실측) —FcmPushSender.sendOne이setNotification(...)을 무조건 세팅하므로 앱이 백그라운드면 SDK 가 트레이에 직접 그리고onMessageReceived()를 건너뛴다. 즉 #1310/PR #1312 가 붙인 진입 인프라는 현재 계약에서 포그라운드 전용이다. 실서비스 발송 트리거는 BE 에 아직 없고 호출부는AdminPushTestService(data={"type":"TEST"}) 하나뿐이다.OnlyReadOneBook commentedon Aug 29, 2026
실측 갱신 (2026-08-30,
origin/develop) — 본문의 「현황」 두 항목은 해소됐다이 이슈 본문은
c897b708기준이라 지금 상태와 어긋난다. 다음 사람이 낡은 현황을 믿고 재조사하지 않도록 기록해 둔다.현황 1 「받을 구조가 없다」 — 해소
MainActivity.kt:55에onNewIntent오버라이드가 있다. 인텐트를 읽어 해석하는 내부 계약도app/.../notification/NotificationIntentContract.kt로 서 있다 —NotificationEntrySource(daily · fcm)와NotificationEntryRequest(source + occurrence, 중복 소비를 막는identityKey포함).현황 2 「보내는 쪽이 2벌」 — 해소
core/common/.../notification/NotificationPendingIntentFactory.kt한 벌로 모였고, 일일 알림(DailyNotificationWorker)과 FCM(AfternoteFirebaseMessagingService)이 그것을 공유한다.남은 것 — 4번(목적지 키)뿐이고 BE 계약 대기다
NotificationIntentContract의 KDoc 이 경계를 명시하고 있다.그 계약은 Afternote/Afternote-BE#261(payload 형태 + 목적지 data 키)이 추적한다. 원 본문의 「4번은 BE 계약이 필요하다. 계약 확정 전이면 1·2·3·5 만 먼저 착수하고 4는 분리한다」에서 앞부분이 끝나고 4만 남은 상태다.
도달 자체가 아직 안 되는 문제는 BE#266 이 따로 본다.
이 이슈는 그 잔여(목적지 배선 + 3개 앱 상태 실측)를 계속 추적한다.
OnlyReadOneBook commentedon Aug 30, 2026
「작업」·「완료 조건」 전 항목 재판정 (2026-08-30,
origin/developfc8f32cd5)앞 코멘트는 본문 「현황 1·2」만 갱신했다. 이번엔 「작업」 1~5 와 「완료 조건」 4개를 같은 리비전에 대고 전수 대조했다. 결론 — 인프라 축은 전부 닫혔고, 남은 건 «목적지» 한 축이다. 그리고 그 한 축을 배선할 코드 자리가 지금 열린 PR 에 잡혀 있어 이번엔 코드를 넣지 않는다.
「작업」 항목별
PendingIntent빌더 1벌 통합PendingIntent.getActivity호출부는core/common/.../notification/NotificationPendingIntentFactory.kt:501건.af909a635(#1310)create(context, source, occurrenceToken)에 목적지 파라미터가 없고, extras 는ENTRY·SOURCE·OCCURRENCE_TOKEN3개뿐이다 (NotificationPendingIntentFactory.kt:24-26)MainActivity.onNewIntentMainActivity.kt:55. cold 진입은onCreate의enqueueNotificationIntent(intent)(L29) 가 같은 경로로 흡수한다launchMode· 인텐트 플래그 조합 확정AndroidManifest.xml의.MainActivity블록에launchMode여전히 미지정 =standard), 알림 인텐트에만NEW_TASK | CLEAR_TOP | SINGLE_TOP을 건다 (NotificationPendingIntentFactory.kt:42-46, 결정 근거는 같은 파일 KDoc L15-18). 본문 「현황 3」이 우려한 finish-후-재생성은SINGLE_TOP추가로 닫혔다data목적지 키 계약 +FcmNotificationContentResolver확장title·body만 읽는다 (AfternoteFirebaseMessagingService.kt의FcmNotificationContentResolver). 계약은 Afternote/Afternote-BE#261 (OPEN, 코멘트 0). 이 이슈에blocked_by로 이미 걸려 있다requestCode충돌 정리0하나로 모으고,PendingIntent동일성은 action 에 길이 prefix 로 넣은source+occurrence로 가른다 (NotificationPendingIntentFactory.kt:59-70). 회귀 가드는NotificationNavigationAndroidTest#sameRequestCode_differentSourceAndOccurrenceKeepLatestExtras본체 — 「extras 를 읽기만 하고 아무 데도 안 보낸다」가 실측된다
MainActivity가 extras 를 파싱해MainViewModel.enqueueNotificationEntry()로 넣는 데까지는 간다. 그런데 그 큐를 꺼내 쓰는 코드가 프로덕션에 0건이다.main 소스셋 소비자 0건이고
consumeNotificationEntry()도 테스트에서만 불린다.AppNavigation·AppState어디에도 알림 기반 이동이 없다. 즉 알림을 탭하면 앱이 뜨고 이벤트가 큐에 들어간 뒤 그대로 멈춘다. 목적지 이동은 한 줄도 없다.이건 낡은 정보가 아니라 #1310 이 의도적으로 그은 경계다 —
NotificationIntentContractKDoc 이 «실제 화면 목적지는 #1111의 BE payload 계약이 확정된 뒤 별도 adapter에서 추가한다» 고 명시한다. 아직 안 한 것이고, 그게 이 이슈에 남은 본체다.「완료 조건」 항목별
DailyNotificationWorker·AfternoteFirebaseMessagingService가 같은 팩토리를 부른다NotificationNavigationAndroidTest#backgroundNotification_resumesSameActivityAndKeepsCurrentScreen— PR #1576 의Pixel 2 API 34 accessibility smoke에서 실패 중이다(기대 실패 목록에 없는 androidTest 실패). 별도로 진단 중NotificationColdStartAndroidTest(cold)·NotificationNavigationAndroidTest(warm·background)가 덮는다지금 코드를 넣지 않는 이유 — 착수 순서
남은 몫을 배선하려면 손댈 자리가 둘이고, 각각 파일이 정해져 있다.
core/common/.../NotificationPendingIntentFactory.kt) + 두 생산자(core/common/.../DailyNotificationWorker.kt,app/.../messaging/AfternoteFirebaseMessagingService.kt)app/.../notification/NotificationIntentContract.kt에 목적지 파싱·whitelist·Home 폴백, 그리고 그것을AppNavigation으로 잇는 adapter1·2 의 4개 파일이 전부 열린 PR #1576(
refactor/1575— 알림 발생 식별자occurrenceToken→occurrenceId)의 변경 파일이다. 회귀 가드인NotificationNavigationAndroidTest.kt·NotificationIntentContractTest.kt·MainViewModelNotificationTest.kt도 마찬가지다. 지금 같은 파일을 고치면 순수 리네임 PR 과 정면 충돌하고, 위 red 테스트까지 겹쳐 진단이 꼬인다.그래서 착수 순서를 이렇게 둔다.
내부 배선(1·2)만 떼어 BE#261 전에 세울 수도 있지만, whitelist 에 담을 값이 BE#261 이 정하는 것이라 껍데기만 남는다. 게다가 목적지 이동을 넣는 순간 위
backgroundNotification_...KeepsCurrentScreen가드(«알림을 받아도 현재 화면을 유지한다» = #1310 단계의 계약)를 이 이슈 계약(«지정 목적지로 이동한다»)으로 갈아끼워야 한다. 그 파일이 지금 #1576 과 red 진단이 동시에 잡고 있는 파일이라, 순서를 지키는 편이 싸다.곁가지
도달 자체(발송 200 인데 기기
NotificationRecord0건)는 Afternote/Afternote-BE#266 이 따로 본다. 그게 풀리기 전에는 FCM 경로의 3개 상태 실측이 물리적으로 불가능하다 — 실측 항목은 BE#266 뒤에 놓인다.OnlyReadOneBook commentedon Aug 30, 2026
목적지 배선을 PR #1641 로 올렸다 — 이 이슈는 계속 열어 둔다
앞 코멘트(「「작업」·「완료 조건」 전 항목 재판정」)가 「본체」로 지목한 「extras 를 읽기만 하고 아무 데도 안 보낸다」 를 닫는다. base 는
develop124547cb.넣은 것
core/common/.../notification/NotificationDestination.kt(신규)home·mind_record·time_letter·afternoteNotificationPendingIntentFactorydestination필수 파라미터 +EXTRA_NOTIFICATION_DESTINATION(= 「작업」 1번 뒤 절반)DailyNotificationWorkerMIND_RECORDNotificationIntentContractapp/.../navigation/NotificationEntryEffect.kt(신규)AppState.navigateToBottomBarRoute(= 「작업」 2번 뒤 절반)목적지 집합은 바텀바 최상위 4개로 제한했다. 알림은 앱 밖에서 오는 입력이라 어떤 화면이든 열 수 있게 두면 인자·권한 관문을 우회하는 통로가 된다. 그 경계를
NotificationDestinationRouteTest가 고정한다.launchMode는 그대로 뒀다(「작업」 3번은 #1310 이 이미 닫은 판정 유지). 「완료 조건」의 홈 폴백도 이 PR 이 닫는다.남는 것 — 이 이슈를 여는 이유
data목적지 키. [feat] FCM 푸시 발송 계약 확정 — payload 형태(notification vs data-only)와 목적지 data 키 Afternote-BE#261 대기 그대로다. 키 이름을 추측해 두면 BE 가 다른 이름을 고르는 순간 조용히 죽는 분기가 남는다. FCM 은HOME을 명시하고, 계약이 서면FcmNotificationContentResolver에서 data 를 읽어NotificationDestination으로 옮기면 된다 — 그 자리는 이 PR 이 만들어 뒀다.그래서 PR 본문은
Closes가 아니라Refs다. 열린 blocked_by 두 건(BE#261 · #1575)이 있어 merge-order-guard 가 closing 링크를 막기도 한다.앞 코멘트가 적어 둔 착수 순서와 어긋난 점
앞 코멘트는 「PR #1576 머지 → BE#261 확정 → 구현」 순서를 제안했다. 이번엔 그 중 BE 계약과 무관한 절반만 먼저 넣었다 — whitelist 에 담을 값이 FE 자체 화면이라 BE#261 없이도 껍데기가 아니고(일일 알림은 실제로 마음의 기록으로 간다), FCM 쪽만 키 계약을 기다리면 되기 때문이다.
#1576 과는 6개 파일이 겹친다. 의존이 아니라 텍스트 충돌이라 스택으로 올리지 않았고, 먼저 머지되는 쪽 뒤에 나머지가 rebase 한다. 충돌 지점은 전부 같은 시그니처 줄이라 기계적이다.
#1310 단계가 세운 계측 가드 2건(
backgroundNotification_...KeepsCurrentScreen·warmNotificationWhileLoggedIn_preservesExistingNavHost)은 계약이 「현재 화면 유지」에서 「목적지 이동」으로 바뀌므로 이름과 단언을 함께 갈아끼웠다. Activity 재사용(assertSame) 단언은 그대로 남긴다.OnlyReadOneBook commentedon Sep 2, 2026
「내부 목적지 축」(vocabulary·팩토리 destination·수신 파싱·홈 폴백·NavHost 연결·계측 5건)을 #1727 로 분리했고 PR #1641 이 그 이슈를 Closes 합니다.
이 이슈에는 작업 4(FCM
data목적지 키 + Resolver 확장, Afternote-BE#261 대기)와 실기기 3개 상태 도달 실측(FCM 은 BE#266 닫힘 이후 도달 재확인)·cold 도달 단언이 남습니다.blocked_byBE#261 은 유지합니다.