feat: PWA FCM 클라이언트 실통합 + 어댑터 키 주입 런처
- firebase SDK 통합: initPush getToken 활성화(동적 import) + isSupported 가드 - public/firebase-messaging-sw.js 백그라운드 수신 SW(웹 config 쿼리스트링 전달) - run-api-with-adapters.sh: adapter.env.local 로드 후 ga-api bootRun - 키 파일(adapter.env.local / .env.local)은 git-ignored 템플릿으로 별도 제공 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+1037
-4
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
||||
"antd-mobile-icons": "^0.3.0",
|
||||
"axios": "^1.7.2",
|
||||
"dayjs": "^1.11.11",
|
||||
"firebase": "^12.14.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/* FCM 백그라운드 메시지 수신 서비스워커 (vite-plugin-pwa SW 와 별개).
|
||||
* Firebase 웹 config 는 정적 파일에 박지 않고, initPush 등록 시 쿼리스트링으로 전달받는다. */
|
||||
importScripts('https://www.gstatic.com/firebasejs/12.14.0/firebase-app-compat.js');
|
||||
importScripts('https://www.gstatic.com/firebasejs/12.14.0/firebase-messaging-compat.js');
|
||||
|
||||
const p = new URL(self.location).searchParams;
|
||||
firebase.initializeApp({
|
||||
apiKey: p.get('apiKey'),
|
||||
projectId: p.get('projectId'),
|
||||
appId: p.get('appId'),
|
||||
messagingSenderId: p.get('messagingSenderId'),
|
||||
});
|
||||
|
||||
firebase.messaging().onBackgroundMessage((payload) => {
|
||||
const n = payload.notification || {};
|
||||
self.registration.showNotification(n.title || '알림', {
|
||||
body: n.body || '',
|
||||
icon: '/pwa-192x192.png',
|
||||
data: payload.data || {},
|
||||
});
|
||||
});
|
||||
@@ -6,11 +6,10 @@ import { pushApi } from '@/api/push';
|
||||
* 활성 조건: 빌드 환경변수 `VITE_FCM_*` 가 모두 설정된 경우에만 동작한다.
|
||||
* 미설정 시 no-op (개발/미연동 환경에서 안전).
|
||||
*
|
||||
* <b>운영 활성화 절차</b>:
|
||||
* 1) Firebase 프로젝트 생성 → 웹 앱 등록 → 설정값을 .env 의 VITE_FCM_* 로 주입
|
||||
* 2) `npm i firebase` 후 아래 TODO 블록의 firebase/messaging 초기화 활성화
|
||||
* 3) getToken(VAPID key)으로 FCM 토큰 발급 → pushApi.register(token) 호출
|
||||
* 4) public/firebase-messaging-sw.js 서비스워커 배치
|
||||
* firebase SDK 통합 완료 — 활성 조건은 `.env(.local)` 의 VITE_FCM_* 주입뿐이다.
|
||||
* 1) Firebase 콘솔 → 웹 앱 등록 → 설정값을 .env.local 의 VITE_FCM_* 로 주입
|
||||
* 2) getToken(VAPID key)으로 FCM 토큰 발급 → pushApi.register(token) 자동 호출
|
||||
* 3) public/firebase-messaging-sw.js 가 백그라운드 수신 처리
|
||||
*
|
||||
* 서버측(PushAdapter=fcm + FCM_PROJECT_ID/FCM_ACCESS_TOKEN)과 함께 활성화해야 실제 발송된다.
|
||||
*/
|
||||
@@ -38,14 +37,38 @@ export async function initPush(): Promise<void> {
|
||||
const permission = await Notification.requestPermission();
|
||||
if (permission !== 'granted') return;
|
||||
|
||||
// TODO(운영): `npm i firebase` 후 활성화.
|
||||
// const { initializeApp } = await import('firebase/app');
|
||||
// const { getMessaging, getToken } = await import('firebase/messaging');
|
||||
// const appFb = initializeApp(cfg);
|
||||
// const messaging = getMessaging(appFb);
|
||||
// const token = await getToken(messaging, { vapidKey: cfg.vapidKey });
|
||||
// if (token) await pushApi.register(token, 'WEB');
|
||||
void pushApi; // 등록 API 는 토큰 발급 후 호출 (위 TODO 활성화 시)
|
||||
const { isSupported, getMessaging, getToken } = await import('firebase/messaging');
|
||||
if (!(await isSupported())) {
|
||||
if (import.meta.env.DEV) console.info('[push] 이 브라우저는 FCM 미지원 — skip.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { initializeApp, getApps } = await import('firebase/app');
|
||||
const appFb = getApps().length
|
||||
? getApps()[0]
|
||||
: initializeApp({
|
||||
apiKey: cfg.apiKey,
|
||||
projectId: cfg.projectId,
|
||||
appId: cfg.appId,
|
||||
messagingSenderId: cfg.messagingSenderId,
|
||||
});
|
||||
|
||||
// FCM 백그라운드 수신용 서비스워커 (vite-plugin-pwa SW 와 별개).
|
||||
// 웹 config 는 정적 SW 파일에 박지 않고 쿼리스트링으로 전달.
|
||||
const swQuery = new URLSearchParams({
|
||||
apiKey: cfg.apiKey,
|
||||
projectId: cfg.projectId,
|
||||
appId: cfg.appId,
|
||||
messagingSenderId: cfg.messagingSenderId ?? '',
|
||||
}).toString();
|
||||
const swReg = await navigator.serviceWorker.register(`/firebase-messaging-sw.js?${swQuery}`);
|
||||
|
||||
const messaging = getMessaging(appFb);
|
||||
const token = await getToken(messaging, {
|
||||
vapidKey: cfg.vapidKey,
|
||||
serviceWorkerRegistration: swReg,
|
||||
});
|
||||
if (token) await pushApi.register(token, 'WEB');
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('[push] init 실패', e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# 외부연동 어댑터 키를 주입하여 ga-api 를 기동한다.
|
||||
# adapter.env.local 의 값을 환경변수로 로드한 뒤 bootRun.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [[ ! -f adapter.env.local ]]; then
|
||||
echo "adapter.env.local 이 없습니다. 키를 먼저 채우세요." >&2
|
||||
exit 1
|
||||
fi
|
||||
set -a; source adapter.env.local; set +a
|
||||
|
||||
echo "[adapters] bank=$ADAPTER_BANK message=$ADAPTER_MESSAGE push=$ADAPTER_PUSH"
|
||||
./gradlew :ga-api:bootRun --args='--spring.profiles.active=trading_ai'
|
||||
Reference in New Issue
Block a user