woodisco 2025. 4. 12. 15:40

 

배치
일정한 시점에 자동으로 여러 작업을 한 번에 처리하는 방식이다. 특히 사람이 일일이 하지 않고 컴퓨터가 정해진 시간에 알아서 하는 작업을 말한다.

▷ 배치 예시

💼 회사에서의 급여 계산:
회사는 매일 급여를 계산하지 않고 한 달에 한 번, 모든 직원의 근무시간, 수당 등을 모아서 한 번에 급여를 계산한다. 이것을 "배치 작업"으로 자동화할 수 있다. (예: 매달 말일 밤 12시에 자동으로 급여 계산 실행)

📊 통계 데이터 모으기:
웹사이트 방문자 수나 매출 데이터를 매일 밤에 한 번 모아서 하루 치 통계 정리하는 경우도 해당한다.

▷ 배치를 사용하는 이유

효율성 : 한꺼번에 처리함으로 리소스를 아낄 수 있다.
자동화 가능 : 사람이 일일이 하지 않아도 된다.
야간 처리 가능: 사용자 없는 시간에 돌릴 수 있어서 시스템에 부담이 적다.

Spring Batch
Spring Batch는 대량의 데이터를 일괄 처리(batch processing) 하기 위한 프레임워크이다. 아래에 간단한 예시를 통해 전체적인 구조와 흐름을 파악할 수 있다.

▷ 시나리오
간단한 CSV 파일에서 이름을 읽고, 모두 대문자로 변환해서 출력하는 배치 작업

▷ 기본 구조
Spring Batch는 크게 3단계로 구성된다.

단계 역활
Reader 데이터를 읽는다. (예: CSV 파일, DB 등)
Processor 데이터를 가공한다. (예: 이름을 대문자로 바꾸기)
Writer 데이터를 출력하거나 저장한다. (예: DB, 파일 등)


▷ 예제 코드
① ItemReader – 이름을 파일에서 읽기
@Bean
public FlatFileItemReader<String> reader() {
    return new FlatFileItemReaderBuilder<String>()
            .name("nameItemReader")
            .resource(new ClassPathResource("names.csv"))
            .lineMapper((line, lineNumber) -> line)
            .build();
}​

 

・FlatFileItemReader<String> : 한 줄씩 텍스트 파일을 읽는 리더 클래스. 한 줄을 String으로 룸
・.name("nameItemReader") : 리더에 이름을 부여 (디버깅이나 모니터링 용도)
・.resource(...) : 읽을 파일의 경로 지정
・(resources/names.csv).lineMapper(...) : 한 줄(line)을 어떻게 처리할지 정의, 여기선 그대로 String으로 반환

=> names.csv에 Alice라는 줄이 있으면, 이 리더는 "Alice"라는 String을 반환함

② ItemProcessor – 이름을 대문자로 바꾸기
@Bean
public ItemProcessor<String, String> processor() {
    return name -> name.toUpperCase();
}​

・ItemProcessor<Input, Output> : 입력과 출력 타입 지정
・(String → String)name -> name.toUpperCase() : 람다식으로 이름을 전부 대문자로 바꿈

=> "Alice"→"ALICE"

③ ItemWriter – 결과 출력 or 저장
@Bean
public ItemWriter<String> writer() {
    return items -> {
        for (String item : items) {
            System.out.println(">> " + item);
        }
    };
}​

・ItemWriter<List<String>> : 데이터를 처리 후 저장하거나 출력하는 단계
・for (String item : items) : 처리된 데이터를 하나씩 꺼냄
・System.out.println(...) : 콘솔에 출력함

=> ALICE

④ Step과 Job 구성
@Bean
public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    return new StepBuilder("step1", jobRepository)
            .<String, String>chunk(5, transactionManager)
            .reader(reader())
            .processor(processor())
            .writer(writer())
            .build();
}

@Bean
public Job job(JobRepository jobRepository, JobLauncher jobLauncher) {
    return new JobBuilder("job", jobRepository)
            .start(step1(jobRepository, transactionManager()))
            .build();
}​

・StepBuilder("step1", jobRepository) : step 이름과 job 저장소 설정
・<String, String> : reader → processor → writer 모두 String 처리
・chunk(5, transactionManager) : 5개씩 처리하고 커밋함
・.reader(), .processor(), .writer() : 각각의 구성요소 연결
・JobBuilder("job", jobRepository) : job 이름 설정
・.start(step1(...)) : 첫 번째 step 지정.build() : job 생성

프로젝트 예제

① Config

package test.test1.batch01.config;

import …

@Configuration
@RequiredArgsConstructor
public class Batch01Config {

    private final Batch01Service Batch01Service;

    @Bean(name = Batch01Constant.JOB_Batch01)
    public Job Batch01Job(FcJobNotificationListener fcJobNotificationListener,
                                 Step Batch01Step,
                                 JobRepository jobRepository) {
        return new JobBuilder(Batch01Constant.JOB_Batch01, jobRepository)
                .incrementer(new RunIdIncrementer())
                .listener(fcJobNotificationListener)
                .flow(Batch01Step)
                .end()
                .build();
    }

    @Bean
    public Step Batch01Step(FcStepLoggingListener fcStepLoggingListener,
                                   Tasklet Batch01Tasklet,
                                   JobRepository jobRepository,
                                   PlatformTransactionManager transactionManager) {
        return new StepBuilder(Batch01Constant.STEP_Batch01, jobRepository)
                .tasklet(Batch01Tasklet, transactionManager)
                .listener(fcStepLoggingListener)
                .build();
    }

    @Bean
    @StepScope
    public Tasklet Batch01Tasklet() {
        MethodInvokingTaskletAdapter tasklet = new MethodInvokingTaskletAdapter();
        tasklet.setTargetObject(Batch01Service);
        tasklet.setTargetMethod(Batch01Constant.TEST);
        return tasklet;
    }
}
▷ 전체 구성 요약

이 클래스는 Spring Batch에서 Job → Step → Tasklet → Service 메서드 호출 구조로 배치 처리를 정의하는 것이다.
・Job: 배치 전체 흐름 정의
・Step: 작업 단위
・Tasklet: 단일 작업 수행 (보통 1개의 메서드 호출)
・Service: 실제 로직을 구현하는 클래스

▷ 보충 설명

・@Configuration: 이 클래스가 Spring 설정 파일임을 의미한다.
・@RequiredArgsConstructor: final로 선언된 필드를 자동으로 생성자 주입해준다. → DI(의존성 주입) 편리하게 가능

📌 생성자 주입이란?
클래스에서 필요한 의존성 객체(다른 클래스의 객체)를 생성자를 통해 전달받는 걸 말한다.
예를 들어 아래처럼 어떤 클래스가 있을 때:
public class MyService {
    private final MemberRepository memberRepository;

    public MyService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}​

이 클래스는 MemberRepository라는 의존성 객체가 꼭 필요해서 생성자를 통해 전달받아야만 동작할 수 있다.
그런데 이걸 매번 쓰기 귀찮기 때문에
그래서 Lombok의 @RequiredArgsConstructor를 쓰면

@RequiredArgsConstructor
public class MyService {
    private final MemberRepository memberRepository;
}

final로 선언된 필드에 대해서 자동으로 생성자를 만들준다.

・FcStepLoggingListener : Step 실행 전/후에 로그 출력이나 사용자 정의 로직을 수행하기 위한 리스너

JobRepository : 배치 처리의 상태 관리와 기록을 담당하며 Job/Step 실행 정보, 실행 결과, 파라미터 등을 DB에 저장한다.
PlatformTransactionManager : 트랜잭션을 관리하는 역할을 하며 (예: 커밋, 롤백) Tasklet 안에서 예외가 발생하면 롤백이 되고, 문제 없으면 커밋된다.

▷ Jop

RunIdIncrementer(): 실행할 때마다 runId를 자동 증가시켜 JobInstance를 새로 만들게 한다.
listener(...): Job 실행 전후에 로그나 알림을 수행할 수 있는 리스너
flow(...): 실행할 Step 지정
build(): Job 객체 완성

▷ Step

tasklet(...): Tasklet 방식으로 처리 (한 번의 실행으로 끝나는 단순 처리)
listener(...): Step 전후 로그를 찍을 수 있는 리스너

▷ Tasklet

@StepScope: 이 Tasklet은 Step 실행 시점에 생성됩니다 (지연 생성)
MethodInvokingTaskletAdapter: 특정 메서드 호출만 할 때 사용하는 간단한 Tasklet 구현체


② Constant

package test.test1.batch01.constant;

import ...

public final class Batch01Constant {

    public static final String TEST = "test01";
}

③ Service

package test.test1.batch01.service;

import ...

public interface Batch01Service {

    void test01() throws Exception;
}
package test.test1.batch01.service.impl;

import ...

@Service
@RequiredArgsConstructor
public class Batch01ServiceImpl implements Batch01Service {

    private final TestRepository testRepository;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void test01() throws Exception {
    
        // 비즈니스 로직 예시
        List<Test01ResultDb> searchResultList =
            testRepository.selectTest01();
    }
}
▷ @Transactional

propagation = Propagation.REQUIRES_NEW :
이 메서드를 호출한 쪽에서 이미 트랜잭션이 있어도 무시하고, 새로운 트랜잭션을 시작한다.
사용 시점: 예를 들어 배치 처리 중 한 건 실패하더라도 전체가 영향을 받지 않게 하고 싶을 때
rollbackFor = Exception.class
기본적으로 @Transactional은 RuntimeException이나 Error가 발생할 때만 롤백한다. 그런데 여기서는 모든 Exception이 발생해도 롤백되게끔 설정한 것이다.

2024.04.16 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 1

 

Spring Batch 구현 - 1

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com 프로젝트 생성 및 샘플 코드 작성Spring bootGradleSpring BatchMySQL

woodisco.tistory.com

2024.04.24 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 2

 

Spring Batch 구현 - 2

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com JPA 연동① repository 작성repository/bookingrepository/notificationreposit

woodisco.tistory.com

2024.04.24 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 3

 

Spring Batch 구현 - 3

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com 이용권 일괄 지급 배치① AddPassesJopConfig 작성package com.fastcamp

woodisco.tistory.com

2024.04.25 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 4

 

Spring Batch 구현 - 4

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com 수업 전 알람 배치① SendNotificationBeforeClassJobConfig 작성@Beanpub

woodisco.tistory.com

2024.05.08 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 5

 

Spring Batch 구현 - 5

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com 수업 종료후, 이용권 차감 배치① UsePassesJobConfig 작성@Configur

woodisco.tistory.com

2024.05.09 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 6

 

Spring Batch 구현 - 6

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com 통계 데이터 생성 배치① StatisticsRepository 작성② StatisticsEnti

woodisco.tistory.com

2024.05.14 - [[공부] 프로그래밍/Spring Boot (JAVA)] - Spring Batch 구현 - 7

 

Spring Batch 구현 - 7

♧ 전체 코드 : https://github.com/woodisco/pass-batch GitHub - woodisco/pass-batchContribute to woodisco/pass-batch development by creating an account on GitHub.github.com REST API로 Job 실행시키기① JobLauncherRequest 작성@Getter@Setter@ToSt

woodisco.tistory.com