[Spring Batch] 개념 및 Job 생성

조성권·2023년 4월 29일
3
post-thumbnail

오늘은 Spring Batch에 대한 간략한 소개 및 Job 생성에 대해 소개해보도록 하겠다.

Spring Batch

Spring Batch의 이론적인 개념에 대해 살펴보면 아래와 같다.

Spring Batch :
Enterprise System의 운영에 있어 대용량 일괄처리의 편의를 위해 설계된 가볍고 포괄적인 Batch Framework

Spring Batch는 보통 위 개념과 같이 일관되고 반복적인 작업, 트랜잭션 관리, Logging/Trace 모니터링, 트랜잭션 관리 등의 용도로 사용되며 대용량 레코드에 대한 처리 방식으로 많은 Enterprise에서 활용하고 있다.

Spring Batch 프로세스

1. Batch Scheduler

Spring Batch는 그 자체만으로 Job을 실행할 수 없다. Configuration Bean을 통해 해당 Job이 실행될 경우에 어떤 일을 처리할지 세팅할 뿐 이를 실행시켜주기 위해선 Batch Scheduler가 필요하다.

이러한 역할을 하는 Scheduler는 여러가지가 있다. 대표적으로는 Quertz Scheduler, Jenkins Scheduler가 있으며 Scheduler에 대한 더 깊은 얘기는 너무 길어질 것 같으니.. 다음 기회에 작성해보도록 하겠다.

2. JobRepository

JobRepository는 배치를 수행하는데 필요한 전반적인 Object를 모두 포함하고 있다. (Job, JobLauncher, Step, 등)
그리고 이를 통해 배치 수행과 관련된 수치 데이터와 Job의 Status를 유지/관리하며 배치와 관련된 CRUD 처리를 하는 역할을 수행한다.

2-1. Job

Job배치 처리 과정을 하나의 단위로 만들어 놓은 객체이다.
배치처리 과정에 있어 최상단 Object라고 볼 수 있다.

2-2. JobLauncher

JobLauncher는 말그대로 Job을 실행시키는 역할을 담당한다.
Job & JobParameters를 param으로 받고 배치 수행 후, JobExecution을 반환한다.
Spring Batch에서는 JobLauncherApplicationRunner 클래스가 자동으로 JobLauncher를 실행하기 때문에 우리가 직접 컨트롤할 일은 없다고 볼 수 있다.

2-3. Step

Step은 Job을 구성하는 독립적인 작업 단위이다. 실제 배치가 실행되는 처리를 정의하고 제어하는데 필요한 모든 정보를 가지고 있는 Object라고 볼 수 있다.
Step은 Tasklet / Chunk 기반으로 수행되며 이는 유저가 선택해서 사용하게 된다.

Tasklet vs Chunk
Tasklet: 한 가지 이상의 CRUD가 발생(=비즈니스 로직)하는 task에 대해 일괄적으로 처리하는 경우, 채택한다. (복잡한 로직을 수행해야 하는 job일 경우, 채택)
Chunk: chunk 단위로 처리할 모든 record를 쭉 읽어들인 후, 모두 읽어들이는데 성공하면 한번에 Write하는 방식 (대용량 데이터에 대해 단순 처리할 경우, 채택)

3. ItemReader

ItemReader는 말 그대로 배치를 수행하는데 있어 대상이 되는 데이터를 읽어들이는데 사용되는 Object이다. 데이터를 읽어들이는 방법에는 DB Connection을 통해 불러올 수도 있지만 File I/O를 통해 불러오기도 한다.

4. ItemProcessor

ItemProcessor는 Read와 Write의 중간 단계에서 가공(처리) 역할을 수행한다.
그리고 이 단계는 필수가 아니기도 하다. Read와 Write만으로 원하는 기능을 수행할 수 있다면 ItemProcessor는 과감히 Skip해도 된다.

5. ItemWriter

ItemWriter는 Batch의 마지막 단계이다. 말 그대로 우리가 처리하고자 하는 데이터에 대해 최종적으로 가공된 데이터를 출력하는 역할을 수행한다.
우리는 ItemWriter를 통해 다시 DB에 해당 내용을 저장할 수도 있고 해당 데이터를 다른 프로젝트로 API 호출시킬 수도, Kafka를 통해 msg 발행시킬 수도 있다.

Spring Batch Job 만들기

그럼 간단히 Spring Batch의 용어 정리도 해봤겠다 이제 소스코드를 만들어보기로 하자.

우선, 나는 Spring Initializr(https://start.spring.io)를 통해 SpringBoot 프로젝트를 하나 만들었으며 기본 Spec은 아래와 같다.

1. @EnbleBatchProcessing 추가

@SpringBootApplication
@EnableBatchProcessing
public class JobPracticeApplication {

	public static void main(String[] args) {
		SpringApplication.run(JobPracticeApplication.class, args);
	}

}

프로젝트 생성 시, 최초 메인 Application 클래스에 위와 같이 @EnableBatchProcessing 애노테이션을 추가해준다.

@EnableBatchProcessing
: 배치 기능 활성화

2. JobConfig Class 생성

특정 Job을 수행하기 위한 클래스 파일을 생성하도록 하자.

@Slf4j
@Configuration
@RequiredArgsConstructor
public class MyFirstJobConfiguration {
					...
}

애노테이션 종류
@Configuration: 해당 클래스가 설정(=Configuration)을 위해 사용되는 Bean임을 명시
@RequiredArgsConstructor: 의존성 주입에 있어 @Autowired를 권장하지 않으며 해당 애노테이션을 통해 생성자 주입

3. Job 생성

Spring Batch 5.0 부터 JobBuilderFactory는 deprecated되었다.
https://docs.spring.io/spring-batch/docs/current/api/deprecated-list.html
이로 인해 기존에 활용하던 방식은 사용하기 어렵게 되었다.
그래서 JobRepository를 활용하는 방식으로 바꿔줄 필요가 있다.
(AS-IS)

				...
    @Bean
    public Job myFirstJob(Step step) {
        return this.jobBuilderFactory.get("myFirstJob")
                .start(myFirstStep)
                .build();
    }
    			...

(TO-BE)

				...
    @Bean
    public Job myFirstJob(JobRepository jobRepository){
        return new JobBuilder("myFirstJob", jobRepository)
                .start(myFirstStep(jobRepository))
                .build();
    }
    			...

예제 속, job은 단일 Step으로 구성하도록 하겠다.

4. Step 생성

이제 Job에 포함되는 Step을 생성해야 한다.
StepBuilderFactory 역시 Spring Batch 5.0이상부터 deprecated되어 다른 방식으로 처리해야 한다.
(AS-IS)

				...
    public Step myFirstStep() {
        return stepBuilderFactory.get("myFirstStep")
                .<String, String>chunk(1000)
                .reader(itemReader())
                .writer(itemWriter())
                .build();
    }
				...

(TO-BE)

				...
    @Bean
    public Step myFirstStep(JobRepository jobRepository){
        return new StepBuilder("myFirstStep",jobRepository)
                .<String, String>chunk(1000,transactionManager)
                .reader(itemReader())
                .writer(itemWriter())
                .build();
    }
    			...

Step은 chunk 기반으로 동작하도록 구성하였고 한번에 들어올 수 있는 chunk size는 1000으로 설정했다.

5. ItemReader 생성

다음으로 Batch를 통해 처리하고자 하는 데이터를 Read하는 ItemReader 부분을 만들어보자.

				...
    @Bean
    public ItemReader<String> itemReader(){
        return new ItemReader<String>() {
            @Override
            public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
                return "Read OK";
            }
        };
    }
    			...

위와 같이 구성할 수 있고 실질적으로 DB 통신 / File I/O를 통해 처리할 경우, 이 부분에 로직을 구현하면 된다.
(아래는 MyBatis를 활용하는 경우, 예시이다.)

    			...
    public MyBatisCursorItemReader<Object> itemReader() {
        String strQueryId = "쿼리 경로 기재";

        return new MyBatisCursorItemReaderBuilder<Object>()
                .sqlSessionFactory(sqlSessionFactory)
                .queryId(strQueryId)
                .parameterValues(parameterValues)
                .saveState(false)
                .build();
    }
     			...

6. ItemWriter 생성

마지막으로 배치를 통해 처리된 데이터에 대한 출력을 담당하는 ItemWriter 부분이다.

     			...
    @Bean
    public ItemWriter<String> itemWriter(){
        return strList -> {
            strList.forEach(
                    str -> log.info("str: {}", str)
            );
        };
    }
     			...

ItemReader에서 처리한 데이터가 String List형태로 담겨 들어오면 해당 str를 로그 출력하는 형태로 간단히 구성해보았다.
실질적으로 활용하게 되다면 Writer 부분에서 해당 데이터가 필요한 곳으로 API 전송을 할 수도 있고 DB에 가공된 데이터를 저장할 수도 있겠다.

회고

회사에서 Spring Batch를 사용할 기회가 있어서 이번 기회를 통해 Spring Batch에 대해 조금 더 알아보는 시간을 가져보게 되었다. 아직 Spring Batch에 대해 완벽히 알고 있다고 얘기할 순 없지만 계속 사용하고 이것저것 바꿔가며 활용하다보면 보다 나은 서비스를 만들 수 있을 것 같다.

profile
천천히, 완벽히 배워나가고자 하는 웹 서비스 엔지니어

0개의 댓글