개발기술/Spring
배치처리
bsh6226
2025. 4. 21. 08:24
배치 처리란?
한꺼번에(일괄로) 많은 데이터를 처리하는 방식. 주로 사용자 개입 없이, 예약된 시점에 자동 실행됨
배치 처리의 구성 요소 (Spring 기준)
Spring에서는 보통 @Scheduled 또는 Spring Batch를 이용해 구현해.
@Scheduled (간단한 주기성 작업)
- 가볍고 단순한 배치에 적합
- 트랜잭션/재시도/병렬처리 기능 없음
@Scheduled(cron = "0 0 0 * * *") // 매일 자정
public void sendDailyReport() {
// 작업 수행
}
Spring Batch (전문 배치 프레임워크)
@Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.start(step1())
.next(step2())
.build();
}
insert 성능 최적화 = batch insert
✔ 대량처리 안정성 + 트랜잭션 + 재시도 등 파이프라인 운영은 Spring Batch의 몫
대규모 일괄 작업에 최적화된 프레임워크
- 청크 처리 (N개 단위 커밋) : 예: 1000개씩 데이터를 읽고 → 처리하고 → 저장 후 커밋, 실패 시 해당 청크만 재시도 가능.
- 트랜잭션 관리 : 각 청크(작업 단위)마다 별도 트랜잭션으로 묶어 실패 시 롤백 가능.
- 재시도/중단/재개 기능 : 장애 발생 시 실패한 부분부터 이어서 실행 가능 (JobRepository에 상태 저장).
- Job/Step 단위 분리 가능 : 작업 전체(Job)와 그 하위 단계(Step)를 구조적으로 분리하여 유연한 처리 가능.
@Scheduled | Spring Batch | |
🧠 목적 | 단순 주기적 실행 | 대규모 배치 처리에 최적화 |
🔁 실행 방식 | 메서드 단위로 주기 실행 | Job/Step 기반으로 처리 흐름 정의 |
⚙ 트랜잭션 제어 | 개발자가 직접 제어 | 프레임워크에서 제공 |
🧾 상태 관리 | 기본적으로 없음 | 실행 상태(JobInstance), 실패/재시도 관리 |
📦 병렬 처리 | Executor 설정 필요 | 기본 기능으로 분할/병렬 처리 지원 |
📑 로그, 모니터링 | 수동 구현 | JobRepository 통해 자동 관리 가능 |
💥 장애 복구 | 수동 처리 | 중단 지점부터 재시작 가능 (restartable) |
🔍 예제 중심 비교
예시 1. 단순한 API 호출 (공공 데이터 저장)
@Scheduled(cron = "0 0 1 * * *") // 매일 1시에 실행
public void getHolidayInfo() {
List<Holiday> holidays = webClient.get(...);
holidayRepository.saveAll(holidays);
}
항목 | 설명 |
작업 크기 | 작음 (몇 백 개 수준) |
실패 처리 | 실패하면 그냥 로그만 남기고 끝 |
트랜잭션 | 크게 중요하지 않음 |
재시작 필요 | 없음 |
병렬 처리 | 필요 없음 |
예시 2. 수십만 고객의 포인트 정산
항목 | 설명 |
작업 크기 | 매우 큼 (수십만 건 이상) |
실패 처리 | 실패 시 롤백, 재시도 필요 |
트랜잭션 | 청크 단위로 커밋 |
재시작 필요 | 중간부터 이어서 가능해야 함 |
병렬 처리 | 고객별로 병렬 처리 유리 |
✅ 적합 | Spring Batch 사용 권장 |
@Bean
public Step pointCalcStep() {
return stepBuilderFactory.get("pointCalcStep")
.<Customer, PointResult>chunk(1000)
.reader(customerReader())
.processor(pointCalculator())
.writer(pointWriter())
.build();
}
JPA 관점의 batch insert
JPA에서는 saveAll() 등을 통해 여러 개의 엔티티를 한꺼번에 persist하면,
내부적으로 EntityManager.persist()를 반복 호출하게 됩니다.
이때 persistence context (1차 캐시) 에 엔티티가 계속 쌓이는데,
너무 많아지면 메모리 사용 증가 + 성능 저하 + OutOfMemoryRisk 가 생길 수 있어요.
그래서 하는 게?
일정 수만큼 저장 후 flush() + clear() 호출 👉 batch 처리처럼 동작
java
복사
편집
for (int i = 0; i < entities.size(); i++) {
entityManager.persist(entities.get(i));
if (i % 100 == 0) {
entityManager.flush(); // DB에 반영
entityManager.clear(); // persistence context 초기화