diff --git a/app/firebase-messaging-sw.js/route.ts b/app/firebase-messaging-sw.js/route.ts new file mode 100644 index 0000000..1c08b04 --- /dev/null +++ b/app/firebase-messaging-sw.js/route.ts @@ -0,0 +1,49 @@ +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID, + }; + + // Firebase SDK 버전은 package.json과 일치시킵니다. (현재 12.8.0 확인됨) + const script = ` +importScripts("https://www.gstatic.com/firebasejs/12.8.0/firebase-app-compat.js"); +importScripts("https://www.gstatic.com/firebasejs/12.8.0/firebase-messaging-compat.js"); + +const firebaseConfig = ${JSON.stringify(firebaseConfig)}; + +if (!firebase.apps.length) { + firebase.initializeApp(firebaseConfig); +} +const messaging = firebase.messaging(); + +messaging.onBackgroundMessage((payload) => { + console.log("[Service Worker] 백그라운드 메시지 수신:", payload); + + const notificationTitle = payload.notification?.title || "새 알림"; + const notificationOptions = { + body: payload.notification?.body || "", + icon: payload.notification?.icon || "/logo/logo.svg", + }; + + self.registration.showNotification(notificationTitle, notificationOptions); +}); + `; + + return new NextResponse(script, { + headers: { + "Content-Type": "application/javascript", + "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate", + Pragma: "no-cache", + Expires: "0", + }, + }); +} diff --git a/components/common/FcmInitializer.tsx b/components/common/FcmInitializer.tsx index f04d0fd..19fc68d 100644 --- a/components/common/FcmInitializer.tsx +++ b/components/common/FcmInitializer.tsx @@ -6,14 +6,24 @@ import { registerServiceWorkerAndGetToken } from "@/lib/firebase"; import { api } from "@/lib/axios"; const FCM_REGISTERED_KEY = "fcm_registered"; -const PUBLIC_PATHS = ["/login", "/register", "/terms", "/privacy", "/reset"]; +const PUBLIC_PATHS = [ + "/", + "/login", + "/register", + "/terms", + "/privacy", + "/reset", +]; export default function FcmInitializer() { const pathname = usePathname(); useEffect(() => { // 공개 페이지에서는 실행 안 함 - if (PUBLIC_PATHS.some((path) => pathname.startsWith(path))) return; + const isPublicPath = PUBLIC_PATHS.some((path) => + path === "/" ? pathname === "/" : pathname.startsWith(path), + ); + if (isPublicPath) return; // 세션 당 1회만 실행 (페이지 이동마다 중복 등록 방지) if (sessionStorage.getItem(FCM_REGISTERED_KEY)) return; diff --git a/lib/axios.ts b/lib/axios.ts index 4961324..4407a2f 100644 --- a/lib/axios.ts +++ b/lib/axios.ts @@ -33,7 +33,7 @@ api.interceptors.response.use( } catch (reissueError) { // 재발급 실패 → 로그인 페이지로 리다이렉트 alert("로그인 세션이 만료되었습니다. 다시 로그인해주세요."); - window.location.href = "/login"; + window.location.href = "/"; return Promise.reject(reissueError); } } diff --git a/lib/firebase.ts b/lib/firebase.ts index 64520d1..63d132a 100644 --- a/lib/firebase.ts +++ b/lib/firebase.ts @@ -24,30 +24,6 @@ if (typeof window !== "undefined") { analytics = getAnalytics(app); } -// 서비스 워커가 active 상태가 될 때까지 기다리는 헬퍼 -function waitForServiceWorkerActive( - registration: ServiceWorkerRegistration, -): Promise { - return new Promise((resolve) => { - if (registration.active) { - resolve(registration.active); - return; - } - const sw = registration.installing ?? registration.waiting; - if (!sw) { - // fallback: navigator.serviceWorker.ready 사용 - navigator.serviceWorker.ready.then((r) => resolve(r.active!)); - return; - } - sw.addEventListener("statechange", function handler() { - if (sw.state === "activated") { - sw.removeEventListener("statechange", handler); - resolve(sw); - } - }); - }); -} - // 서비스 워커 등록 및 FCM 토큰 가져오기 export async function registerServiceWorkerAndGetToken() { if (typeof window === "undefined" || !("serviceWorker" in navigator)) { @@ -55,20 +31,11 @@ export async function registerServiceWorkerAndGetToken() { } try { - // 서비스 워커 등록 + // 서비스 워커 등록 (SW 자체에서 Firebase 초기화하므로 postMessage 불필요) const registration = await navigator.serviceWorker.register( "/firebase-messaging-sw.js", ); - // 서비스 워커가 완전히 active 상태가 될 때까지 대기 - const activeSW = await waitForServiceWorkerActive(registration); - - // 서비스 워커에 Firebase Config 전달 (active 보장 후) - activeSW.postMessage({ - type: "INIT_FIREBASE", - config: firebaseConfig, - }); - // Messaging 인스턴스 가져오기 const messaging = getMessaging(app); diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js deleted file mode 100644 index 3977375..0000000 --- a/public/firebase-messaging-sw.js +++ /dev/null @@ -1,38 +0,0 @@ -// public/firebase-messaging-sw.js - -importScripts( - "https://www.gstatic.com/firebasejs/12.8.0/firebase-app-compat.js", -); -importScripts( - "https://www.gstatic.com/firebasejs/12.8.0/firebase-messaging-compat.js", -); - -let messaging = null; - -// 메인 앱에서 Firebase Config를 받아서 초기화 -self.addEventListener("message", (event) => { - if (event.data && event.data.type === "INIT_FIREBASE") { - const firebaseConfig = event.data.config; - - if (!firebase.apps.length) { - firebase.initializeApp(firebaseConfig); - messaging = firebase.messaging(); - - // 백그라운드 알림 처리 - messaging.onBackgroundMessage((payload) => { - console.log("[Service Worker] 백그라운드 메시지 수신:", payload); - - const notificationTitle = payload.notification?.title || "새 알림"; - const notificationOptions = { - body: payload.notification?.body || "", - icon: payload.notification?.icon || "/logo/logo.svg", - }; - - self.registration.showNotification( - notificationTitle, - notificationOptions, - ); - }); - } - } -});