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에서 제공하는 클라우드 기반 메시징 서비스입니다. 이 서비스는 앱 개발자가 사용자에게 푸시 알림을 보내거나, 앱이 백그라운드에서 실행되어 있는 동안 특정 데이터를 사용자에게 전송하는 데 사용됩니다.

 

💡 [참고] 최초 환경구성이 필요하신 분은 아래의 글을 참고하시고 해당 글을 읽으시면 도움이 됩니다.
 

[Java] Spring Boot Firebase Cloud Message(FCM) 푸시 메시지 API 구축 : firebase-admin

해당 글에서는 Spring Boot API에서 FCM으로 통신하여 FCM Push Message 전송 API 구축을 하는 방법에 대해 알아봅니다. 💡 [참고] FCM 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움

adjh54.tistory.com

 
 
 

2. Spring Boot Quartz


 💡 Spring Boot Quartz

- Java 기반의 오픈 소스 작업 '스케줄링 라이브러리'를 의미합니다. 이를 사용하면 특정시간에 작업을 실행하거나 특정 간격으로 작업을 수행할 수 있습니다.
- 이를 사용하면 시스템의 자동화 및 효율성 향상에 기여하며 백그라운드 작업을 수행하는 서비스, 이메일 발송 스케줄링, 데이터베이스 백업 등에 활용될 수 있습니다.

 

💡 [참고] Spring Boot Quartz에 대해 궁금하시면 아래의 링크를 참고하시면 도움이 됩니다.
 

[Java] 스케줄링 & Spring Boot Quartz 이해하고 적용하기 -1 : 설정 및 간단예시

해당 글에서는 스케줄링에 대해 이해하고 Spring Boot Starter Quartz를 이용하여 스케줄링을 구성하는 간단한 예시를 구성하는 방법을 위한 목적으로 작성한 글입니다. 💡 스케줄러를 이해하기 이전

adjh54.tistory.com

 

[Java] Spring Boot Quartz 상세 이해하기 -2 : 주요 메서드 및 흐름, 처리과정

해당 글에서는 Spring Boot Quartz에 대해 주요 메서드와 흐름, 처리과정에 대해 알아봅니다. 💡 [참고] Spring Boot Quartz의 이론에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다. [Java] 스케

adjh54.tistory.com

 
 

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 환경설정 부분을 설정해 주시면 됩니다.
 

[Java] Spring Boot Firebase Cloud Message(FCM) 푸시 메시지 API 구축 : firebase-admin

해당 글에서는 Spring Boot API에서 FCM으로 통신하여 FCM Push Message 전송 API 구축을 하는 방법에 대해 알아봅니다. 💡 [참고] FCM 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움

adjh54.tistory.com

 
 
 

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을 기반으로 전송된 푸시 메시지를 디바이스에서 확인하였습니다.

 
 

 

 

💡 [참고] 해당 내용의 소스코드는 아래에서 확인이 가능합니다.
 

blog-codes/spring-boot-scheudler at main · adjh54ir/blog-codes

Contributor9 티스토리 블로그 내에서 활용한 내용들을 담은 레포지토리입니다. Contribute to adjh54ir/blog-codes development by creating an account on GitHub.

github.com

 

 

 


 
 
오늘도 감사합니다. 😀
 
 
 
 
 
 

반응형