본문 바로가기
Spring/SpringBoot

[SpringBoot] SpringBatch 사용하기 (3) - Quartz로 배치 만들기

by J4J 2023. 9. 3.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 SpringBatch 사용하기 세 번째인 Quartz로 배치 만드는 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

이전 글

 

[SpringBoot] SpringBatch 사용하기 (1) - Scheduler를 이용하여 Tasklet, Chunk 배치 만들기

[SpringBoot] SpringBatch 사용하기 (2) - Job Parameter 활용 및 동일 Job 반복 실행

 

 

반응형

 

 

Quartz란?

 

Quartz는 Spring에서 배치 프로세스를 만들 수 있는 오픈소스 라이브러리 중 하나로 작은 단위의 서비스부터 시작하여 큰 규모의 서비스들까지 모두 활용해 볼 수 있습니다.

 

이전 글들을 확인해보면 Spring Batch를 이용하여 배치를 만들어내는 것들을 볼 수 있었는데, Quartz를 사용한다면 Spring Batch의 역할을 대체할 수 있습니다.

 

하지만 개인적으로 단순 배치를 돌린다는 목적에서는 Spring Batch와 Quartz는 큰 차이를 경험할 수 없었습니다.

 

그래서 작은 규모의 서비스에서 배치를 돌린다고 할 때 둘 중 어떤 것을 꼭 사용해야 한다고 말할 순 없을 것 같습니다.

 

 

 

그럼에도 불구하고 SpringBatch 대신 Quartz를 사용해야 하는 경우가 있습니다.

 

Quartz GitHub을 확인해보면 Quartz는 클러스터링 기능을 제공해주고 있습니다.

 

즉, 큰 규모의 서비스들에서 배치를 만들어야 할 때 Scale-Out이 되어 있는 여러 배치 서버들에서 동일한 배치 비즈니스 로직이 모두 실행되는 것이 아니라 서버는 여러 대여도 한 번만 배치가 동작될 수 있도록 도와줍니다.

 

 

 

 

Job / Trigger / Scheduler

 

Quartz 소스를 작성하기 전 알고 있어야 되는 Job, Trigger, Scheduler라는 개념이 있습니다.

 

 

 

먼저 Job은 실행할 작업과 관련된 정보들이 담기는 곳입니다.

 

배치가 실행될 때 관련된 비즈니스 로직을 처리하는 곳이라고 생각할 수 있습니다.

 

 

 

Trigger는 Job의 실행 스케줄링을 어떤식으로 수행할 지에 대한 정보들이 담기는 곳입니다.

 

시간 단위로 스케줄링을 할지, cron 표현식으로 스케줄링을 할지에 대한 정보들을 정의할 수 있습니다.

 

 

 

Scheduler는 Job과 Trigger를 관리하는 곳입니다.

 

어떤 Job을 어떤 Trigger로 활용하여 동작시킬지 등을 정의할 수 있습니다.

 

 

 

 

사용 방법

 

개념에 대해 간단히 알게 되었으니 어떤 식으로 사용해볼 수 있는지 확인해 보겠습니다.

 

이전 글들에서 했던 것처럼 주기적으로 로그를 등록하는 배치를 Quartz를 활용하여 다음과 같이 작성해 볼 수 있습니다.

 

 

 

[ 1. dependency 추가 ]

 

dependencies {
	// quartz
	implementation 'org.springframework.boot:spring-boot-starter-quartz'
}

 

 

 

[ 2. Job 클래스 추가 ]

 

Job 클래스를 작성할 때 한 가지 주의사항은 어노테이션을 이용한 DI를 사용할 수 없다는 것입니다.

 

즉, 로그 등록을 위해서는 JpaRepository를 주입받아 활용해야 하는데 Job 클래스 내부에서 바로 주입받아 사용할 수 없기 때문에 아래에서 작성될 Schedule Config 에서 생성한 applicationContext를 활용하여 Bean을 호출해야 합니다.

 

package com.quartz.job;

import com.quartz.entity.LogEntity;
import com.quartz.repository.LogJpaRepository;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LogRegisterJob implements Job {

    /**
     * Job에서 실행될 작업을 정의한다.
     */
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // Schedule 설정에서 넣어둔 applicationContext를 가져와 Bean 호출에 활용
        ApplicationContext applicationContext = (ApplicationContext) context.getJobDetail().getJobDataMap().get("applicationContext");
        // Job에서 @Autowired, @RequiredArgsConstructor 등의 주입이 불가하기 때문에 applicationContext 사용
        LogJpaRepository logJpaRepository = applicationContext.getBean(LogJpaRepository.class);

        // 로그 데이터 적재
        logJpaRepository.save(
                LogEntity
                        .builder()
                        .contents("logRegisterJobContents")
                        .createAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                        .updateAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                        .build()
        );
    }
}

 

 

 

[ 3. Schedule Config 클래스 추가 ]

 

package com.quartz.config;

import com.quartz.job.LogRegisterJob;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class LogRegisterScheduleConfig {

    private final ApplicationContext applicationContext;

    @Bean
    public void start() throws SchedulerException {
        // Job에서 Bean을 가져와 사용하기 위해 활용
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("applicationContext", applicationContext);

        // Job을 이용하여 JobDetail을 생성
        JobDetail jobDetail =
                JobBuilder
                        .newJob(LogRegisterJob.class) // 실행될 Job
                        .setJobData(jobDataMap) // Job에서 활용될 jobDataMap 등록
                        .withIdentity("LogRegisterJobDetailName", "LogRegisterJobDetailGroup") // 식별을 위한 Identity 등록 (name, group)
                        .build();

        // 스케줄링을 위한 Trigger 등록
        Trigger trigger =
                TriggerBuilder
                        .newTrigger()
                        .withIdentity("LogRegisterTriggerName", "LogRegisterTriggerGroup") // 식별을 위한 Identity 등록 (name, group)
                        .startNow() // 실행과 동시에 스케줄링 시작
                        // 5초 간격으로 무한히 스케줄링 처리
                        .withSchedule(
                                SimpleScheduleBuilder
                                        .simpleSchedule()
                                        .withIntervalInSeconds(5)
                                        .repeatForever()
                        )
                        .build();

        // Scheduler로 Job과 Trigger 관리
        Scheduler scheduler = new StdSchedulerFactory().getScheduler();
        scheduler.start();
        scheduler.scheduleJob(jobDetail, trigger);
    }
}

 

 

 

위와 같이 설정 및 소스 작성을 완료해 보면 5초마다 로그 데이터가 적재되는 것을 확인할 수 있습니다.

 

하지만 위의 설정은 Quartz에서 제공하는 클러스터링 기능을 제공하지 않습니다.

 

다음 글에서 어떻게 클러스터링 기능까지 적용할 수 있는지 적어보겠습니다.

 

 

 

 

 

 

 

 

이상으로 SpringBatch 사용하기 세 번째인 Quartz로 배치 만드는 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글