-
Notifications
You must be signed in to change notification settings - Fork 3
Tech SpringBatch
wjkim9 edited this page Jul 23, 2025
·
14 revisions
뉴스 데이터를 외부 API로부터 수집하여 데이터베이스에 저장하는 과정에서, 초기에는 아래와 같은 구조를 사용했습니다:
- API에서 한 번에 100건의 데이터를 가져옴
- JPA를 통해 100건을 각각 DB에 insert
- 결과적으로 API 호출 한 번마다 DB에 100번의 insert 쿼리 발생 → 매우 비효율적
이를 개선하기 위해 Spring Batch를 도입하여 다음과 같은 구조로 변경했습니다:
- 섹션별로 API 데이터를 최대 10,000건까지 메모리에 적재
- 적재된 데이터를 한 번에 Bulk Insert 처리
- 처리 효율 및 속도가 크게 개선됨
또한, Spring Batch는 다음과 같은 장점을 제공하기 때문에 채택하게 되었습니다:
- 대용량 데이터에 적합한 Chunk 기반 처리
- Job/Step 재시작, 에러 복구, 트랜잭션 관리 등 안정적인 일괄 처리 지원
- Spring 생태계와의 높은 통합성
| 대안 방식 | 단점 요약 |
|---|---|
@Scheduled + 서비스 호출 |
재시작 불가, 트랜잭션 처리 복잡, 예외 복구 불안정 |
| Quartz | 스케줄링에 강점 있으나 대량 데이터 배치에 필요한 구조는 직접 구현 필요 |
| Java 기본 스케줄러 | (Timer, ExecutorService)는 복잡한 작업 흐름 관리에 적합하지 않음 |
위 대안들은 모두 대규모 배치 처리에서는 상태 추적, 장애 복구, 트랜잭션 관리 측면에서 신뢰도가 떨어졌습니다.
| 구분 | 내용 |
|---|---|
| ✅ 이득 | 10,000건 이상 데이터를 한 번에 처리 → DB 액세스 최소화로 성능 약 27% 향상 |
| ✅ 이득 | 안정적인 트랜잭션 관리, 실패 시 재시도, Skip 처리 등 생산성 및 안정성 향상 |
| ✅ 이득 | Job, Step, Execution 등 실행 흐름 추적 가능 → 유지보수 용이 |
| 러닝 커브 존재, 설정이 복잡함 (JobBuilder, StepBuilder, Chunk Size 등 고려 필요) | |
| 실시간 처리에는 부적합 (주기적 일괄 처리 전용 구조) |
| 처리 방식 | 전체 처리 시간 (초) |
|---|---|
| 기존 JPA 반복 삽입 방식 | 769.19 |
| Spring Batch 일괄 삽입 방식 | 562.43 |
→ 약 206초(≈27%) 단축, 같은 데이터 양을 더 빠르고 안정적으로 처리할 수 있게 되었음.
Spring Batch는 다음과 같은 구성으로 작동합니다:
Job → Step → Reader → Processor → Writer
- Reader: API 또는 DB로부터 데이터를 읽어옴
- Processor: 중복 제거, 전처리, 필터링 등 수행
- Writer: 가공된 데이터를 일괄 Insert (현재는 JPA 기반)
예시 플로우:
- API로부터 섹션별 뉴스 10,000건 수집
- Processor에서 중복 필터링
- Writer에서 RDBMS 또는 Elasticsearch에 일괄 저장
→ 이 모든 흐름은 배치 Job으로 실행되며, 수동 트리거나 스케줄(Cron)로 주기 실행 가능
뉴스 수집 배치는 Job-Step 구조로 구성되며, 한 번의 실행으로 최대 10,000건까지 뉴스 데이터를 수집 및 저장합니다.
@Bean
public Job newsDataSaveJob(JobRepository jobRepository,
Step newsDataSaveStep,
BatchJobCompletionListener listener) {
return new JobBuilder("newsDataSaveJob", jobRepository)
.start(newsDataSaveStep)
.listener(listener)
.build();
}
@Bean
public Step newsDataSaveStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager,
ItemReader<NewsItemDTO> apiReader,
ItemProcessor<NewsItemDTO, News> newsProcessor,
ItemWriter<News> newsWriter) {
return new StepBuilder("newsDataSaveStep", jobRepository)
.<NewsItemDTO, News>chunk(10_000, transactionManager)
.reader(apiReader)
.processor(newsProcessor)
.writer(newsWriter)
.build();
}위와 같이 구성된 Job은 Reader → Processor → Writer 순으로 동작하며, 10,000건씩 일괄 처리됩니다.
@StepScope
@Bean
public ItemReader<NewsItemDTO> apiReader(
@Value("#{jobParameters['section']}") String section,
@Value("#{jobParameters['offset']}") Long offset
) {
// 하루 전 날짜 구하기
LocalDate day = LocalDate.now().minusDays(1);
String date = day.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
List<NewsItemDTO> newsList = new ArrayList<>();
NewsResponseDTO newsResponseDTO = getAPIResponse(1, section, 100, date, date);
int totalPages = getTotalPagesOfResponse(newsResponseDTO, offset);
for (int page = 1; page <= totalPages; page++) {
getNewsList(page, section, 100, date, date, newsList);
}
return new ListItemReader<>(newsList);
}모든 페이지의 뉴스를 미리 가져와 메모리에 적재하고, 이후 하나씩 읽어가는 구조입니다. 실시간 API 호출을 피하고, 대량 데이터를 한 번에 처리할 수 있도록 구성했습니다.
@Bean
public ItemWriter<News> newsWriter(DataSource dataSource) {
JdbcBatchItemWriter<News> writer = new JdbcBatchItemWriter<>();
writer.setDataSource(dataSource);
writer.setSql("""
INSERT INTO news (title, summary, content_url, published_at, send, sections, publisher)
VALUES (:title, :summary, :contentUrl, :publishedAt, :send, :sections, :publisher)
""");
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
writer.afterPropertiesSet();
return writer;
}JPA 대신 JdbcBatchItemWriter를 사용하여 실제 DB 삽입 쿼리를 직접 실행합니다. 이를 통해 JPA의 성능 병목을 피하고, 10,000건 일괄 삽입이 가능해졌습니다.
Start
↓
\[Reader]
* 뉴스 API 호출
* 섹션별 전체 데이터 수집
* ListItemReader에 저장
↓
\[Processor]
* 중복/결측 필터링
↓
\[Writer]
* 10,000건씩 JDBC 일괄 INSERT
* RDBMS 저장 완료
| 섹션 | 추천 코드 예시 | 위키 목적 |
|---|---|---|
| Job/Step 정의 |
newsDataSaveJob, newsDataSaveStep
|
구조 설명 및 chunk 처리 흐름 강조 |
| Reader 구성 |
apiReader, ListItemReader
|
도메인 기반 뉴스 수집 로직 시각화 |
| Writer 구성 | JdbcBatchItemWriter |
성능 최적화 및 JPA 대비 장점 부각 |
| 항목 | 설명 |
|---|---|
| Writer 최적화 | 현재 JPA ItemWriter 사용 → JdbcBatchItemWriter 로 변경 시 성능 향상 가능 |
| Job 시각화 | Spring Batch Admin, Spring Boot Actuator, Dashboard 도입 고려 |
| 병렬 처리 | 멀티스레드 기반 Step 병렬 처리로 처리 시간 단축 가능 |
| 동적 파라미터 처리 | 날짜, 섹션 등 동적 파라미터 기반 Job 실행 기능 추가 고려 |
- 기존 방식: API 100건 호출 → JPA 100번 insert → 비효율적
- 개선 방식: 최대 10,000건 적재 후 일괄 insert → DB 액세스 횟수 대폭 감소
- 결과: 처리 속도 및 자원 사용량 개선, 운영 안정성 향상