Merge branch 'fe/feat/fcm-notification' into 'fe/develop'

Feat: FCM 알림 기능 추가

See merge request s11-s-project/S11P21S002!207
This commit is contained in:
조현수 2024-09-27 09:22:45 +09:00
commit 8da76afa9c
9 changed files with 1176 additions and 17 deletions

View File

@ -15,6 +15,10 @@
</head>
<body>
<div id="root"></div>
<script
type="module"
src="/serviceWorkerRegistration.js"
></script>
<script
type="module"
src="/src/main.tsx"

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
"axios": "^1.7.5",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"firebase": "^10.13.2",
"konva": "^9.3.14",
"lucide-react": "^0.436.0",
"react": "^18.3.1",

View File

@ -0,0 +1,49 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-undef */
importScripts('https://www.gstatic.com/firebasejs/8.7.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.7.1/firebase-messaging.js');
const firebaseConfig = {
apiKey: 'AIzaSyBQx50AsrS3K687cGbFDh1908ClCLFmnhA',
authDomain: 'worlabel-6de69.firebaseapp.com',
projectId: 'worlabel-6de69',
storageBucket: 'worlabel-6de69.appspot.com',
messagingSenderId: '124097400880',
appId: '1:124097400880:web:022db3cdc0bdea750c5df5',
measurementId: 'G-KW02YRYF5H',
};
self.addEventListener('install', (_) => {
self.skipWaiting();
});
self.addEventListener('activate', (_) => {
console.log('FCM 서비스 워커가 실행되었습니다.');
});
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
const notificationTitle = payload.data.title;
const notificationOptions = {
body: payload.data.body,
icon: payload.data.image,
data: {
url: payload.data.url, // 알림 클릭시 이동할 URL
},
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
// 알림 클릭 이벤트 처리
self.addEventListener('notificationclick', (event) => {
event.notification.close(); // 알림 닫기
// 알림에서 설정한 URL로 이동
const clickActionUrl = event.notification.data.url;
if (clickActionUrl) {
event.waitUntil(clients.openWindow(clickActionUrl));
}
});

View File

@ -0,0 +1,61 @@
/* eslint-disable no-undef */
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.1.3/firebase-app.js';
import { getMessaging, getToken } from 'https://www.gstatic.com/firebasejs/9.1.3/firebase-messaging.js';
const firebaseConfig = {
apiKey: 'AIzaSyBQx50AsrS3K687cGbFDh1908ClCLFmnhA',
authDomain: 'worlabel-6de69.firebaseapp.com',
projectId: 'worlabel-6de69',
storageBucket: 'worlabel-6de69.appspot.com',
messagingSenderId: '124097400880',
appId: '1:124097400880:web:022db3cdc0bdea750c5df5',
measurementId: 'G-KW02YRYF5H',
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
const registerServiceWorker = async () => {
if (!('serviceWorker' in navigator)) {
console.warn('현재 브라우저에서 서비스 워커를 지원하지 않습니다.');
return;
}
try {
console.log('FCM 서비스 워커 등록 중...');
const firebaseRegistration = await navigator.serviceWorker.register('/firebase-messaging-sw.js');
console.log('FCM 서비스 워커 등록 성공');
console.log('FCM 서비스 워커 활성화 중...');
const serviceWorker = await navigator.serviceWorker.ready;
console.log('FCM 서비스 워커 활성화 성공');
if (serviceWorker && !sessionStorage.getItem('fcmToken')) {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('알림 권한이 허용되었습니다.');
console.log('FCM 토큰 발급 중...');
const currentToken = await getToken(messaging, {
vapidKey: 'BApIruZrx83suCd09dnDCkFSP_Ts08q38trrIL6GHpChtbjQHTHk_38_JRyTiKLqciHxLQ8iXtie3lvgyb4Iphg',
serviceWorkerRegistration: firebaseRegistration,
});
console.log('FCM 토큰 발급 성공');
if (currentToken) {
sessionStorage.setItem('fcmToken', currentToken);
} else {
console.warn('FCM 토큰을 가져올 수 없습니다.');
}
} else {
console.log('알림 권한이 거부되었습니다.');
}
}
} catch (error) {
console.error('FCM 서비스 워커 등록에 실패했습니다. : ', error);
}
};
// 서비스 워커 등록 함수 호출
registerServiceWorker();

View File

@ -12,3 +12,11 @@ export async function getProfile() {
export async function logout() {
return api.post('/auth/logout').then(({ data }) => data);
}
export async function saveFcmToken(token: string) {
return api.post('/auth/fcm', { token }).then(({ data }) => data);
}
export async function createFcmNotification() {
return api.post('/auth/test').then(({ data }) => data);
}

View File

@ -0,0 +1,59 @@
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';
const firebaseConfig = {
apiKey: String(import.meta.env.VITE_FIREBASE_API_KEY),
authDomain: String(import.meta.env.VITE_FIREBASE_AUTH_DOMAIN),
projectId: String(import.meta.env.VITE_FIREBASE_PROJECT_ID),
storageBucket: String(import.meta.env.VITE_FIREBASE_STORAGE_BUCKET),
messagingSenderId: String(import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID),
appId: String(import.meta.env.VITE_FIREBASE_APP_ID),
measurementId: String(import.meta.env.VITE_FIREBASE_MEASUREMENT_ID),
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
const getFcmToken = async () => {
const existingToken = sessionStorage.getItem('fcmToken');
if (existingToken) {
// 이미 토큰이 있는 경우, 해당 토큰을 반환한다.
return existingToken;
}
try {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('알림 권한이 허용되었습니다.');
console.log('FCM 토큰 발급 중...');
const currentToken = await getToken(messaging, {
vapidKey: 'BApIruZrx83suCd09dnDCkFSP_Ts08q38trrIL6GHpChtbjQHTHk_38_JRyTiKLqciHxLQ8iXtie3lvgyb4Iphg',
});
console.log('FCM 토큰 발급 성공');
if (currentToken) {
sessionStorage.setItem('fcmToken', currentToken);
return currentToken;
}
console.warn('FCM 토큰을 가져올 수 없습니다.');
} else {
console.log('알림 권한이 거부되었습니다.');
}
} catch (error) {
console.error('FCM 토큰을 가져오는 중 오류가 발생했습니다. : ', error);
}
};
const handleForegroundMessages = () => {
onMessage(messaging, (payload) => {
if (!payload.data) return;
console.log(payload.data);
});
};
export { getFcmToken, handleForegroundMessages, messaging };

View File

@ -0,0 +1,44 @@
import { createFcmNotification, saveFcmToken } from '@/api/authApi';
import { getFcmToken, handleForegroundMessages } from '@/api/firebaseConfig';
import { Button } from '@/components/ui/button';
export default function FirebaseTest() {
const handleSaveFcmToken = async () => {
const fcmToken = await getFcmToken();
if (fcmToken) {
await saveFcmToken(fcmToken);
return;
}
console.log('FCM 토큰이 없습니다.');
};
const handleCreateNotification = async () => {
await createFcmNotification();
};
handleForegroundMessages();
return (
<div>
<h1 className="heading p-2">hello, firebase!</h1>
<div className="p-2">
<Button
onClick={handleSaveFcmToken}
variant="outlinePrimary"
className="mr-2"
>
FCM
</Button>
<Button
onClick={handleCreateNotification}
variant="outlinePrimary"
className="mr-2"
>
FCM
</Button>
</div>
</div>
);
}

View File

@ -20,6 +20,7 @@ import NotFound from '@/pages/NotFound';
import ReviewRequest from '@/pages/ReviewRequest';
import ModelIndex from '@/pages/ModelIndex';
import ModelDetail from '@/pages/ModelDetail';
import FirebaseTest from '@/pages/FirebaseTest';
export const webPath = {
home: () => '/',
@ -27,6 +28,7 @@ export const webPath = {
workspace: () => '/workspace',
admin: () => `/admin`,
oauthCallback: () => '/redirect/oauth2',
firebaseTest: () => '/firebaseTest',
};
const router = createBrowserRouter([
@ -149,6 +151,14 @@ const router = createBrowserRouter([
</Suspense>
),
},
{
path: webPath.firebaseTest(),
element: (
<Suspense fallback={<div></div>}>
<FirebaseTest />
</Suspense>
),
},
]);
export default router;