3 min read
Part 12. 통합 레퍼런스 아키텍처와 최종 선택 가이드
@Scheduled, Quartz, Spring Batch, 수동 배치를 실제 운영 조건에 맞게 조합하는 최종 의사결정 매트릭스와 참조 아키텍처를 제시한다.
Series: Spring Boot 배치 전략 완전 정복
총 12편 구성. 현재 12편을 보고 있습니다.
- 01Part 1. 배치의 본질과 분류: 스케줄, 이벤트, 수동, 대량, Near-real-time
- 02Part 2. @Scheduled 실전 운영: 단순함의 대가와 멀티 인스턴스 함정
- 03Part 3. Quartz 클러스터 아키텍처: JobStore, Misfire, 대규모 스케줄 관리
- 04Part 4. Spring Batch 핵심: Chunk, 트랜잭션 경계, Restart 가능한 Job 설계
- 05Part 5. Spring Batch 확장: Partition과 Multi-threaded Step의 트레이드오프
- 06Part 6. 수동 배치 전략: REST 트리거, Admin UI, 파라미터 재처리, 롤백
- 07Part 7. DB 대량 조회 전략: OFFSET/LIMIT 한계와 Keyset, ID Range, Covering Index
- 08Part 8. OpenSearch/Elasticsearch 배치 전략: Scroll, Search After, PIT, Bulk, Rollover
- 09Part 9. 분산 환경 배치: Leader Election, Kubernetes CronJob, 락 전략 비교
- 10Part 10. 성능 최적화: 배치 사이즈, 커밋 간격, JVM 메모리, Backpressure
- 11Part 11. 장애 대응 아키텍처: Partial Failure, Poison Data, DLQ, 재시도, 멱등성
- 12Part 12. 통합 레퍼런스 아키텍처와 최종 선택 가이드CURRENT

출처: Pexels - Server racks on data center
버전 기준
- Java 21
- Spring Boot 3.3.x
- Spring Batch 5.2.x
- Quartz 2.3.x
- PostgreSQL 15
- OpenSearch 2.x
1) 문제 제기
실무에서 흔한 실패는 기술 선택 자체가 아니라 "모든 배치를 하나의 도구로 해결하려는 시도"에서 시작된다. 정시 정산, 재색인, 운영자 재처리, 이벤트 보정은 요구사항이 다르다. 따라서 단일 정답은 없다. 정답은 조합이다.
이 최종편의 목표는 두 가지다.
- 요구사항을 빠르게 분류해 적절한 실행 모델을 고른다.
- 장애/성능/운영 복잡도를 함께 고려한 현실적인 기준을 제공한다.
2) 핵심 개념 정리
통합 선택 매트릭스
| 요구사항 | 1차 선택 | 2차 보완 | 비고 |
|---|---|---|---|
| 단순 주기 작업(인스턴스 1~2) | @Scheduled | 멱등 키 + 실행 이력 | 빠른 시작에 유리 |
| 복잡 스케줄/캘린더/Misfire | Quartz | JDBCJobStore + 대시보드 | 운영 제어 강함 |
| 대량 데이터 변환/재시작 필요 | Spring Batch | Keyset Reader + Chunk 튜닝 | 표준 선택 |
| 운영자 개입 재처리 | 수동 배치 API/UI | 권한/감사/롤백 | 통제/재현성 확보 |
| 대량 검색 색인 | Spring Batch + OpenSearch Bulk | PIT + Search After | 검색 클러스터 한도 반영 |
실행 계층 분리 원칙
- 트리거 계층:
@Scheduled/Quartz/K8s CronJob - 처리 계층: Spring Batch Step/Chunk
- 데이터 계층: DB Keyset/Range + OpenSearch Bulk
- 제어 계층: 락/리더 선출/멱등성
- 운영 계층: 관측성/경보/DLQ/수동 재처리
참조 아키텍처 다이어그램
Mermaid diagram rendering...

출처: Pexels - Security control room team
3) 코드 예시
예시 A: 통합 오케스트레이터 인터페이스
public interface BatchOrchestrator {
Long launchScheduled(String jobName, LocalDateTime scheduledAt);
Long launchManual(String jobName, Map<String, String> params, String requestedBy);
Long launchRecovery(String jobName, Long failedExecutionId);
}
@Service
@RequiredArgsConstructor
public class DefaultBatchOrchestrator implements BatchOrchestrator {
private final ExecutionGuard executionGuard;
private final JobLauncher jobLauncher;
@Override
public Long launchScheduled(String jobName, LocalDateTime scheduledAt) {
String dedupKey = jobName + ":" + scheduledAt.toString();
executionGuard.assertNotRunning(dedupKey);
return run(jobName, Map.of("scheduledAt", scheduledAt.toString(), "dedupKey", dedupKey));
}
private Long run(String jobName, Map<String, String> params) {
// 실행 이력 저장 후 Spring Batch Job launch
return 1L;
}
}
예시 B: 운영 대시보드용 SQL
SELECT job_name,
COUNT(*) FILTER (WHERE status = 'SUCCEEDED') AS success_count,
COUNT(*) FILTER (WHERE status = 'FAILED') AS failed_count,
AVG(duration_ms) AS avg_duration_ms,
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY duration_ms) AS p99_duration_ms
FROM batch_job_execution
WHERE started_at >= NOW() - INTERVAL '24 hour'
GROUP BY job_name
ORDER BY failed_count DESC, p99_duration_ms DESC;
예시 C: 재처리 대상 Keyset 조회
SELECT id, job_name, error_code, started_at
FROM batch_job_execution
WHERE status = 'FAILED'
AND id > :last_id
ORDER BY id ASC
LIMIT 300;
예시 D: OpenSearch 증분 동기화 Query
POST /_search
{
"size": 1000,
"sort": [
{ "updated_at": "asc" },
{ "_id": "asc" }
],
"search_after": ["2026-03-03T08:00:00Z", "product-9988"],
"query": {
"range": {
"updated_at": { "gte": "2026-03-03T00:00:00Z" }
}
}
}
4) 실제 장애/운영 시나리오
상황: 블랙프라이데이 트래픽에서 주문 정산(Quartz), 상품 색인(Spring Batch+OpenSearch), 운영자 재처리 API가 동시에 증가했다. 공통 DB 커넥션 풀이 고갈되며 전체 배치가 지연되고 일부는 timeout으로 실패했다.
원인:
- 각 배치가 개별 최적화되어 전체 시스템 한도 모델이 없었다.
- 리소스 우선순위(정산 > 재색인 > 리포트)가 설정되지 않았다.
- 실패 배치 재시도가 즉시 수행되어 부하를 증폭했다.
개선:
- 배치 유형별 리소스 버짓(CPU/DB pool/IO) 고정.
- 우선순위 큐 도입과 저우선 작업의 자동 지연.
- 실패 재시도에 지수 백오프 + 혼잡 시간대 실행 금지 윈도우 적용.
5) 설계 체크리스트
- 배치 요구사항을 스케줄/이벤트/수동/대량/NRT로 먼저 분류하는가?
- 실행 주체(
@Scheduled/Quartz/K8s/수동)를 명확히 정의했는가? - DB 조회 전략을 Keyset/Range 중심으로 설계했는가?
- OpenSearch는 PIT + Search After + Bulk 제어를 사용하는가?
- 락/리더 선출/멱등성/펜싱 토큰이 함께 설계되어 있는가?
- DLQ와 재처리 운영 절차가 문서화되어 있는가?
- 성능 목표와 장애 복구 목표(RTO/RPO)가 수치로 관리되는가?
6) 요약
이 시리즈의 결론은 단순하다. 배치는 기술 문제가 아니라 운영 시스템 문제다. @Scheduled, Quartz, Spring Batch, 수동 배치는 경쟁 관계가 아니라 역할 분담 관계다. 요구사항, 실패 모델, 운영 역량에 맞춰 조합해야 한다.
7) 다음 편 예고
이번 편으로 시리즈는 종료된다. 후속 글에서는 실제 샘플 프로젝트를 기반으로 "단일 서비스에서 4가지 배치 모델을 공존"시키는 구현 레포 구조와 관측 대시보드 템플릿을 다룰 예정이다.
참고 링크
- Spring Batch Reference
- Quartz Scheduler Documentation
- PostgreSQL Transaction Isolation
- 블로그: Idempotency Key API 설계
시리즈 네비게이션
- 이전 글: Part 11. 장애 대응 아키텍처: Partial Failure, Poison Data, DLQ, 재시도, 멱등성
- 다음 글: 없음 (이 시리즈의 마지막편)