[BE] Spring Batch 테크스팩 - 100-hours-a-week/2-hertz-wiki GitHub Wiki
배경 (Background)
-
프로젝트 목표 (Objective):
조직 내 매칭 결과를 AI가 자동으로 작성한 ‘튜닝 리포트’ 형태의 뉴스 콘텐츠로 가공하고, 해당 결과를 사용자들에게 정기적 알림으로 전달함으로써 서비스의 재미와 몰입도를 극대화함 -
문제 정의 (Problem):
- 튜닝 리포트 작성은 AI 서버와의 비동기 통신이 필요, 생성 타이밍을 통제하기 어려움
- 수동 트리거 혹은 사용자 요청 기반의 리포트 생성은 사용자 경험을 저해할 수 있음
- 매칭 결과에 대한 알림 전달이 누락되거나 타이밍이 어긋나는 경우, 서비스 신뢰도가 저하됨
-
가설 (Hypothesis):
Spring Batch를 이용해 매칭 상태가 확정된 사용자들에 한해서 알림이 가기 전날 새벽동안 튜닝 리포트를 생성하고, 사용자에게 알림을 발송 → 사용자 참여율과 리텐션이 높아질 수 있음
목표가 아닌 것 (Non-goals)
- 실시간 리포트 생성: 본 시스템은 배치 기반이며, 실시간 생성은 별도 서비스로 관리함
- 개별 리포트 수정 기능: AI가 생성한 리포트는 수정 불가한 콘텐츠로 간주하며, 별도 편집 기능은 고려 대상이 아님
설계 및 기술 문서 (Architecture & Technical Docs)
1. 튜닝 리포트 프로세스 개요
1-1. AI 분석 결과 저장
- AI 서버가 사용자 간 매칭 데이터를 분석하여 백엔드로 전송
- 백엔드는 해당 결과를 DB에 저장 (
tuning_report
내is_visible = false
초기값 저장)
1-2. Spring Batch 실행 트리거 (카테고리 별 해당 요일의 오후 12:30마다)
is_visible = false
인 리포트 항목 조회- 해당 리포트의
is_visible = true
로 상태 변경 - 사용자에게 알림 전송
2. Spring Batch 설계 구성
구성 요소 | 이름 | 설명 |
---|---|---|
Job | TuningReportVisibilityUpdateJob |
하나의 배치 작업 단위이 Job은 일정 주기로 실행되며, 공개되지 않은 튜닝 리포트를 찾아서 공개 처리함 |
Step | updateVisibilityStep |
Job 내부에서 실제 작업이 수행되는 단위 단계한 Step은 Reader → Processor → Writer 순서로 구성됨 |
Reader | JpaPagingItemReader<TuningReport> 또는 JdbcPagingItemReader |
DB에서 아직 공개되지 않은 리포트를 일정 개수씩 조회조회 조건: is_visible = false |
Writer | ItemWriter<TuningReport> 또는 JpaItemWriter |
읽어온 리포트들의 is_visible 값을 true 로 변경하고,배치 작업 종료 후 알림 서비스를 호출해 사용자에게 리포트 공개 알림 전송 |
- Reader 조건 인덱스 최적화 고려
- 현재 기획 상 최근 30일간의 튜닝 리포트만 조회하도록 되어 있으나, 생성되는 튜닝 리포트가 많아질 수록
is_visible = false
조건만으로는 성능 저하가 발생할 수 있으므로 조회 조건에 인덱스 추가
- 현재 기획 상 최근 30일간의 튜닝 리포트만 조회하도록 되어 있으나, 생성되는 튜닝 리포트가 많아질 수록
CREATE INDEX idx_tuning_report_is_visible ON tuning_report (is_visible);
3. 파일별 역할 (domain 패키지 외 생성)
파일명 | 역할 |
---|---|
TuningReportBatchConfig.java |
Job, Step, Chunk, Reader/Writer 등록 |
TuningReportReader.java |
JpaPagingItemReader or JdbcPagingItemReader 설정 분리 |
TuningReportWriter.java |
ItemWriter 구현 → 공개 처리 + 알림 발송 |
TuningReportJobLauncher.java |
JobLauncher로 배치 Job 수동 실행 (테스트 등) |
TuningReportScheduler.java |
@Scheduled 로 주기적 자동 실행 설정 |
- 각 파일에 하나의 역할만 부여하여 단일 책임
- 추후 다른 형태의 튜닝 리포트 배치를 추가하더라도 Job, Step, Reader, Writer만 쉽게 생성 가능
- 각 컴포넌트에 대해 개별 단위 테스트 가능
- 특정 컴포넌트 예외 시 분리 대응이 쉬움
4. Spring Batch 메타 테이블 관리
Spring Batch는 Job 실행 내역 관리를 위해
BATCH_JOB_INSTANCE
,BATCH_JOB_EXECUTION
,BATCH_STEP_EXECUTION
등의 메타 테이블을 사용함
- 로컬 테스트에서는
spring.batch.initialize-schema=always
로 자동 생성 - 운영 환경에서는
initialize-schema=never
로 설정하고 DDL을 수동 적용할 것 - 메타 테이블 삭제 시 Job 재실행 이력이 초기화되므로 주의 필요
5. 예외 처리 전략
5-1. faultTolerant() + skip() / retry()
faultTolerant()
: skip(), retry() 등을 활성화하는 선언skip()
: 특정 예외 발생 시 해당 아이템만 건너뛰고 계속 진행retry()
: 일시적인 예외는 몇 번 재시도 후 계속 진행
.step("updateVisibilityStep")
.<TuningReport, TuningReport>chunk(10)
.reader(reader)
.writer(writer)
.faultTolerant()
.skip(NotificationSendException.class) // 알림 전송 실패는 그냥 skip
.skipLimit(10) // 최대 10번까지만 skip 허용
.retry(OptimisticLockingFailureException.class) // DB 충돌은 재시도
.retryLimit(3) // 3번까지 retry
- chunk(10) 의 이유
- chunk size는 한 번에 처리할 리포트의 개수 (트랜잭션 단위로 묶임)
- 튜닝 리포트는 시그널 상태에서 매칭 상태로 전환된 사용자 쌍마다 생성되며, 주 1회 알림이 발송되기 때문에 현재 설정된 chunk 크기(10)는 적절한 처리량과 실패 시 복구 범위를 고려한 수준이라고 볼 수 있음
5-2. JobExecutionListener 또는 StepExecutionListener 사용
- 전체 Job의 성공/실패 여부를 한 번에 볼 수 있음
@Bean
public JobExecutionListener jobLoggerListener() {
return new JobExecutionListener() {
@Override
public void afterJob(JobExecution jobExecution) {
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
log.info("✅ 튜닝 리포트 공개 작업 완료!");
} else {
log.error("❌ 튜닝 리포트 공개 작업 실패! 상태: {}", jobExecution.getStatus());
}
}
};
}
- 예외 종류
- 알림 전송 실패 :
NotificationSendException
후skip()
- DB 저장 중 충돌 :
OptimisticLockingFailureException
→retry()
로 감싸기 - Runtime Error : 로그 남기고
skipLimit
이내로 처리 (전체 Job이 멈추지 않도록 함)
- 알림 전송 실패 :