글을 작성하게 된 계기
다른 스케줄링 시스템도 많은데 왜 쿼츠를 사용해야만 하는지 에 대해 고민하게 됐고, 이를 정리하기 위해 글을 작성하게 되었습니다.
1. 다른 스케줄링 시스템과 비교
스케줄링 시스템은 사용하는 환경, 목적, 복잡도에 따라 다양한 종류가 존재합니다. 이 중에서 쿼츠 선택해야 하는 상황을 판단하려면, 다른 주요 스케줄러들과의 차이를 비교해보는 것이 중요합니다.
| 스케줄링 시스템 | 주요 특징 | 장점 | 단점 |
|---|---|---|---|
| Quartz | 자바 기반 정교한 스케줄러 | · 정교한 cron 표현 가능 · 상태 저장 · 분산 실행 지원 · 코드로 제어 가능 | · 복잡한 설정 · JVM 환경 종속 |
| AWS EventBridge | AWS 네이티브 이벤트 중심 스케줄링 | · 서버리스 구조 · AWS 서비스 간 연동 용이 | · AWS 종속 · 상태 기반 제어 어려움 |
| Apache Airflow | DAG 기반 복잡한 워크플로 정의 | · 의존성 기반 실행 · 강력한 UI/모니터링 | · 러닝 커브 있음 · 반복 작업에 과한 구조 |
| Kubernetes CronJob | 쿠버네티스에서 주기적 컨테이너 실행 | · 선언적 구성 · 클라우드 네이티브에 적합 | · 상태 관리 어려움 · 실패 대응 복잡 |
Spring @Scheduled | Spring 내장 단순 스케줄러 | · 코드 수준에서 간편 적용 · 설정이 단순 | · 단일 인스턴스 한정 · 분산 환경 미지원 |
| Jenkins | CI/CD 중심 파이프라인 스케줄링 | · Git 연동 쉬움 · 플러그인 풍부 · 배포 자동화 용이 | · 스케줄링은 부가 기능 · 워크플로 제어 한계 |
1-1. Quartz vs AWS EventBridge
Quartz는 JVM 기반 애플리케이션 내부에서 정밀한 cron 표현과 상태 기반 작업 제어가 필요한 경우에 적합합니다. 반면 AWS EventBridge는 AWS 생태계에 최적화된 서버리스 이벤트 스케줄러로, Lambda, SQS 등과의 연동에 강점을 가집니다. 단, 상태 저장이나 복잡한 작업 흐름에는 적합하지 않습니다.
복잡한 내부 로직 제어가 필요한 경우 → Quartz / 단순 트리거 중심의 클라우드 연동 → EventBridge
1-2. Quartz vs Apache Airflow
Airflow는 DAG 기반으로 작업 간 의존성을 시각적으로 정의하고 관리하는 데 특화되어 있습니다. 복잡한 데이터 파이프라인, ETL, 분석 작업에 최적화되어 있으며, UI와 모니터링이 매우 강력합니다. 반면 쿼츠는 DAG 개념 없이 시간 중심 트리거에 초점을 맞춘 코드 기반 스케줄러이며, 단순하지만 빠르고 직접적인 제어가 가능합니다.
데이터 파이프라인 등 복잡한 의존 흐름 필요 → Airflow / 시간 중심 제어와 분산 처리 필요 → Quartz
1-3. Quartz vs Kubernetes CronJob
Quartz는 JVM 환경에서 cron 기반으로 세밀한 제어가 가능하며, DB에 작업 상태를 저장하거나 분산 환경에서 실행할 수 있습니다. 반면 Kubernetes CronJob은 클러스터 내에서 컨테이너를 주기적으로 실행하는 데 유용하며, 선언적으로 구성할 수 있어 DevOps 관점에서 편리합니다. 하지만 작업 상태 추적이나 조건 기반 흐름 제어는 어렵습니다.
컨테이너 기반 주기 작업, 쿠버네티스 환경 → CronJob / 애플리케이션 내부 제어 및 상태 관리 → Quartz
1-4. Quartz vs @Scheduled
Spring @Scheduled는 간단한 반복 작업을 빠르게 구현할 수 있도록 도와주는 내장 기능입니다. 설정이 거의 필요 없고 코드 기반으로 직관적이지만, 단일 인스턴스에서만 동작하고 분산 환경이나 상태 저장은 지원하지 않습니다. 반면 Quartz는 분산 환경 대응과 DB 기반 상태 관리가 가능하며, 다양한 트리거 전략도 제공합니다.
단순한 반복 로직 → @Scheduled / 분산 환경 + 상태 관리 + 유연한 트리거 → Quartz
1-5. Quartz vs Jenkins
Jenkins는 CI/CD 자동화를 위한 도구로, Git 연동, 빌드, 테스트, 배포에 최적화되어 있습니다. 스케줄링은 부가 기능 수준이며 복잡한 흐름 제어나 상태 기반 실행에는 한계가 있습니다. 반면 Quartz는 비즈니스 로직 수준에서 복잡한 스케줄링을 직접 관리할 수 있으며, CI/CD 외의 업무성 배치 로직이나 내부 트리거 관리에 강점을 가집니다.
코드 배포, 테스트 자동화 → Jenkins / 비즈니스 로직 중심 스케줄링 → Quartz
비교 결과를 종합해보면 쿼츠를 사용하는 이유는 비즈니스 로직 중심의 정밀한 스케줄링, 상태 저장, 분산 환경 대응 이 가능하기 때문입니다. 간단한 반복 작업이나 외부 서비스 연동은 다른 도구들이 더 적합할 수 있지만, 쿼츠는 로직을 작성할 수 있기 때문에, 즉 코드로 제어할 수 있기 때문에 내부 로직 제어가 중요한 업무성 배치 작업 에서는 더 유용합니다.
물론 어떤 방식으로든 쿼츠를 대체할 수는 있습니다. 쿼츠는 코드로 제어할 수 있다는 점이 가장 큰 장점이란 것이죠. 🚀
2. 쿼츠를 사용해야 할 때
쿼츠는 단순한 반복 작업이 아닌, 상태 기반의 비즈니스 로직 스케줄링, 분산 환경 대응, 유지보수가 필요한 복잡한 배치 제어 가 필요한 경우에 사용할 수 있습니다. 특정 조건 에 따라 반복 주기를 동적으로 설정 하거나, 실행 실패 시 재시도 로직 이 필요하고, 실행 여부나 이력을 DB에 저장해 관리 하고자 할 때 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
class ComplexBatchJob(
private val batchTriggerExecutionService: BatchTriggerExecutionService,
private val notificationService: NotificationService
) : Job {
private val logger = LoggerFactory.getLogger(this::class.java)
override fun execute(context: JobExecutionContext) {
val jobId = context.jobDetail.key.name
val dataMap = context.jobDetail.jobDataMap
val currentAttempt = dataMap.getInt("attempt").takeIf { it > 0 } ?: 1
logger.info("💼 Job [$jobId] 실행 시도 #$currentAttempt")
runCatching {
batchTriggerExecutionService.execute(jobId, context)
}.onFailure { ex ->
val retryMaxAttempts = 3
if (currentAttempt < retryMaxAttempts) {
logger.warn("⚠ 재시도 예약 ($currentAttempt/$retryMaxAttempts)")
val nextDataMap = JobDataMap()
nextDataMap["attempt"] = currentAttempt + 1
batchTriggerExecutionService.reschedule(jobId, context, delaySeconds = 60, retryMaxAttempts, nextDataMap)
} else {
logger.error("❌ 최대 재시도 초과. 알림 전송 및 중단", ex)
notificationService.sendFailureAlert(jobId, ex)
}
}
}
}
또는 분산 환경에서도 유용한데, 쿼츠는 데이터베이스에 상태를 저장할 수 있기 때문에 락 을 통해 동일한 작업이 중복 실행되지 않도록 할 수 있으며, 클러스터링 을 통해 멀티 인스턴스 환경에서도 안정적으로 동작할 수 있습니다.
3. 주의할 점
쿼츠의 장점에 대해서만 살펴 봤는데요, 쿼츠를 사용할 때 주의해야 할 점과 몇 가지 한계점도 있습니다.
- 짧은 주기의 Job 대량 처리
- 분산 환경에서의 안정성
- Job 간 순차 실행이나 의존 관계 관리의 어려움
- 쿼츠 서버 중단
3-1. 짧은 주기의 Job 대량 처리
짧은 주기의 Job을 대량으로 처리하는 데는 적합하지 않습니다. 쿼츠는 기본적으로 데이터베이스 기반의 락을 활용하여 작업의 중복 실행을 방지합니다. 이로 인해 수백, 수천 개의 Job이 짧은 주기로 동시에 실행되면, 데이터베이스 락 경합이 발생하고 성능이 급격히 저하될 수 있습니다. 따라서 쿼츠는 CPU를 많이 쓰거나 실행 시간이 긴, 비즈니스 단위의 Job에 더 적합하며, 밀리초 단위로 돌아가는 대규모 이벤트 처리는 Kafka나 별도의 워커 시스템으로 분리하는 것이 일반적입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Quartz A] [Quartz B] [Quartz C]
| | |
Job A1 Job B1 Job C1
| | |
+-----------------+-----------------+
|
Simultaneous Trigger Fire
|
==============================================
|| Shared Quartz Database ||
|| (QRTZ_LOCKS / QRTZ_TRIGGERS tables) ||
==============================================
| | |
Lock A Lock B Lock C
(경합 중...) (경합 중...) (경합 중...)
\_______________|________________/
↓
🔒 DB 락 병목 발생
3-2. 분산 환경에서의 안정성
분산 환경에서의 안정성도 주의해야 합니다. 쿼츠는 클러스터 모드를 지원하며, 각 인스턴스가 주기적으로 데이터베이스에 자신의 상태를 체크인하면서 동기화를 유지합니다. 그러나 네트워크 장애나 서버 간 시계 차이 등으로 인해 체크인이 제대로 되지 않을 경우, 서로 다른 인스턴스에서 같은 Job이 동시에 실행되는 문제가 발생할 수 있습니다. 이러한 문제를 보완하기 위해서는 ShedLock과 같은 외부 분산 락 라이브러리를 병행하거나, Job 자체에 이중 실행 방지 로직을 내장하는 것이 필요합니다.
1
2
3
4
# Quartz A와 Quartz B가 동일한 Job X를 실행 중
Quartz A ──▶ Job X ──▶ Shared DB ◀─ Job X ◀── Quartz B
│ │
└───── 네트워크 장애 ───────────┘
3-3. Job 간 순차 실행이나 의존 관계 관리의 어려움
Job 간 순차 실행이나 의존 관계 관리가 어렵습니다. 쿼츠는 DAG 기반의 워크플로 엔진이 아니기 때문에, 특정 Job이 끝난 뒤 다른 Job을 순서대로 실행하는 기능이 기본적으로는 없습니다. 이를 해결하려면 JobListener나 TriggerListener를 사용하거나, Job 내부에서 명시적으로 다음 Job을 호출하는 방식으로 우회해야 합니다. 하지만 이 방식은 흐름이 분산되어 유지보수가 어려워질 수 있고, 시각적/논리적 워크플로 관리도 어렵다는 한계가 있습니다.
1
2
# JobA는 완료 됐지만 JobB는 수동으로 트리거해야 함
Job A ──▶ 실행 완료 ──▶ Job B 수동 트리거 (scheduler.triggerJob)
3-4. 쿼츠 서버 중단
쿼츠 스케줄러 서버가 다운되면 모든 Job 실행이 중단 됩니다. 즉, 쿼츠는 애플리케이션 내에 내장하거나, 독립된 별도의 서버로 운영해야 합니다. 이 서버가 배포나 장애로 인해 꺼지면 Job은 실행되지 않습니다. 다른 시스템처럼 서버가 죽더라도 자동으로 실행되는 구조 가 아니기 때문에, 쿼츠는 항상 가용성 확보와 헬스체크, 모니터링이 동반되어야 합니다. 고가용성이 필요한 경우에는 반드시 여러 인스턴스를 띄우고 클러스터링 설정을 적용한 후, 데이터베이스 상태를 공유하며 다중 노드에서 동작하도록 구성해야 합니다.
1
2
3
4
5
[❌ Quartz Scheduler] ──▶ [Batch Job 실행 트리거] ──▶ [비즈니스 로직 실행]
▲
│
└── 서버 장애 / 프로세스 다운: Batch Job 트리거 자체가 발생하지 않음
4. 정리
쿼츠는 단순한 반복 작업이 아닌, 상태 기반의 비즈니스 로직 스케줄링, 분산 환경 대응, 유지보수가 필요한 복잡한 배치 제어가 필요한 경우에 적합합니다. 쿼츠를 사용해야 하는 이유를 찾는 과정에서 다른 스케줄링 시스템과 비교해보니, 쿼츠를 왜 사용해야 하는지 명확해졌습니다. 현 회사에서 정산, 거래대사 등 복잡한 비즈니스 로직을 다루는데, 기왕 도입한 쿼츠를 신나게 사용해보겠습니다. 🚀