본문으로 건너뛰기
AIDevOps
  • Learn
  • Learning Paths
  • Practice
  • Open Source
  • Books
  • Engineering

    AI DevOpsAI 서비스 개발·운영 전체 지도LLMOpsLLM 배포·평가·관측실전 프로젝트AI Agent 프로젝트 실습

    Knowledge

    Docs기술 문서 모음Blog엔지니어링 아티클Plogger개발 기록 피드

    Validate

    Certification3단계 역량 인증 · 준비 중
AI Models
LlamaMistralGemmaDeepSeekQwen
🌱 Spring Cloud
Spring 입문 & 로드맵Spring Cloud GatewaySpring BootJava|Spring AISpring SecuritySpring BatchSpring JPA
🤖 AI 실전 개발
AI 실전 입문 & 로드맵Hugging FaceLangChainLlamaIndexLLMOps|LangGraphMCPMulti-AgentAgent Evaluation
🧠 AI Core
AI 입문 & 로드맵ML FundamentalsLLM Fundamentals|Python AIC++|PyTorchTensorFlowJAX
🧠 AI Agent 개발
금융 AI AgentLLM API 서버주식 투자 AgentAIOps AI Agent교육 AI Agent코딩 AI Agent
🐳 DevOps
DevOps 입문 & 로드맵LinuxDockerCI/CD|Kubernetes 기본K8s 심화/실무PrometheusGrafana
🧱 인프라
인프라 입문 & 로드맵NginxRedis
☁️ 클라우드
클라우드 입문 & 로드맵AWSGCPAzureNCPCloudflare
🎨 Frontend
Frontend 입문 & 로드맵JavaScriptTypeScript|ReactNext.js|VueNuxt
📱 Mobile
Mobile 입문 & 로드맵KotlinAndroidFlutter
⚙️ Backend
Backend 입문 & 로드맵Python 기본FastAPIDjangoFlask|CGoGinNode.js
💾 Database
DB 입문 & 로드맵공통 SQLOracleMySQLPostgreSQL|MongoDB벡터 DB
🧪 검증
k6JMeternGrinder
AIDevOps

Engineering AI. From Code to Production.
AI와 AI Agent를 개발하고 운영하기 위한 엔지니어링 학습 플랫폼

Learn

  • 전체 가이드
  • Learning Paths
  • Practice
  • Books

Resources

  • AI DevOps
  • LLMOps
  • 실전 프로젝트
  • Docs
  • Blog
  • Plogger
  • Open Source
  • Certification (준비 중)

Start Here

  • AI Core 로드맵
  • AI 실전 개발 로드맵
  • Spring Cloud 로드맵
  • DevOps 로드맵
  • 인프라 로드맵

 

  • 클라우드 로드맵
  • Frontend 로드맵
  • Mobile 로드맵
  • Backend 로드맵
  • Database 로드맵
© 2026 AI DevOps Korea. All rights reserved.
이용약관개인정보처리방침Sitemaptestforge.kr
  1. Home
  2. Learn
  3. Spring Cloud
  4. Spring Batch
Spring Boot 배치 처리 가이드

⚙️ Spring Batch 완전 가이드

Visitors

Spring Boot 기반 Spring Batch 5로 대용량 데이터 처리 파이프라인을 구성합니다. Job/Step/Chunk 아키텍처, ItemReader/Writer, 스케줄링, 병렬 처리, 재시작 전략까지 실전 배치 시스템을 설계합니다.

  • Advanced · 심화
  • 업데이트 2026.09.19
  • 약 10분 읽기
  • 9개 섹션
  • 예제 코드 8개

포함된 Learning Path

이 가이드는 아래 경로의 한 단계입니다. 앞뒤 순서와 함께 학습해보세요.

  • Spring Cloud Engineer →
대용량 ETL 처리정산/집계 배치데이터 마이그레이션Spring Boot 스케줄링

관련 프레임워크 & 개발환경

🍃Spring Boot→☕Java→🗄️Spring JPA→

목차

0 / 11
  1. 가이드 사용법
  2. 구조 다이어그램
  3. Spring Batch란?
  4. Spring Boot 프로젝트 설정
  5. Job & Step 구성
  6. Chunk 처리 (Reader-Processor-Writer)
  7. ItemReader 종류
  8. ItemWriter 종류
  9. 병렬 처리 & Partitioning
  10. 스케줄링 & 재시작 전략
  11. 배치 테스트
목차 11개 섹션
  1. 가이드 사용법
  2. 구조 다이어그램
  3. Spring Batch란?
  4. Spring Boot 프로젝트 설정
  5. Job & Step 구성
  6. Chunk 처리 (Reader-Processor-Writer)
  7. ItemReader 종류
  8. ItemWriter 종류
  9. 병렬 처리 & Partitioning
  10. 스케줄링 & 재시작 전략
  11. 배치 테스트

가이드 사용법

읽는 방향

Spring Batch를 실무 흐름으로 이해하기

Spring Boot 기반 Spring Batch 5로 대용량 데이터 처리 파이프라인을 구성합니다. Job/Step/Chunk 아키텍처, ItemReader/Writer, 스케줄링, 병렬 처리, 재시작 전략까지 실전 배치 시스템을 설계합니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.

핵심 관점

백엔드 / 시스템 개발

문법보다 요청이 들어와 검증, 처리, 저장, 응답으로 이어지는 경계를 먼저 잡습니다.

대용량 ETL 처리정산/집계 배치데이터 마이그레이션Spring Boot 스케줄링

구조 다이어그램

글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 Spring Batch를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.

학습 흐름

다이어그램 렌더링 중…

아키텍처 관점

다이어그램 렌더링 중…

Spring Batch란?

Spring Batch를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.

Spring Batch는 대용량 레코드 처리를 위한 Spring Boot 기반 경량 배치 프레임워크입니다. Job → Step → Chunk 계층 구조와 재시작, 스킵, 재시도 메커니즘을 기본 제공합니다.
다이어그램 렌더링 중…
개념설명
Job배치 작업의 최상위 단위 — 하나 이상의 Step으로 구성
Step실제 처리 단위 — Chunk 방식 또는 Tasklet 방식
ChunkRead → Process → Write 를 N건씩 트랜잭션 단위로 처리
JobRepositoryJob 실행 이력, 상태를 DB에 저장
JobLauncherJob을 실행하는 진입점

Spring Boot 프로젝트 설정

여기서는 Spring Boot 프로젝트 설정을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

spring-boot-starter-batch 하나로 Job/Step 실행에 필요한 JobRepository, JobLauncher 같은 핵심 컴포넌트가 자동 구성됩니다. spring-batch-test는 실제 배치를 돌리지 않고도 Job과 Step 단위로 테스트할 수 있게 해주므로 테스트 의존성에 반드시 포함하세요.
build.gradle.ktsKOTLIN
plugins {
    id("java")
    id("org.springframework.boot") version "3.3.5"
    id("io.spring.dependency-management") version "1.1.6"
}

java { toolchain { languageVersion.set(JavaLanguageVersion.of(21)) } }

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-batch")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")
    runtimeOnly("com.h2database:h2")        // 개발용
    runtimeOnly("org.postgresql:postgresql") // 운영용
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.batch:spring-batch-test")
}

Job & Step 구성

여기서는 Job & Step 구성을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

@EnableBatchProcessing을 선언하고 JobRepository, PlatformTransactionManager를 주입받아 Job과 Step을 Bean으로 등록합니다.
BatchConfig.javaJAVA
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class BatchConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final UserItemReader   userItemReader;
    private final UserItemProcessor userItemProcessor;
    private final UserItemWriter   userItemWriter;

    @Bean
    public Job userMigrationJob() {
        return new JobBuilder("userMigrationJob", jobRepository)
            .start(userMigrationStep())
            .build();
    }

    @Bean
    public Step userMigrationStep() {
        return new StepBuilder("userMigrationStep", jobRepository)
            .<User, MigratedUser>chunk(100, transactionManager)
            .reader(userItemReader)
            .processor(userItemProcessor)
            .writer(userItemWriter)
            .faultTolerant()
            .skipLimit(10)
            .skip(Exception.class)
            .build();
    }
}

Chunk 처리 (Reader-Processor-Writer)

여기서는 Chunk 처리 (Reader-Processor-Writer)을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Chunk 방식은 N건씩 읽고 → 가공하고 → 쓰는 사이클을 트랜잭션으로 묶습니다. 실패 시 해당 청크만 롤백됩니다. 전체 데이터를 한 트랜잭션으로 묶는 대신 청크 단위로 커밋을 나누기 때문에, 수백만 건을 처리하다 중간에 실패해도 이미 커밋된 앞선 청크는 그대로 유지되고 실패한 청크부터 재시도할 수 있습니다.
다이어그램 렌더링 중…
UserItemProcessor.javaJAVA
@Component
public class UserItemProcessor implements ItemProcessor<User, MigratedUser> {

    @Override
    public MigratedUser process(User user) {
        // null 반환 시 해당 아이템은 Writer로 전달되지 않음 (필터링)
        if (!user.isActive()) return null;

        return MigratedUser.builder()
            .originalId(user.getId())
            .email(user.getEmail().toLowerCase())
            .createdAt(LocalDateTime.now())
            .build();
    }
}

ItemReader 종류

여기서는 ItemReader 종류을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Spring Batch는 JPA, JDBC, CSV, XML 등 다양한 ItemReader를 기본 제공합니다. JpaCursorItemReader는 커서 방식으로 대용량 데이터를 메모리에 한 번에 올리지 않고 스트리밍으로 읽어 대규모 마이그레이션에 적합하고, setSaveState(true)를 켜두면 배치가 중간에 죽어도 마지막으로 처리한 지점부터 재시작할 수 있습니다.
UserItemReader.javaJAVA
// ── JpaCursorItemReader (대용량 추천) ────────────
@Component
@StepScope
public class UserItemReader extends JpaCursorItemReader<User> {

    public UserItemReader(EntityManagerFactory emf) {
        setEntityManagerFactory(emf);
        setQueryString("SELECT u FROM User u WHERE u.active = true ORDER BY u.id");
        setSaveState(true);  // 재시작 시 중단 지점부터 재개
    }
}

// ── FlatFileItemReader (CSV 읽기) ─────────────────
@Bean
@StepScope
public FlatFileItemReader<UserCsvRow> csvReader(
        @Value("#{jobParameters['file']}") String file) {
    return new FlatFileItemReaderBuilder<UserCsvRow>()
        .name("csvReader")
        .resource(new FileSystemResource(file))
        .delimited().delimiter(",")
        .names("id", "email", "name")
        .targetType(UserCsvRow.class)
        .linesToSkip(1)  // 헤더 스킵
        .build();
}

ItemWriter 종류

여기서는 ItemWriter 종류을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

JpaItemWriter는 EntityManager를 통해 병합하고, JdbcBatchItemWriter는 JDBC 벌크 INSERT/UPDATE를 사용합니다.
UserItemWriter.javaJAVA
// ── JpaItemWriter ─────────────────────────────────
@Component
@RequiredArgsConstructor
public class UserItemWriter implements ItemWriter<MigratedUser> {

    private final MigratedUserRepository repository;

    @Override
    public void write(Chunk<? extends MigratedUser> chunk) {
        repository.saveAll(chunk.getItems());
    }
}

// ── JdbcBatchItemWriter (벌크 성능 최적화) ─────────
@Bean
public JdbcBatchItemWriter<MigratedUser> jdbcWriter(DataSource dataSource) {
    return new JdbcBatchItemWriterBuilder<MigratedUser>()
        .dataSource(dataSource)
        .sql("INSERT INTO migrated_users (original_id, email, created_at) " +
             "VALUES (:originalId, :email, :createdAt)")
        .beanMapped()
        .build();
}

병렬 처리 & Partitioning

여기서는 병렬 처리 & Partitioning을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Partitioner로 데이터를 분할하고 TaskExecutor로 병렬 처리하면 대용량 배치 처리 속도를 크게 높일 수 있습니다. 마스터 Step은 데이터를 어떻게 나눌지만 결정하고 실제 처리는 워커 Step들이 각자 맡은 범위를 독립적으로 수행하므로, gridSize를 늘리는 것만으로 처리량을 CPU 코어 수에 맞춰 수평으로 확장할 수 있습니다.
다이어그램 렌더링 중…
PartitionConfig.javaJAVA
@Bean
public Step partitionedStep() {
    return new StepBuilder("partitionedStep", jobRepository)
        .partitioner("workerStep", new RangePartitioner(userRepository))
        .step(workerStep())
        .taskExecutor(new SimpleAsyncTaskExecutor())
        .gridSize(4)  // 4개 파티션 병렬 처리
        .build();
}

// 파티셔너 — ID 범위 기준으로 분할
public class RangePartitioner implements Partitioner {
    private final UserRepository repository;

    @Override
    public Map<String, ExecutionContext> partition(int gridSize) {
        long min = repository.findMinId();
        long max = repository.findMaxId();
        long range = (max - min) / gridSize + 1;
        Map<String, ExecutionContext> result = new HashMap<>();
        for (int i = 0; i < gridSize; i++) {
            ExecutionContext ctx = new ExecutionContext();
            ctx.putLong("minId", min + i * range);
            ctx.putLong("maxId", min + (i + 1) * range - 1);
            result.put("partition" + i, ctx);
        }
        return result;
    }
}

스케줄링 & 재시작 전략

여기서는 스케줄링 & 재시작 전략을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

@Scheduled로 주기 실행을 설정하고, allowStartIfComplete를 통해 재시작 정책을 제어합니다. JobParameters를 매번 다른 값(실행 시각 등)으로 채워 넘기지 않으면 Spring Batch는 "이미 완료된 동일 Job"으로 판단해 재실행을 거부하므로, 스케줄 작업에는 항상 실행마다 달라지는 파라미터를 포함해야 합니다.
BatchScheduler.javaJAVA
@Component
@RequiredArgsConstructor
public class BatchScheduler {

    private final JobLauncher jobLauncher;
    private final Job userMigrationJob;

    @Scheduled(cron = "0 0 2 * * *")  // 매일 새벽 2시
    public void runBatch() throws Exception {
        JobParameters params = new JobParametersBuilder()
            .addLocalDateTime("runAt", LocalDateTime.now())  // 매번 새 파라미터로 실행
            .toJobParameters();
        jobLauncher.run(userMigrationJob, params);
    }
}

// application.yml
// spring.batch.job.enabled: false  → 서버 시작 시 자동 실행 방지

배치 테스트

여기서는 배치 테스트을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

@SpringBatchTest로 JobLauncherTestUtils를 주입받아 Job 전체 또는 개별 Step을 검증합니다. Job 전체를 실행하면 느리고 실패 지점을 특정하기 어려우므로, launchStep으로 특정 Step만 골라 실행해 Reader/Processor/Writer 각각의 동작을 좁혀서 검증하는 것이 디버깅에 더 효율적입니다.
BatchTest.javaJAVA
@SpringBatchTest
@SpringBootTest
class UserMigrationBatchTest {

    @Autowired JobLauncherTestUtils jobLauncherTestUtils;
    @Autowired JobRepositoryTestUtils jobRepositoryTestUtils;
    @Autowired MigratedUserRepository migratedUserRepository;

    @BeforeEach
    void setUp() {
        jobRepositoryTestUtils.removeJobExecutions();
    }

    @Test
    void 배치_전체_실행_성공() throws Exception {
        JobExecution execution = jobLauncherTestUtils.launchJob();
        assertThat(execution.getStatus()).isEqualTo(BatchStatus.COMPLETED);
    }

    @Test
    void 특정_Step만_실행() throws Exception {
        JobExecution execution = jobLauncherTestUtils.launchStep("userMigrationStep");
        StepExecution step = execution.getStepExecutions().iterator().next();
        assertThat(step.getStatus()).isEqualTo(BatchStatus.COMPLETED);
        assertThat(step.getWriteCount()).isGreaterThan(0);
    }
}
← 이전 가이드Spring Security다음 가이드 →Spring JPA