2024. 11. 7. 11:41ㆍUtil
1. 리얼타임 데이터베이스
-> 실행하는동안에 수정이 된다.
2.FCM
- 지속형 연결
- 최근에는 SSE라고 한다. > Server Sent Events
> Server -> 클라이언트에게 실시간으로 보내주는것
- websocket -> socket.io 이용해서 채팅 구현
>>앱이 활성화가 되어있어야 동작한다.
>>>알림이란느 서비스 자체가 포그라운드, 백그라운드 에서 작동을 해야한다.
>>>> React쪽 메세지를 받을 수 있는것을 테스트 해줘야 한다.
>>>>> Boot쪽에서 메세지를 쏘는것도 해야한다.
1. FireBase
- 로그인
> console
>> HTML5에 워커라는 개념이 있다. > 브라우저 화면 다 뜨고 백그라운에서 동작하는것 > 걔를이용해서 돌아가는것
- 2가지 테스트
화면 떠있을때, 안떠있을때
- 프로젝트 만들어서 돌릴떄 IP, port같은걸로 전송했는데 > 자기 현재 디바이스, 현재 브라우저의 고유토큰값을 전송하게 된다.
> 토큰을 얻어야 한다.
- 사용자의 동의를 받으면 DB에 토큰값을 써놔야한다.
- Http는 로컬호스트만 허용되기 때문에 내디바이스 > 옆사람 디바이스 > 하려면 엔지록 써야함
2. 프로젝트 만들기
3. 프로젝트 설정
> 클라우드 메세징
>> 좀있다 이걸 할건데, 이거없으면 메세지 못보냄 일단 넘어감

4. 프로젝트 메내ㅠ
> web > hosting은 안한다 아직
>> API 키라던가 이런거 바꾸면 안된다. (작동이 안됨)
>>> TypeScript라서 문제가 되는데 >

5. react_kakao_1 열기
npm install
> src / firebase.package / firebaseConfig.ts
6. npm firebase install
7. firebaseConfig
- 위에 코드 넣는다.
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyBwqykIGgauGxeoD9752CfPWGLF6J3QvJQ",
authDomain: "testproject-ada8f.firebaseapp.com",
projectId: "testproject-ada8f",
storageBucket: "testproject-ada8f.firebasestorage.app",
messagingSenderId: "648086931814",
appId: "1:648086931814:web:e1fe31043f2d9b1ff120a7",
measurementId: "G-4TSYVQXJCF"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
8.웹푸시인증서 코드가 없으면 발신을 못한다.
>

- 설정/클라우드메세징/Generate key pair
BKvtsFJfxy419pm52_1KQnhLZsKztRx4XTt4jZmZV1mP0vb5xJ3HHHsHouTzUb_2y7sH3uynF_SyB0djSzB4A6Y

9. 사용자 permission을 획득해야하는데..
다짜고짜 문서에는 알아듣지못하게 나옴
<등록 토큰 엑세스>
requestPermission 을 하면 이 브라우저/디바이스에 토큰값이 생기게 된다.
그럼 상대방에게 알림을 보낼 수 있따.
하지만, 라우팅을 쓸떄는 문제가 생긴다.
10. 어쩃든 사용하려면 컨피그레이션, 푸시인증서가 있어야 한다.
11. requestPermission을 해야한다.(사용자 동의)
- 대부분의 문서는 App.tsx에 설정을 해라고하는데 여기 해봤자 나중에 문제가 생김
> 이게 컴포넌트처럼 동작하는애가 아니다 보니까 > useEffect를 해야하는데 hooks를 못 쓴다.
12. 레이아웃에 requestPermission, firebase코드를 넣는다. ( 모든 페이지가 다 필요로 해서 사용하는 페이지)
> 그래서 거기에 넣을까 한다.'
menu/SampleMenu 를 이용할거다
main/about/login에 모두 사용하기 때문이다.
13. App.tsx
- 나중에 firebase.tsx만들어서 사용하면된다.
function App() {
return (
<>
<h1> App Component</h1>
</>
)
}
export default App
14. SampleMenu
import {Link} from "react-router-dom";
import React from "react";
import App from "../../App.tsx";
function SampleMenu() {
return (
<>
<App></App>
<div className="flex m-3 text-3xl content-center justify-center bg-green-600 gap-3">
<div>
<Link to={'/'}>MAIN</Link>
</div>
<div>
<Link to={'/about'}>ABOUT</Link>
</div>
<div>
<Link to={'/member/login'}>LOGIN</Link>
</div>
</div>
</>
);
}
export default SampleMenu;
-> npm run dev
App Component 확인가능
참고자료
15. firebase.tsx
// Import the functions you need from the SDKs you need
import {initializeApp} from "firebase/app";
import {getAnalytics} from "firebase/analytics";
import {getMessaging} from "firebase/messaging";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyBwqykIGgauGxeoD9752CfPWGLF6J3QvJQ",
authDomain: "testproject-ada8f.firebaseapp.com",
projectId: "testproject-ada8f",
storageBucket: "testproject-ada8f.firebasestorage.app",
messagingSenderId: "648086931814",
appId: "1:648086931814:web:e1fe31043f2d9b1ff120a7",
measurementId: "G-4TSYVQXJCF"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
//Messaging Service
export const messaging = getMessaging(app);
- 어떤 경로든 실행했을때 알람이 공통으로 떠야한다.
16. App.tsx
import {useEffect} from "react";
function App() {
async function requestPermission() {
//requesting permission using Notification API
const permission = await Notification.requestPermission();
if (permission === "granted") {
alert("Notification granted!");
} else if (permission === "denied") {
//notifications are blocked
alert("You denied for the notification");
}
}
useEffect(() => {
requestPermission();
}, []);
return (
<>
<h1>----</h1>
</>
)
}
export default App
17. APP.tsx
import {getMessaging} from "firebase/messaging";
18. firebase-messaging-sw.js
- 백그라운드 용도 > 아직 background가 없어서 포그라운드 밖에 못받는다.
19. App.tsx
import {useEffect} from "react";
import { getToken } from "firebase/messaging"
import { messaging} from "./firebase/firebaseConfig.ts"
function App() {
async function requestPermission() {
//requesting permission using Notification API
const permission = await Notification.requestPermission();
if (permission === "granted") {
const token = await getToken(messaging, {
vapidKey: 'BKvtsFJfxy419pm52_1KQnhLZsKztRx4XTt4jZmZV1mP0vb5xJ3HHHsHouTzUb_2y7sH3uynF_SyB0djSzB4A6Y',
});
//We can send token to server
console.log("Token generated : ", token);
} else if (permission === "denied") {
//notifications are blocked
alert("You denied for the notification");
}
}
useEffect(() => {
requestPermission();
}, []);
return (
<>
<h1>----</h1>
</>
)
}
export default App
> 브라우저 이동해서 console에 토큰값 나오는지 확인

- 토큰값 복사
- 새로고침해도 토큰값 변경안됨
20. 파이어베이스 > 프로젝트 설정 > 실행 > messaging > 첫번쨰 캠페인 만들기
> firebase 알림 메세지 만들기 > 테스트 메세지
> 콘솔에서 나온 토큰 넣어주기
>> 테스트 한거 자체가 날라간건데 워커가 동작하지 않아서 ( 백그라운드 ) 아직 안날아감

21. fb-mes-sw
// Scripts for firebase and firebase messaging
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts(
"https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"
);
const firebaseConfig = {
apiKey: "AIzaSyBwqykIGgauGxeoD9752CfPWGLF6J3QvJQ",
authDomain: "testproject-ada8f.firebaseapp.com",
projectId: "testproject-ada8f",
storageBucket: "testproject-ada8f.firebasestorage.app",
messagingSenderId: "648086931814",
appId: "1:648086931814:web:e1fe31043f2d9b1ff120a7",
measurementId: "G-4TSYVQXJCF"
};
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: payload.notification.image,
};
self.registration.showNotification(notificationTitle, notificationOptions);
});
22. npm run dev
서비스 워커 - 백그라운드용
23. APp.tsx
import {useEffect} from "react";
import { getToken, onMessage } from "firebase/messaging"
import { messaging} from "./firebase/firebaseConfig.ts"
function App() {
async function requestPermission() {
//requesting permission using Notification API
const permission = await Notification.requestPermission();
if (permission === "granted") {
const token = await getToken(messaging, {
vapidKey: 'BKvtsFJfxy419pm52_1KQnhLZsKztRx4XTt4jZmZV1mP0vb5xJ3HHHsHouTzUb_2y7sH3uynF_SyB0djSzB4A6Y',
});
//We can send token to server
console.log("Token generated : ", token);
} else if (permission === "denied") {
//notifications are blocked
alert("You denied for the notification");
}
}
useEffect(() => {
requestPermission();
}, []);
onMessage(messaging, (payload) => {
console.log(payload);
alert("On Message ")
});
return (
<>
<h1>----</h1>
</>
)
}
export default App
- . SampleMenu
import {Link} from "react-router-dom";
function SampleMenu() {
return (
<>
<div className="flex m-3 text-3xl content-center justify-center bg-green-600 gap-3">
<div>
<Link to={'/'}>MAIN</Link>
</div>
<div>
<Link to={'/about'}>ABOUT</Link>
</div>
<div>
<Link to={'/member/login'}>LOGIN</Link>
</div>
</div>
</>
);
}
export default SampleMenu;
-.App.tsx
import {useEffect} from "react";
import { getToken, onMessage } from "firebase/messaging"
import { messaging} from "./firebase/fIrebaseConfig.ts"
function App() {
async function requestPermission() {
//requesting permission using Notification API
const permission = await Notification.requestPermission();
if (permission === "granted") {
const token = await getToken(messaging, {
vapidKey: 'BKvtsFJfxy419pm52_1KQnhLZsKztRx4XTt4jZmZV1mP0vb5xJ3HHHsHouTzUb_2y7sH3uynF_SyB0djSzB4A6Y',
});
//We can send token to server
console.log("Token generated : ", token);
} else if (permission === "denied") {
//notifications are blocked
alert("You denied for the notification");
}
}
useEffect(() => {
requestPermission();
}, []);
onMessage(messaging, (payload) => {
console.log(payload);
alert("On Message ")
});
return (
<>
<h1>----</h1>
</>
)
}
export default App
ngrok 테스트
- 서버사이드 세팅
1.서비스 계정 > 비공개 키 생성 > JSON생성 > 이걸 이용해서 보낸다.

> json파일 생성된다. (다운)
- api1014 프로젝트 실행
3. resources/firebase/ json파일 넣어라
{
"type": "service_account",
"project_id": "testproject-ada8f",
"private_key_id": "a43f3e9d1c5a8e66d0a73e711d584b7902bec48a",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0pIXM4yJy9P8s\ndB6SowRT0KCM5DiOQV5TBJOR3dsPOkg5OKF87n42UQOC44y+ln5lM0l8kw5iEkDO\nFweeVG8uY7pjmCop/wZsfSMW9mhSNraFl7k0/TdFCddmmdoHcFzeidoD727dCc1A\nGn9HFsuYbHiRwLVweONH3e81NRen/Cmm+t+U88IOzl6CHKxc1gGUNDQhJlE9hSXt\naJSXdrNrDn41Ofbq6xcZ0AmR2sgQfyB4U4KjoR+4Wt3kMDLDfFbUJW3rXsqo8Wxh\n6MgPmW/Rlde5jkkTL1//Ij0k1r4dmzpbyroz857o/ut9i0b1e+RDOMM9mgqytDPn\np2V/EgfpAgMBAAECggEATUH0C5209Q99Nwjurm5UAni+waM14PlqGv1hE8ib0NZv\nXzGuN11U02wwoUEqx7RbmHKn4kSOqTj/SGUnF/sqld+HLuM8XTu6Bpo6cK6wDUFj\nLJ2oU1Zc1gUQf8wbKIVQ4sh4WqiDdLulcd4jQ0cniigSJNwTfWfmZK0xikMLvAbu\nc0xH/OHgLBOOui4x1VCzLq+i8VIYkE/exkjsyOT17W9Mo3NhWVJ2VM+JV6Gp/LmP\nl6FmRGQW6VsnrOlU0/34eDE2laMhuQMK70tBwlq1Zw/jwGX3doaaASm0s7Sl+j5J\nZn7UsaEBLek8Akf8okbA88I4+RN3Q5RLyQu0mZJ7hQKBgQDzrPgTh6BWxQsIzxMN\nzu34j22Sz2Lm8Kn5+fzOr4OuExaamL6dxTr43498GvO7m/ZkUzplAj1QUuIBzraz\nqPUr+XKpUzs+Kw51Gn2X5OJYJMqWnEvg7ZqvbI+GtN8d/V9f/DXbYefkYq7dZwDo\nkLVZ6+J/d+qljdzJTdV0WhHMHwKBgQC9x2xpv2//Ozmh39HAD2K85buuvvGRPcXf\nLujYXcW/jT3JTNtQji9UybiEtDlfFzQ9LgfYbbgQHuXSIxL8Uc4e3FOzULWK+Wr9\nd+WjndZgaARNojB/XAGSwRCSdQh6wMc7Xz3ReO8bNinSx7p79xbLIPkwULpcd35c\nyb0Q9ncq9wKBgQCIQE5SdUK1YeZCna70yKENm/1T2rxdj3IrwZmXZFKH8kpwVTo4\nc8D+ydqsNVHVtGZ4QIVlV3Q7Rqzy/8fu+2ljlk6D2XNF1sN1vUM+vI/HY4MX5fsT\noSgeMOCsHNSpKzS6MgdXTQ5iCL/oMqEyaT/OMPRr+/xrZ8BtmyTgs0BCVwKBgC9e\nZXsHo4bLW5lB6nLL5FNN7EiztEwSZR9N8CSBU2h6cp+aJWu38axyJTJKYb+QZSOY\nJ7EnwbeUXrzSsFx2dsJRMDsjvAySMNhPYuwx615o2Bogj4ZairH8qoxD1ff9wjzZ\niu6MBvJ91HaeD7f9dp0A//HFVJ7b3JiAakafniMFAoGAX2TiCwx9lTC27F9Z/bTp\nON5vd5vkf+KP3IG6je9gXFoZS37JUoKYvZBvybNJB2Q9JAtc8oPojKbbp0nra5cC\nk5wyLm8lgQ7g9TwB248RCa6/FKaE7oMb1yidm1UB6cYfMdjMEsKeNRgyM5s8ZlPn\nqxPyeVHWK/V/0mhxKtCo3EI=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-ykqdp@testproject-ada8f.iam.gserviceaccount.com",
"client_id": "114290031640629895514",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-ykqdp%40testproject-ada8f.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
4. build.gradle
firebase admin maven 추가
implementation 'com.google.firebase:firebase-admin:9.3.0'
5. fcm.package / config.package / fcmConfig.class
package org.zerock.api1014.fcm.config;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@Configuration
public class FCMConfig {
@Bean
FirebaseMessaging firebaseMessaging() throws IOException {
ClassPathResource resource = new ClassPathResource("firebase/testproject-ada8f-firebase-adminsdk-ykqdp-a43f3e9d1c.json");
InputStream inputStream = resource.getInputStream();
FirebaseApp firebaseApp = null;
List<FirebaseApp> firebaseApps = FirebaseApp.getApps();
if( firebaseApps != null && firebaseApps.size() > 0) {
for(FirebaseApp firebaseApp1 : firebaseApps) {
if(firebaseApp1.getName().equals(FirebaseApp.DEFAULT_APP_NAME)) {
firebaseApp = firebaseApp1;
}
}//end for
}else {
FirebaseOptions firebaseOptions = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(inputStream)).build();
firebaseApp = FirebaseApp.initializeApp(firebaseOptions);
}
return FirebaseMessaging.getInstance(firebaseApp);
}
}
- JSON파일의 이름을 수정
6. fcm/ dto.package /FCMRequestDTO.class
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class FCMRequestDTO {
private String token;
private String title;
private String body;
}
7. . fcm/ exceptions.package / FCMMessageException
package org.zerock.api1014.fcm.exceptions;
public class FCMMessageException extends RuntimeException {
public FCMMessageException(String message) {
super(message);
}
}
8. fcm/ service.package / FCMService.class
@Service
@RequiredArgsConstructor
@Log4j2
public class FCMService {
private final FirebaseMessaging firebaseMessaging;
public void sendMessage(FCMRequestDTO fcmRequestDTO) {
if(fcmRequestDTO == null) {
throw new FCMMessageException("fcmRequestDTO is null");
}
if(fcmRequestDTO.getToken() == null) {
throw new FCMMessageException("fcmRequestDTO.getToken is null");
}
if(fcmRequestDTO.getTitle() == null || fcmRequestDTO.getTitle().isEmpty()) {
throw new FCMMessageException("title is null or empty");
}
Notification notification = Notification.builder()
.setBody(fcmRequestDTO.getBody())
.setTitle(fcmRequestDTO.getTitle())
.build();
Message message = Message.builder()
.setToken(fcmRequestDTO.getToken())
.setNotification(notification)
.build();
try {
firebaseMessaging.send(message);
} catch (FirebaseMessagingException e) {
throw new FCMMessageException(e.getMessage());
}
}
}
9. test/fcm/FCMTests.class
package org.zerock.api1014.fcm;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.zerock.api1014.fcm.dto.FCMRequestDTO;
import org.zerock.api1014.fcm.service.FCMService;
@SpringBootTest
@Log4j2
public class FCMTests {
@Autowired
FCMService fcmService;
@Test
public void sendTest() {
String token = "cg7UfwwhGSIZ7DAhzNluwd:APA91bFS03yCWNx0VZ93461OLEL1h72Lk_QmwRJEISvx9PFBciw6umGVQKfhNJCWD2uxC-eVzgrJg8hvborls-NPOrLYHcjpnujrIBlbTY1e8j0McTeA2tI";
String title = "메시지 전송 테스트";
String body = "<h1>메시지 전송 테스트 입니다.<b>AAA</b> </h1>" ;
FCMRequestDTO req = FCMRequestDTO.builder()
.token(token)
.title(title)
.body(body)
.build();
fcmService.sendMessage(req);
}
}
- 폰에서 확인할 때
chrome://inspect/#devices
> console.log찍힐때 메세지를 서버쪽으로 ajax로 쏘게끔 해놓으면 좋다.
>> 그 코드 하나 만들어 놓으면 된다.
https://turing0809.tistory.com/72
참고자료
[정리]
1. 사용자에게 허용
2. 토큰
3. 포/백그라운드에서 메세지 발송