토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 배치 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.

목차


스프링 배치 3.0부터 JSR-352 규격을 전부 지원한다. 이번 장에서는 규격 명세를 설명하진 않으며, JSR-352 컨셉을 스프링 배치에 적용하는 법을 설명한다. JSR-352에 대한 추가 정보는 여기 JCP를 참고하라: https://jcp.org/en/jsr/detail?id=352


12.1. General Notes about Spring Batch and JSR-352

스프링 배치와 JSR-352는 구조적으로 동일하다. 둘 다 job으로 step을 구성하며, reader, processor, writer, listener를 사용한다. 그러나 상호 작용이 미묘하게 다르다. 예를 들어 스프링 배치의 org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t) 는 스킵할 아이템과 스킵을 유발한 Exception을 파라미터로 받는다. 같은 JSR-352 버전 메소드 (javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex)) 도 파라미터를 두 개를 받지만, 현재 청크 안에 있는 모든 아이템 List와 스킵을 유발한 Exception을 받는다. 이 차이로 인해 스프링 배치는 전통적인 스프링 배치 job과 JSR-352 기반 job을 다른 방법으로 실행한다. 스프링 배치의 reader, writer 등의 아티팩트(artifact)는 JSR-352의 JSL로 설정한 job에서도 동작하며, JsrJobOperator로 실행되지만 JSR-352 명세대로 동작한다. JSR-352 인터페이스 규격을 지키지 않은 배치 아티팩트는 전통적인 스프링 job과 잘 동작하지 않는다는 점을 알아두는 게 좋다.


12.2. Setup

12.2.1. Application Contexts

스프링 배치에서 JSR-352 기반으로 job을 실행하면 어플리케이션 컨텍스트가 두 개 생긴다. 부모 컨텍스트는 JobRepository, PlatformTransactionManager 등의 스프링 배치 구조와 관련된 빈이 등록돼 있고, 자식 컨텍스트는 실행할 job 설정을 가지고 있다. 부모 컨텍스트는 프레임워크가 제공하는 jsrBaseContext.xml로 정의한다. 이 컨텍스트는 JSR-352-BASE-CONTEXT 시스템 프로퍼티로 오버라이드할 수 있다.

JSR-352 프로세서는 기본 컨텍스트에 프로퍼티를 주입하는 등의 처리를 하지 않으므로 추가적으로 필요한 컴포넌트는 없다.

12.2.2. Launching a JSR-352 based job

JSR-352에 따르면 매우 간단하게 배치 job을 실행할 수 있어야 한다. 아래 코드만 있으면 배치 job을 실행할 수 있다.

JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties());

매우 편리하지만, 내부에 많은 처리가 숨겨져 있다. 스프링 배치는 뒷단에서 기반 구조를 부트스트랩하며, 원한다면 재정의할 수 있다. BatchRuntime.getJobOperator()를 처음 호출하면 다음 내용들이 부트스트랩된다:

Bean Name Default Configuration Notes
dataSource 설정된 값을 가지고 있는
Apache DBCP BasicDataSource.
디폴트로 HSQLDB가 부트스트랩된다.
transactionManager org.springframework.
jdbc.datasource.
DataSourceTransactionManager
위에서 정의한 dataSource 빈을 참고한다.
A Datasource initializer   batch.drop.script, batch.schema.script 프로퍼티로 받은 스크립트를 실행하기 위해 설정한다. 디폴트로 HSQLDB 스키마 스크립트를 실행한다. batch.data.source.init 프로퍼티로 이 기능을 끌 수 있다.
jobRepository JDBC 기반 SimpleJobRepository. JobRepository는 이전에 언급한 데이터소스와 트랜잭션 매니저를 사용한다. batch.table.prefix 프로퍼티로 스키마 테이블 프리픽스를 변경할 수 있다 (디폴트는 BATCH_다).
jobLauncher org.springframework.batch.
core.launch.support.
SimpleJobLauncher
job을 실행할 때 사용한다.
batchJobOperator org.springframework.batch.
core.launch.support.
SimpleJobOperator
기능 대부분은 JsrJobOperator가 감싸서 제공한다.
jobExplorer org.springframework.batch.
core.explore.support.
JobExplorerFactoryBean
JsrJobOperator의 lookup 기능을 처리하기 위해 사용한다.
jobParametersConverter org.springframework.batch.
core.jsr.
JsrJobParametersConverter
JSR-352 전용 JobParametersConverter 구현체.
jobRegistry org.springframework.batch.
core.configuration.support.
MapJobRegistry
SimpleJobOperator가 사용한다.
placeholderProperties org.springframework.beans.
factory.config.
PropertyPlaceholderConfigure
batch-${ENVIRONMENT:hsql}.properties 파일을 로드해서 위에서 언급한 프로퍼티를 설정한다. ENVIRONMENT는 스프링 배치가 지원하는 데이터베이스를 명시할 때 사용하는 시스템 프로퍼티다 (디폴트는 hsql).

JSR-352 기반 job을 실행하려면 위에 있는 모든 빈이 필요하다. 커스텀 기능을 사용하려면 모두 오버라이드해야 한다.


12.3. Dependency Injection

JSR-352는 대부분이 스프링 배치 프로그래밍 기반이다. 예를 들어 명시적으로 의존성(dependency) 주입을 구현하지 않아도 DI가 적용된다. 스프링 배치는 JSR-352에 정의된 배치 아티팩트를 로딩하기 위한 세 가지 메소드를 모두 지원한다:

JSR-352 기반 배치 job에 스프링 의존성을 주입하려면 배치 아티팩트를 스프링 어플리케이션 컨텍스트 빈으로 등록해야 한다. 빈을 정의하고 나면 batch.xml에 있는 빈처럼 스프링 빈도 참조 할 수 있다.

@Configuration
public class BatchConfiguration {

    @Bean
    public Batchlet fooBatchlet() {
        FooBatchlet batchlet = new FooBatchlet();
        batchlet.setProp("bar");
        return batchlet;
       }
}

스프링 컨텍스트는 다른 스프링 기반 어플리케이션에서처럼 JSR-352 job과도 잘 동작한다. JSR-352 job이 유일하게 다른 점은 컨텍스트 정의 진입점이 /META-INF/batch-jobs/에 정의된 job이라는 것이다.

thread context class loader를 사용하려면 레퍼런스로 패키지명을 모두 포함한 클래스 이름을 제공하기만 하면 된다. 이 방법이나 batch.xml을 사용하면 참조하는 클래스에 매개변수가 없는 생성자가 있어야 빈을 생성할 수 있다는 점에 주의하라.

<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
    <step id="step1" >
        <batchlet ref="io.spring.FooBatchlet" />
    </step>
</job>

12.4. Batch Properties

12.4.1. Property Support

JSR-352에 따르면 JSL 설정으로 job, step, 배치 아티팩트 레벨에 프로퍼티를 정의할 수 있어야 한다. 배치 프로퍼티는 각 레벨에 아래처럼 설정한다:

<properties>
    <property name="propertyName1" value="propertyValue1"/>
    <property name="propertyName2" value="propertyValue2"/>
</properties>

Properties는 어떤 배치 아티팩트에도 설정할 수 있다.

12.4.2. @BatchProperty annotation

배치 아티팩트 클래스 필드에 @BatchProperty, @Inject 애노테이션을 선언하면 Properties를 참조할 수 있다 (스펙대로 두 가지 모두 사용해야 한다). JSR-352가 정의하는 대로 프로퍼티를 사용하는 필드는 String이어야 한다. 타입 변환은 개발자가 구현해야 한다.

위에서 설명한 프로퍼티 블록을 설정하면 다음과 같이 javax.batch.api.chunk.ItemReader 아티팩트에서 접근할 수 있다:

public class MyItemReader extends AbstractItemReader {
    @Inject
    @BatchProperty
    private String propertyName1;

    ...
}

필드 “propertyName1”의 값은 “propertyValue1”이 된다.

12.4.3. Property Substitution

몇 가지 오퍼레이터와 간단한 조건 포현식으로 속성값을 치환할 수 있다. 일반적으로 #{operator['key']}같이 사용한다.

지원하는 오퍼레이터:

#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}

왼쪽이 사용하려는 값이고, 오른쪽에 있는 값은 디폴트 값이다. 이 예시에서 #{jobParameters['unresolving.prop']}를 매핑할 수 없다고 가정하면 file.separator 시스템 프로퍼티로 리졸브한다. 두 표현식을 모두 찾지 못하면 빈 문자열을 리턴한다. ‘;’로 구분해서 여러 조건을 사용할 수도 있다.


12.5. Processing Models

JSR-352는 스프링과 동일한 두 가지 기본 프로세싱 모델을 제공한다:

12.5.1. Item based processing

아이템 기반 처리는 ItemReader가 읽을 아이템 수를 청크 사이즈로 설정한다. 이 방법으로 step을 만들려면 item-count(디폴트는 10)를 명시해야 하고, 원한다면 checkpoint-policy를 item으로 등록할 수 있다(이게 디폴트다).

...
<step id="step1">
    <chunk checkpoint-policy="item" item-count="3">
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

아이템 기반 체크포인팅은 time-limit 속성을 추가로 지원한다. 이 값은 지정된 아이템 수를 처리하는 데 걸리는 시간 제한한다. 타임아웃되면 item-count를 몇으로 설정했는지는 상관없이 읽은 만큼만 청크로 처리한다.

12.5.2. Custom checkpointing

JSR-352에선 step을 커밋 인터벌 단위로 처리하는 것을 “checkpointing”이라고 한다. 아이템 기반 체크포인팅은 위에서 언급한 방법이다. 하지만 좀 더 견고한 방법이 필요할 때도 많다. 이 때문에 스펙에선 javax.batch.api.chunk.CheckpointAlgorithm 인터페이스를 구현해서 커스텀 체크포인팅 알고리즘을 만드는 것을 허용한다. 이 기능은 스프링 배치의 커스텀 completion policy와 동일하다. CheckpointAlgorithm 구현체를 사용하려면 아래처럼 step에 커스텀 checkpoint-policy를 설정하면 된다. 아래에선 fooCheckpointerCheckpointAlgorithm 구현체를 참조한다.

...
<step id="step1">
    <chunk checkpoint-policy="custom">
        <checkpoint-algorithm ref="fooCheckpointer"/>
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

12.6. Running a job

JSR-352 기반 job은 javax.batch.operations.JobOperator가 시작한다. 스프링 배치는 이 인터페이스 구현체를 제공한다 (org.springframework.batch.core.jsr.launch.JsrJobOperator). javax.batch.runtime.BatchRuntime이 이 구현체를 로딩한다. JSR-352 배치 job을 시작하는 코드는 다음과 같다:

JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties());

위 코드는 다음과 같은 일을 한다:

모든 JSR-352 기반 배치 잡은 비동기로 실행된다.

SimpleJobOperatorJobOperator#start를 호출하면 스프링 배치는 첫 번째 실행인지 이전에 실행했던, 이전 실행을 재시도하는 건지 판단한다. JSR-352 기반 JobOperator#start(String jobXMLName, Properties jobParameters)를 사용하면 프레임워크는 항상 새 JobInstance를 만든다 (JSR-352는 job 파라미터로 job을 식별하지 않는다). job을 재시작하고 싶으면 JobOperator#restart(long executionId, Properties restartParameters)를 호출해야 한다.


12.7. Contexts

JSR-352는 job, step의 메타 데이터에 접근하기 위한 두 가지 컨텍스트 객체를 정의한다: javax.batch.runtime.context.JobContext, javax.batch.runtime.context.StepContext. step 레벨 아티팩트(Batchlet, ItemReader 등)에서는 둘 다 사용할 수 있고 JobContext는 job 레벨 아티팩트(e.g. JobListener)에서 사용할 수 있다.

현재 스코프에서 JobContextStepContext를 참조하고 싶으면 간단하게 @Inject 애노테이션을 사용하면 된다:

@Inject
JobContext jobContext;

스프링의 @Autowire를 사용하면 이 컨텍스트를 주입할 수 없다.

스프링 배치에서는 JobContextStepContext가 각 실행 객체를 감싸고 있다 (JobExecution, StepExecution). StepContext#setPersistentUserData(Serializable data)로 저장한 데이터는 스프링 배치의 StepExecution#executionContext에 저장된다.


12.8. Step Flow

step flow는 JSR-352 기반 job에서도 스프링 배치와 유사하게 동작한다. 하지만 몇 가지가 미묘하게 다르다:


12.9. Scaling a JSR-352 batch job

전통적인 스프링 배치 job은 4가지 방식으로 확장할 수 있다 (마지막 두 개는 여러 JVM에서 실행할 수 있다):

JSR-352는 두 가지 확장법을 제공한다. 두 가지 모두 단일 JVM만 지원한다:

12.9.1. Partitioning

JSR-352의 파티셔닝은 스프링 배치의 파티셔닝과 개념적으로 동일하다. 각 워커는 메타데이터로 처리할 입력을 식별하고 완료하면 매니저에게 결과를 보고한다. 하지만 몇 가지 중요한 차이점이 있다:

job 레포지토리에 여전히 StepExecution이 있으며 JobExplorer와 스프링 배치 어드민으로 접근할 수 있다.

Artifact Interface Description
javax.batch.api.partition.PartitionCollector 워커 step이 매니저에게 정보를 전달할 수 있게 해준다. 워커 쓰레드 당 인스턴스가 하나씩 있다.
javax.batch.api.partition.PartitionAnalyzer PartitionCollector가 수집한 정보와 종료된 파티션의 상태를 받는 엔드포인트.
javax.batch.api.partition.PartitionReducer 파티션된 step이 보상 로직을 처리할 수 있게 해준다.

12.10. Testing

JSR-352 기반 job은 모두 비동기로 실행하기 때문에 언제 job이 종료됐는지 알기 어렵다. 스프링 배치는 테스트를 도와줄 org.springframework.batch.test.JsrTestUtils를 제공한다. 이 유틸리티 클래스는 job을 시작하고, 재시작하고, 완료를 기다리는 기능을 제공한다. job이 완료되면 JobExecution을 반환한다.


전체 목차는 여기에 있습니다.

<< >>