작성일 최종 수정일시

본 문서는 batch-common 공통 플랫폼의 패키지 구조와 각 기능 영역별 구현 목록 및 실제 구현 중 확인된 설계 결정 사항을 정리한다. 실제 코드는 /spring-batch-practice 저장소의 batch-common 모듈에 있다.


1. batch-common 실제 패키지 구조#

batch-common/
└── src/main/java/com/example/spring_batch_practice/
    │
    ├── core/                                   # 공통 인프라 (job 독립)
    │   ├── api/                                # Admin REST API
    │   │   ├── BatchAdminController.java       # GET/POST 엔드포인트
    │   │   ├── BatchJobService.java            # 실행·중단·재시작·이력 조회
    │   │   └── dto/
    │   │       └── JobExecutionResponse.java   # read/write/skip 집계 DTO
    │   │
    │   ├── error/
    │   │   └── BatchErrorConfig.java           # Retry·Skip 정책 기본 빈
    │   │                                       # @ConditionalOnMissingBean → 잡별 재정의 가능
    │   │
    │   ├── item/
    │   │   ├── reader/
    │   │   │   ├── JdbcCursorReaderFactory.java    # JdbcCursorItemReader 팩토리
    │   │   │   ├── JpaPagingReaderFactory.java     # JpaPagingItemReader 팩토리
    │   │   │   └── MyBatisCursorReaderFactory.java # MyBatisCursorItemReader 팩토리
    │   │   └── writer/
    │   │       ├── JpaItemWriterFactory.java        # JpaItemWriter 팩토리
    │   │       └── MyBatisBatchWriterFactory.java   # MyBatisBatchItemWriter 팩토리
    │   │
    │   ├── listener/
    │   │   └── JobLoggingListener.java         # 잡 실행 전후 로그 (시작·종료·소요시간)
    │   │
    │   └── partition/
    │       ├── RangePartitioner.java           # ID 범위 기반 파티셔너 (JdbcTemplate 사용)
    │       └── PartitionStepHelper.java        # 파티션 Step 정적 빌더
    │
    └── job/
        └── sample/                             # 샘플 잡 (JPA vs MyBatis 비교용)
            ├── domain/
            │   └── Order.java                  # @Entity(name="BatchOrder"), @Table(name="orders")
            ├── mybatis/
            │   └── OrderMapper.java            # @Mapper, SELECT/UPDATE/INSERT/DELETE
            ├── SampleJpaJobConfig.java         # JpaCursorItemReader + JpaItemWriter
            └── SampleMyBatisJobConfig.java     # MyBatisCursorItemReader + MyBatisBatchItemWriter

└── src/main/resources/
    ├── application.yaml
    ├── mapper/
    │   └── OrderMapper.xml                     # MyBatis SQL XML
    └── schema/
        ├── batch-schema-postgresql.sql         # Spring Batch 메타 테이블 DDL (수동 관리)
        └── batch-schema-drop-postgresql.sql    # 메타 테이블 DROP DDL

2. 의존성#

의존성버전용도
spring-boot-starter-batchBoot 관리배치 코어
spring-boot-starter-data-jpaBoot 관리JPA 리더·라이터
spring-boot-starter-webBoot 관리Admin REST API
spring-boot-starter-actuatorBoot 관리헬스체크, 메트릭
mybatis-spring-boot-starter3.0.4MyBatis 자동구성 (mybatis-spring 포함 → 배치 클래스도 내장)
spring-cloud-starter-taskCloud 관리Task 메타 테이블
postgresqlBoot 관리운영 DB
h2Boot 관리테스트 인메모리 DB
lombokBoot 관리보일러플레이트 제거
spring-batch-testBoot 관리JobLauncherTestUtils, JobRepositoryTestUtils

mybatis-spring-batch 별도 아티팩트는 존재하지 않는다. MyBatisCursorItemReader, MyBatisBatchItemWriter 등 배치 통합 클래스는 mybatis-spring (org.mybatis.spring.batch.*) 내에 포함되어 있으며, mybatis-spring-boot-starter를 추가하면 함께 제공된다.


3. 주요 설계 결정 사항#

3-1. Spring Batch 메타 테이블 관리 방식#

spring.batch.jdbc.initialize-schema: never 로 Spring Boot의 자동 DDL 생성을 비활성화. src/main/resources/schema/batch-schema-postgresql.sql 에 Spring Batch 5.2.x PostgreSQL DDL을 보관, Docker Compose 기동 시 초기 스크립트(01_batch_schema.sql)로 실행.

3-2. Job 자동 실행 비활성화#

spring.batch.job.enabled: false → 애플리케이션 기동 시 Job 자동 실행 없음. Admin REST API(POST /admin/batch/jobs/{jobName}/run) 또는 스케줄러를 통한 명시적 트리거만 허용.

3-3. Spring Boot 3.5의 JobOperator 자동 구성#

Spring Boot 3.5부터 JobOperatorBatchAutoConfiguration 내에서 자동 구성한다. 이전에는 직접 SimpleJobOperator@Bean으로 등록해야 했으나, 3.5 이후 불필요. → BatchCoreConfig.java(직접 정의했던 JobOperator 빈) 삭제. Spring Boot 자동 구성에 위임.

3-4. @Entity 이름과 JPQL 예약어#

JPA 엔티티 클래스명이 Order일 때 JPQL에서 FROM Order o를 사용하면 ORDER가 예약어로 인식될 수 있다. → @Entity(name = "BatchOrder")로 JPQL 내 엔티티명을 변경하여 충돌 회피. 테이블명은 @Table(name = "orders")로 그대로 유지.

3-5. JpaCursorItemReader vs JpaPagingItemReader#

JpaCursorItemReaderJpaPagingItemReader
방식JPQL 스트리밍 커서OFFSET/LIMIT 페이지네이션
트랜잭션커서가 청크 경계를 넘어 유지됨각 페이지가 독립 트랜잭션
읽기 대상 수정 시안전 (커서 기반)데이터 밀림 발생 (처리 후 OFFSET 틀어짐)
샘플 채택SampleJpaJobConfig

→ 처리 후 상태가 바뀌는 쿼리에는 JpaCursorItemReader 사용.

3-6. @SpringBatchTest 미사용 이유#

@SpringBatchTest는 내부적으로 JobScopeTestExecutionListener를 등록한다. 이 리스너는 JobExecution을 반환하는 모든 메서드를 job execution 공급자로 간주하고 @BeforeEach 이전에 호출을 시도한다. launch() 같은 테스트 헬퍼 메서드가 있으면 충돌이 발생한다 (The Job must not be null).

@SpringBatchTest 제거. JobLauncherTestUtilsJobRepositoryTestUtils@BeforeEach에서 직접 인스턴스화.

@BeforeEach
void setUp() {
    launcher = new JobLauncherTestUtils();
    launcher.setJobLauncher(jobLauncher);
    launcher.setJobRepository(jobRepository);
    launcher.setJob(targetJob);

    new JobRepositoryTestUtils(jobRepository).removeJobExecutions();
}

3-7. MyBatis 표준화 결정#

영역기술비고
배치 Reader (업무)MyBatisCursorItemReaderSQL XML에서 관리
배치 Writer (업무)MyBatisBatchItemWriterbatch executor 자동 사용
인프라 쿼리 (RangePartitioner 등)JdbcTemplateMIN/MAX 2줄 쿼리에 Mapper XML 생성은 과잉
테스트 setup (JPA 버전 테스트)JdbcTemplate테스트 헬퍼는 JdbcTemplate이 간단
테스트 setup (MyBatis 버전 테스트)OrderMapper프로덕션 Mapper 재사용

4. 기능 영역별 구현 목록#

4-1. Job 실행/관리#

#구현 항목상태
1Job 수동 실행 API POST /admin/batch/jobs/{jobName}/run✅ 완료
2Job 재실행 POST /admin/batch/executions/{id}/restart✅ 완료
3Job 중단 API POST /admin/batch/executions/{id}/stop✅ 완료
4Job 목록 조회 GET /admin/batch/jobs✅ 완료
5실행 이력 조회 GET /admin/batch/executions✅ 완료

4-2. 공통 에러 처리#

#구현 항목상태
1Retry 정책 빈 (SimpleRetryPolicy(3), @ConditionalOnMissingBean)✅ 완료
2Skip 정책 빈 (LimitCheckingItemSkipPolicy(10, ...), @ConditionalOnMissingBean)✅ 완료
3JobLoggingListener (실행 전후 로그, 소요시간)✅ 완료
4StepLoggingListener⬜ 미구현
5JobAlertListener (실패 알림)⬜ 미구현

4-3. 공통 ItemReader / Writer#

#구현 항목상태
1JpaPagingReaderFactory✅ 완료
2JdbcCursorReaderFactory✅ 완료
3JpaItemWriterFactory✅ 완료
4MyBatisCursorReaderFactory✅ 완료
5MyBatisBatchWriterFactory✅ 완료
6FlatFileWriterFactory⬜ 미구현
7RestApiItemReader⬜ 미구현

4-4. 병렬 처리#

#구현 항목상태
1RangePartitioner (ID 범위 분할)✅ 완료
2PartitionStepHelper (빌더 래퍼)✅ 완료
3TaskExecutor 공통 빈⬜ 미구현

4-5. 샘플 Job#

#구현 항목설명
1SampleJpaJobConfigJpaCursorItemReader + JpaItemWriter, orders 처리
2SampleMyBatisJobConfigMyBatisCursorItemReader + MyBatisBatchItemWriter, 동일 기능

두 Job은 동일한 비즈니스 로직(PENDING·amount>10000 → COMPLETED)을 JPA와 MyBatis로 각각 구현하여 비교 목적으로 공존한다.


5. 미결 사항#

항목현황검토 방향
스케줄링 방식미정Quartz(클러스터링) vs 외부(k8s CronJob, Jenkins)
멀티 모듈 구조단일 모듈batch-core + batch-jobs 분리 고려 가능
알림(Notification)미구현Slack Webhook 우선 검토
Prometheus/Grafana 연동미구현Actuator Micrometer 기반