From 04f706649532b76265f1d1735cc4dfa8d7b35917 Mon Sep 17 00:00:00 2001 From: gray <68818952+Kim0914@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:37:48 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20fcm=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EB=B9=84=EB=8F=99=EA=B8=B0=20=EC=B2=98=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#499)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: fcm sdk 적용 및 fcm 서버측 알림처리 비동기 설정 * refactor: FCM 초기화 로직 변경 * refactor: FCM 파일 경로 변경 * refactor: FCM 인스턴스 명 변경 * test: 알림 테스트 계정 변경 * chore: Async 스레드 풀 개수 변경 * chore: 알림 로깅 추가 및 불필요한 코드 삭제 * refactor: 파일 경로 정적 변수로 추출 * refactor: 비동기 스레드 풀 40으로 설정 * chore: 현재 필요하지 않는 테스트 메서드 주석 처리 * chore: FCM 정적 변수 빈 주입으로 변경 --------- Co-authored-by: 이건회 --- .../pium/admin/service/TestService.java | 63 ++++++------ .../pium/admin/ui/TestController.java | 9 +- .../fcm/application/FcmMessageSender.java | 97 ++++++++----------- .../pium/src/main/resources/application.yml | 8 +- .../pium/src/test/resources/application.yml | 5 +- 5 files changed, 76 insertions(+), 106 deletions(-) diff --git a/backend/pium/src/main/java/com/official/pium/admin/service/TestService.java b/backend/pium/src/main/java/com/official/pium/admin/service/TestService.java index b12cba39..0a3b2856 100644 --- a/backend/pium/src/main/java/com/official/pium/admin/service/TestService.java +++ b/backend/pium/src/main/java/com/official/pium/admin/service/TestService.java @@ -1,9 +1,6 @@ package com.official.pium.admin.service; -import com.official.pium.petPlant.domain.PetPlant; -import com.official.pium.petPlant.event.notification.NotificationEvent; import com.official.pium.petPlant.repository.PetPlantRepository; -import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; @@ -33,8 +30,8 @@ public class TestService { // log.info("동기 알림 테스트 종료. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName()); // } - public void sendWaterNotificationAsyncRampTest() { - List petPlants = petPlantRepository.findAllByMemberId(6L); +// public void sendWaterNotificationAsyncRampTest() { +// List petPlants = petPlantRepository.findAllByMemberId(7L); // List events = petPlants.stream() // .map(plant -> NotificationEvent.builder() // .title(plant.getNickname()) @@ -43,39 +40,39 @@ public void sendWaterNotificationAsyncRampTest() { // .build() // ).toList(); - for (int i = 0; i < 100; i++) { - PetPlant petPlant = petPlants.get(i); - NotificationEvent event = NotificationEvent.builder() - .title(petPlant.getNickname()) - .body("물줘") - .deviceToken(petPlant.getMember().getDeviceToken()) - .build(); - publisher.publishEvent(event); - } +// for (int i = 0; i < 100; i++) { +// PetPlant petPlant = petPlants.get(i); +// NotificationEvent event = NotificationEvent.builder() +// .title(petPlant.getNickname()) +// .body("물줘") +// .deviceToken(petPlant.getMember().getDeviceToken()) +// .build(); +// publisher.publishEvent(event); +// } // log.info("비동기 테스트 램프업 시작"); // for (int i = 0; i < 100; i++) { // NotificationEvent notificationEvent = events.get(i); // publisher.publishEvent(notificationEvent); // } - } - - public void sendWaterNotificationAsyncTest() { - List petPlants = petPlantRepository.findAllByMemberId(6L); - List events = petPlants.stream() - .map(plant -> NotificationEvent.builder() - .title(plant.getNickname()) - .body("(테스트 중) 물을 줄 시간이에요!") - .deviceToken(plant.getMember().getDeviceToken()) - .build() - ).toList(); +// } - int i = 1; - log.info("비동기 알림 테스트 시작. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName()); - for (NotificationEvent event : events) { - log.info(i++ + "번째 알림 이벤트"); - publisher.publishEvent(event); - } - log.info("비동기 알림 테스트 종료. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName()); - } +// public void sendWaterNotificationAsyncTest() { +// List petPlants = petPlantRepository.findAllByMemberId(7L); +// List events = petPlants.stream() +// .map(plant -> NotificationEvent.builder() +// .title(plant.getNickname()) +// .body("(테스트 중) 물을 줄 시간이에요!") +// .deviceToken(plant.getMember().getDeviceToken()) +// .build() +// ).toList(); +// +// int i = 1; +// log.info("비동기 알림 테스트 시작. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName()); +// for (NotificationEvent event : events) { +// log.info(i++ + "번째 알림 이벤트"); +// publisher.publishEvent(event); +// } +// log.info("비동기 알림 테스트 종료. Thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName()); +// } } diff --git a/backend/pium/src/main/java/com/official/pium/admin/ui/TestController.java b/backend/pium/src/main/java/com/official/pium/admin/ui/TestController.java index 555d26df..1d0514a0 100644 --- a/backend/pium/src/main/java/com/official/pium/admin/ui/TestController.java +++ b/backend/pium/src/main/java/com/official/pium/admin/ui/TestController.java @@ -1,5 +1,6 @@ package com.official.pium.admin.ui; +import com.official.pium.admin.service.TestService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -9,13 +10,7 @@ @RequestMapping("/test") public class TestController { -// private final TestService testService; - -// @GetMapping("/notifications") -// public ResponseEntity notificationTest() { -// testService.sendWaterNotificationTest(); -// return ResponseEntity.ok("알림 기능 테스트 성공"); -// } + private final TestService testService; // @GetMapping("/notifications/ramp") // public ResponseEntity notificationRampTest() { diff --git a/backend/pium/src/main/java/com/official/pium/notification/fcm/application/FcmMessageSender.java b/backend/pium/src/main/java/com/official/pium/notification/fcm/application/FcmMessageSender.java index a595c90b..eba7f6dc 100644 --- a/backend/pium/src/main/java/com/official/pium/notification/fcm/application/FcmMessageSender.java +++ b/backend/pium/src/main/java/com/official/pium/notification/fcm/application/FcmMessageSender.java @@ -1,81 +1,64 @@ package com.official.pium.notification.fcm.application; import com.google.auth.oauth2.GoogleCredentials; -import com.official.pium.notification.fcm.dto.FcmMessageResponse; -import com.official.pium.notification.fcm.exception.FcmException; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; import com.official.pium.notification.application.MessageSendManager; +import jakarta.annotation.PostConstruct; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.concurrent.ExecutionException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import java.io.IOException; @Slf4j @Component @RequiredArgsConstructor public class FcmMessageSender implements MessageSendManager { - @Value("${fcm.api.url}") - private String apiUrl; - - @Value("${fcm.key.path}") - private String keyPath; - - @Value("${fcm.key.scope}") - private String keyScope; + @Value("${fcm.json.path}") + private String FCM_JSON_PATH; - private final RestTemplate restTemplate; - - public void sendMessageTo(String targetToken, String title, String body) { + @PostConstruct + public void initialize() { try { - FcmMessageResponse message = makeMessage(targetToken, title, body); - - HttpHeaders headers = new HttpHeaders(); - headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken()); - headers.set(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8"); - - HttpEntity request = new HttpEntity<>(message, headers); + ClassPathResource resource = new ClassPathResource(FCM_JSON_PATH); + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(resource.getInputStream())) + .build(); - ResponseEntity postResult = restTemplate.postForEntity( - apiUrl, - request, - FcmMessageResponse.class - ); - - log.info("FCM 메시지 전송 성공: {}", postResult.getBody()); - - } catch (Exception e) { - log.error("FCM 메시지 전송 실패", e); - throw new FcmException.FcmMessageSendException(e.getMessage()); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + } + } catch (FileNotFoundException e) { + log.error("파일을 찾을 수 없습니다. ", e); + } catch (IOException e) { + log.error("FCM 인증이 실패했습니다. ", e); } } - private FcmMessageResponse makeMessage(String targetToken, String title, String body) { - return FcmMessageResponse.builder() - .message(FcmMessageResponse.Message.builder() - .token(targetToken) - .notification(FcmMessageResponse.Notification.builder() - .title(title) - .body(body) - .image(null) - .build() - ) - .build() - ) - .validate_only(false) + public void sendMessageTo(String targetToken, String title, String body) { + Notification notification = Notification.builder() + .setTitle(title) + .setBody(body) .build(); - } - - private String getAccessToken() throws IOException { - GoogleCredentials googleCredentials = GoogleCredentials - .fromStream(new ClassPathResource(keyPath).getInputStream()) - .createScoped(keyScope); - googleCredentials.refreshIfExpired(); - return googleCredentials.getAccessToken().getTokenValue(); + Message message = Message.builder() + .setToken(targetToken) + .setNotification(notification) + .build(); + try { + String response = FirebaseMessaging.getInstance().sendAsync(message).get(); + log.info("알림 전송 성공 : " + response); + } catch (InterruptedException e) { + log.error("FCM 알림 스레드에서 문제가 발생했습니다.", e); + } catch (ExecutionException e) { + log.error("FCM 알림 전송에 실패했습니다.", e); + } } } diff --git a/backend/pium/src/main/resources/application.yml b/backend/pium/src/main/resources/application.yml index 5b1ccb8f..7d605ec1 100644 --- a/backend/pium/src/main/resources/application.yml +++ b/backend/pium/src/main/resources/application.yml @@ -27,11 +27,9 @@ management: endpoints: enabled-by-default: false fcm: - key: - path: test/ - scope: https://www.googleapis.com/auth/firebase.messaging - api: - url: https://fcm.googleapis.com/v1/projects/project-id/messages:send + json: + path: config/pium-fcm.json + petPlant: image: directory: test diff --git a/backend/pium/src/test/resources/application.yml b/backend/pium/src/test/resources/application.yml index f25c9199..8fc7a303 100644 --- a/backend/pium/src/test/resources/application.yml +++ b/backend/pium/src/test/resources/application.yml @@ -64,11 +64,8 @@ server: max-http-form-post-size: 10MB fcm: - key: + json: path: config/pium-fcm.json - scope: https://www.googleapis.com/auth/firebase.messaging - api: - url: https://fcm.googleapis.com/v1/projects/pium-test/messages:send management: endpoint: