티스토리 뷰

Spring

Spring Batch Sample Code (1)

§무명소졸§ 2022. 9. 20. 20:26

몇 년 전 N사에서 프로젝트 중에 스프링 배치를 처음 접했다. 그 당시에는 스프링 배치에 대한 한글 서적이나(글을 쓰는 이시점에도 1권정도 있음) 블로그등 레퍼런스도 많이 없던 시절인데 어떻게 꾸역꾸역 했었던거 같다. 그 후 4-5 년이 시간이 흘러 다시 스프링 배치를 사용해 2개의 프로젝트를 수행했고 몇 년전의 나보다는 스프링 배치에 대한 지식이 조금은 나아졌다고 생각이 든다. 해당 경험으로 쉬운 예제 프로그램을 작성하면서 스프링 배치에 대한 내용을 알아보겠다.

Batch

배치라는 용어는 범용적으로 많이 사용된다. 내가 생각하는 배치 프로세싱의 간단한 정의는 시작과 종료가 있고 특정 시간에 일련의 작업들이 순서대로 진행되는 프로그램이다. Daemon 이랑 대비 된다고 볼 수있다. Daemon 은 우리가 흔히 웹개발시에 서버로 사용하는 톰캣과 같이 운영체제 백그라운드에서 종료되지 않고 실행되는 프로그램을 말한다.

Spring Batch

Spring Eco-system 에는 Spring Web, Spring Data, Spring Cloud 등 많은 수의 프레임워크가 존재한다. 그중 Spring Batch 는 배치 프로그램 개발을 스프링 프레임워크 구성에 맞게 개발할 수 있는 환경을 제공한다. 또한 배치 프로그램의 기본적 구성 요소인 로깅, 병렬처리, 대용량 데이터 처리를 위한 작업들을 제공하고 있다. 즉 스프링의 큰 장점 중 하나인 기술 복잡도를 대신 처리해주고 비지니스 복잡도에 개발자가 집중할 수 있게 해서 배치 프로그램을 빠르게 개발할 수 있는 프레임워크이다.
물론 스프링 배치가 빠르게 배치 프로그램을 작성할 수 있지만 세부적인 내용으로 들어가면 결코 간단하지 않다.

Job & Step

https://docs.spring.io/spring-batch/docs/current/reference/html/job.html

스프링 배치의 가장 기본이 되는 구성요소는 Job 과 Step이다. 사실 많은 배치성 모듈들이 같은 네이밍을 쓰는 편이다. Job 은 배치의 기본 단위가 된다. 예를 들면
JOB: "매일 아침 6시마다 집 앞 공원에서 트랙을 3바퀴 돌고 온다."
이렇게 Job 을 선언하고 해당 Job을 수행하기 위한 Step 을 정의 한다
Step 1: "아침에 6시에 모닝콜이 울린다. 모닝콜을 끈다."
Step 2: "물을 한 잔 먹고 화장실을 간다. "
Step 3: "간단하게 세수를 한다."
Step 4: "신발끈을 묶는다."
...
결국 한 개의 Job 이 N개의 Step을 가질 수 있다. Step 의 정의 범위를 얼만큼 어떻게 가져갈지 정하는 건 오로지 개발자가 선택하는 부분이다. (이 부분이 개발자 설계 역량이라고 생각한다.)
이제 한 개의 Job 과 Step 을 가지는 간단한 스프링 배치 프로그램을 만들어 보겠다.

Simple Batch

Spring Boot 애플리케이션을 통한 스프링 배치 프로그램의 시작은 너무 간단한다. 아래와 같이 배치를 위한 dependency 한 줄과 Spring Batch 내부에서 사용하는 로그 기록(Spring Batch 는 로깅을위해 자동으로 구성을 한다. 이 부분은 이후 설명하겠다.)을 위해 H2DB 를 추가하면 바로 스프링 배치 프로그램을 작성할 수 있다.(gradle 과 spring boot 초기화 및 기본 실행 내용은 생략)

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-batch'
    implementation 'com.h2database:h2'
}

application.yml

spring boot configuration 파일에는 H2 db 연결 정보를 설정한다. 여기서는 우선 memory db 형태로 설정하겠다.

spring:
  datasource:
    url: jdbc:h2:mem:batchdb
    username: sa
    password: password
    driverClassName: org.h2.Driver

SimpleBatch

10 부터 1까지 1초에 한 개의 숫자를 출력하는 초간단 배치 코드이다. 아래에서 필요한 요소들을 설명하겠다.

package info.m2sj.tistorybatch;

import java.util.concurrent.TimeUnit;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class SimpleBatch {
    private final JobBuilderFactory jobBuilderFactory;

    private final StepBuilderFactory stepBuilderFactory;

    public SimpleBatch(JobBuilderFactory jobBuilderFactory, 
    StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }


    @Bean
    public Job simpleJob() {
        return jobBuilderFactory
            .get("simple-job")
            .start(simpleStep())
            .build();
    }

    @Bean
    public Step simpleStep() {
        return this.stepBuilderFactory
            .get("simple-step")
            .tasklet(simpleTasklet())
            .build();
    }

    @Bean
    public Tasklet simpleTasklet() {
        return (stepContribution, chunkContext) -> {
            for (int i = 10; i > 0; i--) {
                TimeUnit.SECONDS.sleep(1);
                System.out.println(i);
            }
            return RepeatStatus.FINISHED;
        };
    }
}

@EnableBatchProcessing

Spring Boot 에서 Spring Batch 기능을 활성화하기 위한 어노테이션이다. 반드시 선언을 해야지 배치를 실행하기 위한 내부 Bean 들이 초기화 된다. 여러번 선언할 필요는 없고 보통은 설정 클래스나 메인 클래스에 한 번만 선언하면 된다.

JobBuilderFactory, StepBuilderFactory

Job 과 Step 객체를 생성하기 위한 FactoryBean 들을 생성자를 통해 Injection(@Autowired 로도 가능) 하고 있다. @EnableBatchProcessing 의해 Bean 이 주입되며 Builder Pattern 을 통해 객체를 생성한다.

Job, Step Beans

Job, Step Bean을 선언하고 있다. 각 각의 .get 메서드 안에 문자열은 각 빈의 이름이며 내부 로깅 테이블에 Job Name 또는 Step Name 으로 기록된다. (@Bean name이 아니다.). 그리도 위에서 언급했듯이 Job에서 Step 을 호출 하는 형태이다.

Tasklet vs Chunk

실제 처리 로직이 들어가 있는 빈이다. 스프링배치에 처리를 하는 방식은 크게 Chunk 방식과 Tasklet 방식으로 나눌수 있다. Chunk 방식은 세가지 클래스 빈 ItemReader, ItemProcessor, ItemWriter 를 구현하고 이미 스프링 배치에서 작성해놓은 클래스들이 있어 그것을 이용하면 된다. 물론 직접 상속받아 구현할 수도 있다. Tasklet 방식은 Tasklet 인터페이스만 구현하면 되고 개발자가 직접 작성한다. for loop 를 돌면 숫자를 출력하고 종료되면 RepeatStatus.FINISHED 를 반환 배치에 정상 종료를 전달한다.

실행

./gradlew bootRun 실행하면 아래와 같이 숫자를 출력한다면 정상적으로 실행이 된 것이다.

./gradlew bootRun

2022-09-20 10:54:27.220  INFO 9178 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=simple-job]] launched with the following parameters: [{}]
2022-09-20 10:54:27.230  INFO 9178 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [simple-step]
10
9
8
7
6
5
4
3
2
1
2022-09-20 10:54:37.280  INFO 9178 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [simple-step] executed in 10s49ms
2022-09-20 10:54:37.283  INFO 9178 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=simple-job]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 10s57ms
2022-09-20 10:54:37.286  INFO 9178 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-09-20 10:54:37.289  INFO 9178 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

결론

아주 간단한 스프링 배치 샘플 코드를 만들어보았다. 위에 스프링 배치 코드는 Hello World 수준이라고 볼 수 있다. 앞으로 이 코드에 기능을 추가하면서 계속 발전시키면서 스프링 배치의 기능을 좀 더 알아 보겠다.

전체 코드는 아래 github 의 chap1 브랜치 를 참고하면 된다.
https://github.com/warpgate3/spring-batch-tistory.git

'Spring' 카테고리의 다른 글

Spring Security Class Diagram  (1) 2022.11.24
Spring Batch Sample Code (2)  (0) 2022.10.07
Spring Boot Reactive + Postgresql(r2dbc)  (0) 2020.09.23
Async method with Spring boot  (0) 2020.08.06
Non Blocking with Spring boot  (2) 2020.07.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크