Java/Spring Boot
[Java] Spring Boot Firebase Cloud Message(FCM) 구축 -2 : 예약 발송 - Spring Boot Quartz 활용
adjh54
2024. 2. 29. 10:28
반응형
해당 글에서는 Spring Boot 환경에서 FCM 예약 발송 기능을 Spring Boot Quartz(Scheduler)를 이용하여 구성하는 방법에 대해서 알아봅니다.
💡 [참고] FCM 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 | 링크 |
FCM Spirng Boot API 구성 : 단순 전송 기능 | https://adjh54.tistory.com/432 |
FCM React Native 초기 환경 설정 | https://adjh54.tistory.com/431 |
FCM React Native Notifee 메시지 수신 구성 | https://adjh54.tistory.com/4333 |
FCM React Natiive Notifee 메시지 전송 및 예약 발송 구성 | https://adjh54.tistory.com/434 |
Spring Boot Quartz를 이용한 FCM 구축 Github | https://github.com/adjh54ir/blog-codes/tree/main/spring-boot-scheudler |
1) FCM(Firebase Cloud Message) & Spring Boot Quartz
1. FCM(Firebase Cloud Message)
💡 FCM(Firebase Cloud Message)
- Google에서 제공하는 클라우드 기반 메시징 서비스입니다. 이 서비스는 앱 개발자가 사용자에게 푸시 알림을 보내거나, 앱이 백그라운드에서 실행되어 있는 동안 특정 데이터를 사용자에게 전송하는 데 사용됩니다.
💡 [참고] 최초 환경구성이 필요하신 분은 아래의 글을 참고하시고 해당 글을 읽으시면 도움이 됩니다.
2. Spring Boot Quartz
💡 Spring Boot Quartz
- Java 기반의 오픈 소스 작업 '스케줄링 라이브러리'를 의미합니다. 이를 사용하면 특정시간에 작업을 실행하거나 특정 간격으로 작업을 수행할 수 있습니다.
- 이를 사용하면 시스템의 자동화 및 효율성 향상에 기여하며 백그라운드 작업을 수행하는 서비스, 이메일 발송 스케줄링, 데이터베이스 백업 등에 활용될 수 있습니다.
💡 [참고] Spring Boot Quartz에 대해 궁금하시면 아래의 링크를 참고하시면 도움이 됩니다.
2) 구성 개발 환경
💡 구성 개발 환경
- FCM을 전송하기 위해 사용한 개발환경들입니다.
분류 | 버전 |
Java | 17 |
Spring Boot | 3.2.3 |
Spring Boot Web | 3.2.3 |
Spring Boot JDBC | 3.2.3 |
Spring Boot Quartz | 3.2.3 |
firebase-admin | 9.2.0 |
mybatis-spring-boot-starter | 3.0.3 |
mysql-connector-java | 8.0.33 |
3) FCM 구성 프로세스
1. 전체 프로세스
💡 전체 프로세스
- 전반적인 프로세스를 확인해 봅니다.
1. Spring Boot Application 실행
- Spring Boot Application Server가 시작되는 동시에 스케줄러가 수행이 됩니다.
2. SchedulerConfiguration 클래스 실행
- 해당 클래스에서 @Configuration 어노테이션으로 지정되었기에 서버가 실행되고 수행이 됩니다. 또한 해당 클래스는 WebMvcConfigurer 인터페이스의 구현체로 구성합니다.
3. configScheduler 함수 실행
- @PostConstruct으로 지정하여 빈 객체의 초기화과 모두 완료된 후 호출됩니다.
4. configScheduler 함수 : JobDetail 구성
- 스케줄러를 처리하기 위한 Job을 구성하며 JobDetail의 인스턴스를 구성합니다.
- 해당 Job 내에서 전송을 보낼 목록을 조회하여 조회된 목록 중 FCM Token 값을 받아서 실제 FCM API를 호출하여 FCM 메시지를 전송합니다.
5. configScheduler 함수 : Trigger 구성
- SimpleScheduler를 이용하여 구성하였습니다. 시간과 반복여부를 지정한 뒤 트리거 인스턴스 생성합니다.
6. configScheduler 함수 : Scheduler 구성
- 스케줄러의 생명주기를 관리하는 리스너를 등록하고 실제 스케줄러를 실행시킵니다.
7. Scheduler 시작
- 구성한 JobDetail, Trigger를 기반으로 스케줄링을 시작합니다.
[더 알아보기 ]
💡@Configuration 어노테이션
- 스프링의 빈 생명주기를 관리하는 ApplicationContext에게 해당 클래스를 빈 설정 정보를 제공하는 구성 클래스로 사용하라는 표시입니다.
- 이 어노테이션이 붙은 클래스에서는 메서드 위에 @Bean 어노테이션을 사용하여 실제 인스턴스를 생성하는 팩토리 메서드를 정의할 수 있습니다.
💡@PostConstruct 어노테이션
- 라이프사이클 어노테이션 중 하나로, 객체가 생성되고 모든 의존성이 주입된 후에 실행되어야 하는 메서드에 이 어노테이션을 붙입니다. 이 어노테이션은 Spring Framework, Java EE 등에서 지원됩니다.
- 주로 초기화 작업을 수행하는 데 사용되며, 해당 객체가 Spring의 bean으로 등록되어서 Spring 컨테이너에 의해 생성될 때, 그리고 모든 의존성이 주입된 직후에 한 번만 실행됩니다.
2. Job 상세구성
💡 Job 상세구성
- 해당 Job은 비즈니스 처리를 수행하며 실제 Job만 구성되었다고 시작되는 것이 아닌 Scheduler 인스턴스가 생성되고 start()되어야 수행이 됩니다.
1. JobDetail 구성
- 결론적으로 스케줄링에 추가할 JobDetail의 인스턴스를 생성하는 것을 목표로 합니다. 이는 JobBuilder를 기반으로 각각의 메서드를 통해 JobDetail을 구성합니다.
2. FcmJob 구성
- Job 인터페이스의 구현체이며 실제 수행을 위한 비즈니스 로직이 포함된 Job입니다.
3. FcmJob 구성 : 현재 시간과 특정 시간을 비교하여 조건에 맞는 정보 가져오기
- DB 상에 FCM Token과 전송시간이 포함되었다는 가정하에 DB에 접근하여 ‘예약 발송’이 필요한 목록을 조회하여 FCM Token을 반환받습니다.
- 특정 시간과 현재 시간을 비교하여 해당 조건에 맞는 FCM 토큰 정보를 가져옵니다.
4. FcmJob 구성 : 조건에 맞는 리스트가 존재 시 외부 FCM API와 통신하여 FCM을 전송합니다. (예약 발송)
- 반환받은 FCM Token 정보들을 기반으로 FCM API와 통신하여 실제 Push Message를 전송합니다.
3. Trigger 상세 구성
💡 Trigger 상세 구성
- 해당 Trigger는 구성된 행동(Job)에 대한 수행 방법(Trigger)을 정의합니다. 역시 Scheduler 인스턴스가 생성되고 start()되어야 수행이 됩니다.
1. Trigger 구성
- 결론적으로 스케줄링에 추가할 Trigger의 인스턴스를 생성하는 것을 목표로 합니다. 이는 TriggerBuilder를 기반으로 각각의 메서드를 통해 Trigger를 구성합니다.
2. SimpleScheduleBuilder 구성
- 시간을 지정하는 메서드를 사용합니다: withIntervalInSeconds()
- 반복 여부를 지정하는 메서드를 사용합니다 :repeatForever(), withRepeatCount()
4. Scheduler 상세 구성
💡 Scheduler 상세 구성
- 해당 Scheduler는 사전에 구성된 Job과 Trigger를 기반으로 Scheduler 인스턴스를 구성합니다. 이를 start() 메서드를 통해 수행합니다.
1. StdSchedulerFactory
- Scheduler 인스턴스를 구성하기 위해 StdSchedulerFactory를 통해 구성합니다.
- StdSchedulerFactory 내에는 JobListener를 구성하여 Job의 라이프 사이클을 확인할 수 있습니다.
2. Scheduler.start()
- 구성한 인스턴스를 실행하여 스케줄링을 시작합니다.
4) 환경구성: 초기 구성
1. 구성 디렉터리 구조
파일 | 설명 |
FcmController | 임시 테스트용으로 구성하였습니다. |
FcmService | FCM 처리를 위한 인터페이스입니다. |
FcmServiceImpl | FcmSerivce 인터페이스의 구현체 이며 비즈니스 로직을 처리하는 클래스입니다. |
FcmMapper | FcmMapper.xml 파일에서 SQL 정보를 가져오는 인터페이스 입니다. |
FcmMapper.xml | FCM 처리를 위한 SQL을 관리하는 MyBatis 파일 입니다. |
FcmJob | FCM 스케줄링 처리를 위한 Job을 구성한 클래스입니다. |
FcmJobListener | FCM 스케줄링 수행 시 라이프사이클을 관리하는 클래스입니다 |
SchedulerConfiguration | FCM 스케줄링을 수행하기 위한 스케줄러 구성을 관리하는 클래스입니다. |
2. 의존성 주입
dependencies {
// Spring Boot Starter
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc:3.2.3' // Spring Boot JDBC
implementation 'org.springframework.boot:spring-boot-starter-quartz:3.2.3' // Spring Boot Quartz
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.3' // Spring Boot Web
implementation 'org.springframework.boot:spring-boot-starter-validation:3.2.3' // Spring Boot Validation
implementation 'org.springframework.boot:spring-boot-starter-log4j2:3.2.3' // Spring Boot Log4j2
// OpenSource
implementation 'com.google.firebase:firebase-admin:9.2.0' // Firebase Admin
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' // Jackson Databind
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.1' // Jackson Yaml
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' // MyBatis
implementation 'mysql:mysql-connector-java:8.0.33' // MySQL Connector
// Compile & Runtime Level
compileOnly 'org.projectlombok:lombok' // Lombok
annotationProcessor 'org.projectlombok:lombok' // Lombok
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' // Spring Boot Configuration
}
3. 초기 설정
💡 초기 설정
- 아래의 글을 참고하여 Firebase 환경설정 부분을 설정해 주시면 됩니다.
5) 환경설정 : 프로젝트 구성
1. Fcm DTO 구성
💡 FcmSendDto
- 모바일에서 전달받은 객체를 매핑하는 DTO입니다
import lombok.*;
/**
* 모바일에서 전달받은 객체
*
* @author : lee
* @fileName : FcmSendDto
* @since : 2/21/24
*/
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FcmSendDto {
private String token;
private String title;
private String body;
@Builder(toBuilder = true)
public FcmSendDto(String token, String title, String body) {
this.token = token;
this.title = title;
this.body = body;
}
}
💡 FcmMessageDto
- FCM에 실제 전송될 데이터의 DTO입니다.
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
/**
* FCM 전송 Format DTO
*
* @author : lee
* @fileName : FcmMessageDto
* @since : 2/21/24
*/
@Getter
@Builder
public class FcmMessageDto {
private boolean validateOnly;
private FcmMessageDto.Message message;
@Builder
@AllArgsConstructor
@Getter
public static class Message {
private FcmMessageDto.Notification notification;
private String token;
}
@Builder
@AllArgsConstructor
@Getter
public static class Notification {
private String title;
private String body;
private String image;
}
}
💡 FcmSendDeviceDto
- DB에서 조회해 오는 FCM Token 값을 조회할 수 있는 DTO입니다.
import lombok.*;
/**
* FCM 전송을 위한 디바이스 정보 조회 DTO
*
* @author : lee
* @fileName : FcmSendDeviceDto
* @since : 2/26/24
*/
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FcmSendDeviceDto {
private String dvcTkn;
@Builder
public FcmSendDeviceDto(String dvcTkn) {
this.dvcTkn = dvcTkn;
}
}
2. FcmService.java
💡 FcmService.java
- FcmService 인터페이스를 구성합니다.
메서드 | 설명 |
sendMessageTo | FCM과 호출하여 디바이스로 전송 할 수 있는 메서드를 의미합니다. |
selectFcmSendList | 데이터베이스에서 조회해와 FCM을 보낼 디바이스의 FCM Token 리스트를 확인 할 수 있는 메서드입니다. |
/**
* FCM SERVICE
*
* @author : lee
* @fileName : FcmService
* @since : 2/21/24
*/
@Service
public interface FcmService {
void sendMessageTo(FcmSendDto fcmSendDto) throws IOException;
List<FcmSendDeviceDto> selectFcmSendList();
}
3. FcmServiceImpl.java
💡 FcmServiceImpl.java
- FcmService의 구현체로 실제 비즈니스 로직을 처리합니다.
메서드 | 설명 |
sendMessageTo | 메시지를 구성하고 토큰을 받아서 FCM으로 메시지 처리를 수행하는 비즈니스 로직 |
selectFcmSendList | DB에서 FCM을 전송할 목록들을 조회해오는 비즈니스 로직입니다. |
getAccessToken | Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다. |
makeMessage | FCM 전송 정보를 기반으로 메시지를 구성합니다. (Object -> String) |
💡 중요
- for문을 수행하면서 sendMessageTo 함수를 호출합니다 . 해당 함수를 호출 할때, 만약에 토큰이 유효하지 않은 400, 404 에러가 발생하는 경우 for문을 수행하는 도중에 5건의 데이터라고 가정하였을 시 1건에 에러에 대해 4건이 전송되지 않을 수 있는 오류가 발생할 수 있습니다.
- 대부분 아래의 메서드 중 여기서 발생합니다.
restTemplate.exchange(PROJECT_API_URL, HttpMethod.POST, entity, String.class);
- 그렇기에 문제가 발생하는 메서드에 try ~ catch 작업을 통해서 catch 내에서 작업을 수행하는 부분이 추가가 필요합니다.
import org.springframework.http.HttpMethod;
/**
* FCM 서비스를 처리하는 구현체
*
* @author : FcmServiceImpl
* @fileName : PushMessageServiceImpl
* @since : 2/21/24
*/
@Service
public class FcmServiceImpl implements FcmService {
private final SqlSession sqlSession;
public FcmServiceImpl(SqlSession ss) {
this.sqlSession = ss;
}
/**
* 푸시 메시지 처리를 수행하는 비즈니스 로직
*
* @param fcmSendDto 모바일에서 전달받은 Object
* @return 성공(1), 실패(0)
*/
@Override
public void sendMessageTo(FcmSendDto fcmSendDto) throws IOException {
String message = makeMessage(fcmSendDto);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + getAccessToken());
HttpEntity entity = new HttpEntity<>(message, headers);
String API_URL = "<https://fcm.googleapis.com/v1/projects/adjh54-effb8/messages:send>";
try {
restTemplate.exchange(PROJECT_API_URL, HttpMethod.POST, entity, String.class);
} catch (Exception e) {
log.error("[-] FCM 전송 오류 :: " + e.getMessage());
log.error("[-] 오류 발생 토큰 :: [" + fcmSendDto.getToken() + "]");
log.error("[-] 오류 발생 메시지 :: [" + fcmSendDto.getBody() + "]");
log.error("[-] 현재 바라보고 있는 Key Path 파일 :: [" + KEY_PATH + "]");
}
}
/**
* FCM 전송 디바이스 리스트 조회
*/
@Override
@Transactional(readOnly = true)
public List selectFcmSendList() {
FcmMapper udm = sqlSession.getMapper(FcmMapper.class);
return udm.selectFcmSendList();
}
/**
* Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다.
*
* @return Bearer token
*/
private String getAccessToken() throws IOException {
String firebaseConfigPath = "firebase/tugboat-dev-firebase-key.json";
GoogleCredentials googleCredentials = GoogleCredentials
.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())
.createScoped(List.of("<https://www.googleapis.com/auth/cloud-platform>"));
googleCredentials.refreshIfExpired();
return googleCredentials.getAccessToken().getTokenValue();
}
/**
* FCM 전송 정보를 기반으로 메시지를 구성합니다. (Object -> String)
*
* @param fcmSendDto FcmSendDto
* @return String
*/
private String makeMessage(FcmSendDto fcmSendDto) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
FcmMessageDto fcmMessageDto = FcmMessageDto.builder()
.message(FcmMessageDto.Message.builder()
.token(fcmSendDto.getToken())
.notification(FcmMessageDto.Notification.builder()
.title(fcmSendDto.getTitle())
.body(fcmSendDto.getBody())
.image(null)
.build()
).build())
.validateOnly(false)
.build();
return om.writeValueAsString(fcmMessageDto);
}
}
4. FcmMapper.xml
💡 FcmMapper.xml
- MyBatis로 구성되어 있으며 현재 시간:분을 기반으로 00초부터 59초까지의 리스트를 조회하여 FCM Token을 반환받는 SQL입니다.
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.adjh54.scheduler.mapper.FcmMapper">
<!-- FCM 전송을 위한 디바이스 정보 조회-->
<select id="selectFcmSendList" resultType="FcmSendDeviceDto">
SELECT dvc_tkn
FROM TB_USER_DVC AND t1.plan_tm BETWEEN DATE_FORMAT(CURRENT_TIME(), '%H:%i:00') AND DATE_FORMAT(CURRENT_TIME(), '%H:%i:59')
</select>
</mapper>
5. Job 구성 : FcmJob.java
💡 Job 구성 : FcmJob.java
- Job 인터페이스의 구현체로 execute 메서드를 구현합니다. 해당 메서드는 실제 Job을 수행하기 비즈니스로직이 포함된 메서드입니다.
💡execute()
1. Service 인터페이스를 호출하기 위해 ApplicationContext에 appContext 이름으로 bean을 등록합니다.
2. FCM 전송 리스트 구성합니다. : FCM 토큰을 구성합니다.
3. 리스트를 순회하며 값들을 추출합니다. : 토큰 값을 반환하여 FCM에 전송할 데이터를 구성합니다.
4. FCM 전송 데이터를 구성합니다.
5. FCM 전송을 합니다.
/**
* FCM 메시지 전송을 위한 Job
*
* @author : lee
* @fileName : FcmJob
* @since : 2/23/24
*/
@Slf4j
public class FcmJob implements Job {
private FcmService fcmService;
/**
* Job 실행시 수행이 됩니다.
*
* @param jobExecutionContext JobExecutionContext
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) {
if (fcmService == null) {
// [STEP1] Service 인터페이스를 호출하기 위해 ApplicationContext에 appContext 이름으로 bean을 등록합니다.
ApplicationContext appCtx = (ApplicationContext) jobExecutionContext.getJobDetail().getJobDataMap().get("appContext");
fcmService = appCtx.getBean(FcmService.class);
}
// [STEP2] FCM 전송 리스트 구성합니다. : FCM 토큰을 구성합니다.
List<FcmSendDeviceDto> selectFcmSendList = fcmService.selectFcmSendList();
// [STEP3] 리스트를 순회하며 값들을 추출합니다. : 토큰 값을 반환하여 FCM에 전송할 데이터를 구성합니다.
for (FcmSendDeviceDto fcmSendItem : selectFcmSendList) {
// [STEP4] FCM 전송 데이터를 구성합니다.
FcmSendDto fcmSendDto = FcmSendDto.builder()
.token(fcmSendItem.getDvcTkn())
.title("푸시 메시지입니다!")
.body("계획된 시간이 되었어요!")
.build();
try {
// [STEP5] FCM 전송을 합니다.
fcmService.sendMessageTo(fcmSendDto);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
6. JobListener 구성
💡 JobListener 구성
- 스케줄러를 수행하는 과정에서 라이프사이클을 확인하기 위해 JobListener 인터페이스의 구현체로 구성합니다.
- 해당 부분은 간단하게 라이프사이클만 확인할 수 있도록 콘솔을 작성하였습니다.
/**
* Please explain the class!!
*
* @author : lee
* @fileName : FcmJobListener
* @since : 2/23/24
*/
public class FcmJobListener implements JobListener {
@Override
public String getName() {
return "hello";
}
/**
* Job 실행 이전 수행
*
* @param jobExecutionContext
*/
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
System.out.println("[-] Job이 실행되기전 수행됩니다");
}
/**
* Job 실행 취소 시점 수행
*
* @param jobExecutionContext
*/
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
System.out.println("[-] Job이 실행 취소된 시점 수행됩니다.");
}
/**
* Job 실행 완료 시점 수행
*
* @param jobExecutionContext
* @param e
*/
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
System.out.println("[+] Job이 실행 완료된 시점 수행됩니다.");
}
}
💡 [참고] 아래와 같이 Job이 수행되기 전후에 콘솔이 출력됩니다.
7. SchedulerConfiguration
💡 SchedulerConfiguration
- 실제 스케줄러가 수행되는 클래스입니다. @Configuration 어노테이션으로 앱이 실행된 후 수행되며 WebMvcConfigurer 인터페이스의 구현체로 구성되어 있습니다. 해당 스케줄러는 10초마다 반복되며 DB를 조회하고 FCM 메시지가 전송되는 구조입니다.
- 최초 스케줄러 객체를 구성하고, ApplicationContext를 객체로 구성하였습니다.
- 해당 구성이 완료되면 @PostConstruct 어노테이션을 선언했기에 configScheduler() 메서드가 수행이 됩니다.
💡configScheduler
1. Job 생성
2. Trigger 생성
3. 스케줄러 생성 및 Job, Trigger 등록
/**
* 스케줄러 구성을 지정합니다.
*
* @author : lee
* @fileName : SchedulerConfiguration
* @since : 2/27/24
*/
@Configuration
public class SchedulerConfiguration implements WebMvcConfigurer {
private Scheduler scheduler;
private final ApplicationContext applicationContext;
// 애플리케이션 영역을 가져오기 위한 이름
private static String APPLICATION_NAME = "appContext";
// 생성자를 통해 두개의 객체 구성
public SchedulerConfiguration(Scheduler sch, ApplicationContext applicationContext) {
this.scheduler = sch;
this.applicationContext = applicationContext;
}
/**
* FCM 전송을 위한 스케줄러 구성
*/
@PostConstruct
private void configScheduler() throws SchedulerException {
JobDataMap ctx = new JobDataMap(); // 스케줄러에게 애플리케이션 영역을 추가합니다.
ctx.put(APPLICATION_NAME, applicationContext); // 애플리케이션 영역을 "appContext"으로 지정합니다.
// [STEP1] Job 생성
JobDetail job = JobBuilder
.newJob(FcmJob.class) // Job 구현 클래스
.withIdentity("fcmSendJob", "fcmGroup") // Job 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Job") // Job 설명
.setJobData(ctx)
.build();
// [STEP2] Trigger 생성
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("fcmSendTrigger", "fcmGroup") // Trigger 이름, 그룹 지정
.withDescription("FCM 처리를 위한 조회 Trigger") // Trigger 설명
.startNow()
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
// [STEP3] 스케줄러 생성 및 Job, Trigger 등록
scheduler = new StdSchedulerFactory().getScheduler();
FcmJobListener fcmJobListener = new FcmJobListener();
scheduler.getListenerManager().addJobListener(fcmJobListener);
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
}
6) 결과 확인
1. 서버 수행
💡 서버 수행
- 서버가 수행되는 동시에 10초마다 반복하여 DB를 조회하며, 대상이 되는 정보가 있으면 FCM Token 정보를 가져와 FCM 전송을 수행합니다.
2. 모바일 결과 확인
💡 모바일 결과 확인
- 서버에서 FCM Token을 기반으로 전송된 푸시 메시지를 디바이스에서 확인하였습니다.
💡 [참고] 해당 내용의 소스코드는 아래에서 확인이 가능합니다.
오늘도 감사합니다. 😀
반응형