클라이언트에서 Spring Boot 서버로 FCM 토큰을 보내는 테스트 과정은 아래 게시글 참고하기
[FN] Spring Boot API / FCM Token 전송
모임 신청을 하고 신청에 대한 알림을 FCM으로 모임장에게 띄워주기 위해서React Native에서 Spring Boot 서버로 FCM 토큰을 보내주어야하는데 로컬호스트:포트 번호로 연결할 수는 있지만,다른 기기나
codingco.tistory.com
이제 알림을 보내는 기능을 구현해보자.
신청서를 작성하고 작성 완료 버튼을 누르면 -> Spring Boot 서버로 요청을 보내고 -> 서버에서는 모임장에게 알림 전송
1. DTO
클라이언트와 서버 간 데이터를 주고받을 때 사용하는 객체로 DTO 파일을 생성한다.
( React Native에서 백엔드(Spring Boot)로 푸시 알림 요청을 보낼 때 JSON 데이터를 담는 용도)
코드를 간결하게 짜기 위해 Lombok을 사용하기로 하였다.
의존성 추가
implementation 'org.projectlombok:lombok:1.18.28'
annotationProcessor 'org.projectlombok:lombok:1.18.28'
NotificationRequest.java
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class NotificationRequest {
private String targetToken; // 알림 받을 사용자의 FCM 토큰
private String title; // 알림 제목
private String message; // 알림 메시지
}
2. FCM 알림 전송 서비스
FcmService.java
- Message : FCM을 통해 보낼 메시지를 생성하는 클래스
- Notification : 푸시 알림의 제목과 내용을 설정함 (title, body)
- Map : 데이터 형태로 추가 정보를 전달하기 위해 (key - value)
- Notification notification = Notification.builder() : 알림 생성
- Message firebaseMessage = Message.builder() : 푸시 메시지 생성
- FirebaseMessaging.getInstance().send(firebaseMessage): 실제로 FCM 서버에 요청하여 푸시 알림을 보냄
package com.example.project;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class FcmService {
public void sendNotification(String targetToken, String title, String message) {
System.out.println("전달받은 targetToken: " + targetToken);
if (targetToken == null || targetToken.isEmpty()) {
System.out.println("유효하지 않은 FCM 토큰입니다.");
return;
}
Notification notification = Notification.builder()
.setTitle(title)
.setBody(message)
.build();
Message firebaseMessage = Message.builder()
.setToken(targetToken)
.setNotification(notification)
.putAllData(Map.of(
"title", title,
"message", message
))
.build();
try {
FirebaseMessaging.getInstance().send(firebaseMessage);
System.out.println("푸시 알림 전송 성공!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("푸시 알림 전송 실패: " + e.getMessage());
}
}
}
다음으로는 API 엔드포인트를 설정해준다 api/send-norification 으로 엔드포인트 설정
NotificationController.java
package com.example.project;
import com.example.festanow_meeting.NotificationRequest;
import com.example.festanow_meeting.FcmService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class NotificationController {
private final FcmService fcmService;
public NotificationController(FcmService fcmService) {
this.fcmService = fcmService;
}
@PostMapping("/send-notification")
public ResponseEntity<String> sendNotification(@RequestBody NotificationRequest request) {
fcmService.sendNotification(request.getTargetToken(), request.getTitle(), request.getMessage());
return ResponseEntity.ok("푸시 알림 전송 완료!");
}
}
3. React Native 신청서 작성 페이지
API가 구현되었다면 클라이언트에서 모임장의 FCM 토큰을 조회하고 서버로 요청을 보내고 푸시 알림이 전송되도록 구현해야한다.
<흐름>
- MeetingContentScreen에서 postId를 MeetingJoinScreen으로 전달
- MeetingJoinScreen에서 firestore의 meetings/{postId}/applications에 신청 정보 저장
- authorId를 기반으로 users 컬렉션에서 fcmToken 조회
- 조회한 fcmToken으로 푸시 알림 전송
MeetingJoinScreen.tsx
route로 이전 화면에서 postId, userId, authorId 등의 데이터 전달
type MeetingJoinScreenProps = StackNavigationProp<any, 'MeetingJoin'>;
type MeetingJoinScreenRouteProps = RouteProp<any, 'MeetingJoin'>;
type Props = {
navigation: MeetingJoinScreenProps;
route: MeetingJoinScreenRouteProps;
};
모임 신청자의 데이터를 가져오는 코드
const getApplicantNicName = async (userId: string) => {
try {
const userDoc = await firestore().collection("users").doc(userId).get();
console.log("Firestore에서 가져온 신청자 문서:", userDoc.data()); // 로그 추가
return userDoc.exists ? userDoc.data()?.nicName : "익명 사용자";
} catch (error) {
console.error("신청자의 nicName 조회 실패:", error);
return "익명 사용자";
}
};
firestore에 users 컬렉션의 저장되어 있는 hostId 를 기반으로 모임장의 fcmToken을 가져온다.
hostId 는 authorId의 역할을 함. 즉, 모임을 만든 작성자의 userId가 hostId로 사용된 것임.
firestore에서 모임장의 데이터를 받아옴 -> 모임장의 FCM 토큰 존재 여부 체크
const getHostFcmToken = async (hostId: string) => {
try {
const hostDoc = await firestore().collection("users").doc(hostId).get();
console.log("Firestore에서 가져온 host 문서:", hostDoc.data());
const fcmToken = hostDoc.exists ? hostDoc.data()?.fcmToken : null;
if (!fcmToken) {
console.error("모임장의 FCM 토큰이 Firestore에 저장되어 있지 않습니다.");
}
return fcmToken;
} catch (error) {
console.error("모임장 FCM 토큰 조회 실패:", error);
return null;
}
};
- getHostFcmToken(hostId)를 호출하여 모임장의 FCM 토큰을 가져옴
- axios를 이용해 서버로 FCM 요청을 보냄
- 푸시 알림에는 모임 제목, 신청자 이름이 포함된다.
const sendPushNotification = async (hostId: string, meetingTitle: string, applicantName: string) => {
const fcmToken = await getHostFcmToken(hostId);
if (!fcmToken) {
console.error("모임장의 FCM 토큰이 없습니다.");
return;
}
try {
await axios.post("http://ip주소/포트번호/api/send-notification", {
targetToken: fcmToken,
title: "새로운 모임 신청",
body: `${applicantNicName}님이 "${meetingTitle}" 모임에 신청했습니다!`,
});
console.log("모임장에게 푸시 알림 전송 성공");
} catch (error) {
console.error("푸시 알림 전송 실패:", error);
}
};
- 작성 완료 버튼을 눌렀을 때 handleSubmit 함수 실행
- meetings 컬렉션에서 postId 문서의 applications 하위 컬렉션에 신청 데이터 추가
- meetings 컬렉션에서 postId 문서 조회
- sendPushNotification 호출하여 모임장에게 FCM 푸시 알림 전송
- 신청자의 닉네임 정보 applicantNicName
const handleSubmit = async () => {
if (message.trim() === "") {
Alert.alert("알림", "자기소개와 메시지를 입력해 주세요.");
return;
}
try {
const db = firestore();
await db.collection("meetings").doc(postId).collection("applications").add({
userId,
authorId,
message,
createdAt: firestore.FieldValue.serverTimestamp(),
});
const applicantNicName = await getApplicantNicName(userId);
const meetingDoc = await db.collection("meetings").doc(postId).get();
const meetingTitle = meetingDoc.exists ? meetingDoc.data()?.title : "모임";
await sendPushNotification(authorId, meetingTitle, applicantNicName);
Alert.alert("신청 완료", "모임 참여 신청이 완료되었습니다!");
navigation.navigate("Home");
} catch (error) {
console.error("신청 실패:", error);
Alert.alert("오류", "신청을 처리하는 중 문제가 발생했습니다.");
}
};
결과
'Spring Boot :' 카테고리의 다른 글
[DAS] SpringBoot - isOverNight (0) | 2025.06.10 |
---|---|
[FN] 신청서 상태 관리 API (신청서 승인, 거절, 취소) (1) | 2025.03.28 |
[FN] 신청서 조회 API (7) | 2025.03.06 |
[FN] Spring Boot API / FCM Token 전송 (2) | 2025.02.19 |
[FN] Intellij Community에서 Spring Boot 사용하기 (1) | 2025.02.19 |