Skip to the content.

배치 처리나 주기적인 백그라운드 작업이 필요한 대규모 엔터프라이즈 시스템에서 신뢰성 높은 작업 예약을 위해 필수적으로 고려되는 자바 오픈소스 라이브러리가 있습니다. 바로 Quartz Job Scheduler입니다. 스프링 기본 스케줄러(@Scheduled)가 단일 JVM 내에서 단순 실행을 지원하는 데 비해, Quartz는 다중 서버(Clustering) 환경에서의 트랜잭션 관리와 Fail-over 기능을 네이티브하게 제공합니다.

이번 포스팅에서는 Quartz의 핵심 아키텍처와 구성요소를 분석하고, 실무 다중화 인프라 환경에서 안정적으로 스케줄러를 가동하기 위한 JDBCJobStore 기반 클러스터링 설정에 대해 알아보겠습니다.


1. Quartz의 3대 핵심 구성 요소

Quartz 아키텍처를 이해하려면 다음 세 가지 객체의 관계와 역할을 정립해야 합니다.

                  ┌──────────────────────┐
                  │      Scheduler       │
                  └──────────┬───────────┘
                             │ (스케줄링 등록 및 제어)
             ┌───────────────┴───────────────┐
             ▼                               ▼
    ┌────────────────┐              ┌────────────────┐
    │   JobDetail    │              │    Trigger     │
    │ (실행할 작업 정의)  │              │ (언제 실행할지 지정)  │
    └────────────────┘              └────────────────┘

1) Job & JobDetail

2) Trigger

3) Scheduler


2. Spring Boot와 Quartz 연동 예시 코드

Spring Boot 환경에서는 spring-boot-starter-quartz 의존성을 추가하면 간편하게 빈으로 등록하여 연동할 수 있습니다.

Job 구현체

public class HelloWorldJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String message = dataMap.getString("message");
        System.out.println("======> Quartz Job 실행! 메시지: " + message);
    }
}

Configuration 설정

@Configuration
public class QuartzConfig {

    @Bean
    public JobDetail helloWorldJobDetail() {
        return JobBuilder.newJob(HelloWorldJob.class)
                .withIdentity("helloWorldJob", "group1")
                .usingJobData("message", "안녕하세요, Quartz입니다.")
                .storeDurably() // 트리거가 없이도 스케줄러에 저장 유지
                .build();
    }

    @Bean
    public Trigger helloWorldJobTrigger(JobDetail helloWorldJobDetail) {
        return TriggerBuilder.newTrigger()
                .forJob(helloWorldJobDetail)
                .withIdentity("helloWorldTrigger", "group1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")) // 10초마다 실행
                .build();
    }
}

3. 다중 서버 환경에서의 클러스터링: JDBCJobStore

서버가 2대 이상 실행되는 이중화 환경에서 내장 메모리(RAMJobStore)를 사용하여 스케줄러를 돌릴 경우, 각 서버마다 동일한 시간이 되어 스케줄을 똑같이 가동해 중복 데이터 생성이나 리소스 낭비가 초래되는 심각한 문제가 발생합니다.

이를 해결하기 위해 Quartz는 데이터베이스 테이블을 공유하여 동시성 제어를 수행하는 JDBCJobStore 클러스터링을 제공합니다.

1) 동작 메커니즘

2) 설정 파일 (application.yml)

spring:
  quartz:
    job-store-type: jdbc # 데이터베이스 기반으로 스토어 설정
    jdbc:
      initialize-schema: always # 최초 기동 시 DB 스키마 자동 생성
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO # 서버별 고유 인스턴스 ID 부여
          jobStore:
            isClustered: true # 클러스터 활성화
            clusterCheckinInterval: 20000 # 클러스터 노드 감지 주기 (20초)

스프링 배치가 대량의 일괄 데이터 처리를 주 목적으로 삼는다면, Quartz는 비즈니스 이벤트 시점의 정밀한 실행 예약 및 조작 제어를 담당합니다. 다중화 인프라 하에서 견고하게 구동될 스케줄러 시스템 설계를 목표로 하신다면, 락 분할 제어 성능을 고려한 JDBC JobStore 스키마 설정을 적극 검토해야 합니다.