본문 바로가기
Study/스프링배치 완벽 가이드

Day 4. 잡 파라미터 이해하기

반응형
스프링 배치 완벽 가이드 내용 정리입니다
3장. 예제 잡 어플리케이션은 스킵했습니다

 

http://www.yes24.com/Product/Goods/99422216

 

스프링 배치 완벽 가이드 2/e - YES24

스프링 배치의 `Hello, World!`부터 최근 플랫폼의 발전에 따른 클라우드 네이티브 기술을 활용한 배치까지 폭넓은 스프링 배치 활용 방법과 이와 관련된 유용한 내용을 다룬다. 또한 스프링 프레임

www.yes24.com


1. 잡 파라미터

  • 앞서 배치 잡이 실행되면 JobInstnace가 생기고, 생겨난 JobInstance는 JobName + JobParameters로 식별된다고 했습니다
  • 이를 통해 동일하게 식별되는 JobInstance(같은 JobName이면서 같은 JobParameter로 실행된)가 정상적으로 성공된 JobExecution을 가진다면 해당 JobInstance는 다시 실행할 수 없습니다
    • 위에서 정의한 예제를 두번이상 실행하게 되면 아래와 같은 로그를 보실수 있습니다
      // teminal
      Step already complete or not restartable, so no action to execute: StepExecution​
  • 스프링 부트의 JobLauncherCommandLineRunner를 기준으로 파라미터 전달 방법을 소개하겠습니다
    • key=value 쌍으로 전달한다
    • 사용자가 배치 잡에게 파라미터를 전달하면 잡 러너는 JobParameters 인스턴스를 생성하는데, 해당 인스턴스는 잡이 전달받는 모든 파라미터의 컨테이너 역할을 한다
  • JobPrameters 인스턴스
    • java.util.Map<String, JobParameters> 객체의 래퍼
    • 스프링 배치는 파라미터의 타입을 변환하는 기능을 제공하며, 변환된 타입에 맞는 JobParameter의 접근자를 제공한다
    • 지원하는 타입
      • String
      • Double
      • Long
      • java.util.Date
        • LocalDate, LocalTime, LocalDateTime이 없기 때문에 String을 변환하는 방식으로 사용해야한다
  • 잡 파라미터에 접근하기 :: ChunkContext
    • ChunkContext: execute 메서드에는 두개의 파라미터가 존재
      • StepContribution: 아직 커밋되지 않은 현재 트랜잭션에 대한 정보(쓰기 수, 읽기 수등)
      • ChunkContext: 실행 시점의 잡 상태를 제공, Tasklet 내에서는 처리 중인 청크와 관련된 정보 보유
    • ChunkContext에는 JobParameters가 포함된 org.springframework.batch.core.scope.context.StepContext의 참조가 있다
    • 예제코드
      // ChunkContextJobParamterBatch.class
      @RequiredArgsConstructor
      @Configuration
      public class ChunkContextJobParameterBatch {
      
          private final JobBuilderFactory jobBuilderFactory;
          private final StepBuilderFactory stepBuilderFactory;
      
          @Bean
          public Job chunkContextJobParameterJob() {
              return this.jobBuilderFactory.get("chunkContextJobParamterJob")
                      .start(chunkContextJobParameterStep())
                      .build();
          }
      
          @Bean
          public Step chunkContextJobParameterStep() {
              return this.stepBuilderFactory.get("chunkContextJobParameterStep")
                      .tasklet(((contribution, chunkContext) -> {
                          String name = (String) chunkContext.getStepContext().getJobParameters().get("name");
      
                          System.out.printf("Hello, %s!%n", name);
                          return RepeatStatus.FINISHED;
                      }))
                      .build();
          }
      }​

      • getJobParameters()를 호출하는 방식으로 잡 파라미터를 가져오면 Map<String, Object>가 반환된다
      • 위 예제처럼 타입 캐스팅이 필요하다
  • 잡 파라미터에 접근하기 :: Late Binding
    • 주입하기 가장 쉬운 방식
    • 스프링 구성을 사용해 주입, 부트스트랩시 바인딩하는 것이 제일 좋다
    • 예제코드
      // LateBindingJobParameterBatch.class
      @RequiredArgsConstructor
      @Configuration
      public class LateBindingJobParameterBatch {
      
          private final JobBuilderFactory jobBuilderFactory;
          private final StepBuilderFactory stepBuilderFactory;
      
          @Bean("lateBindingJobParameterBatchJob")
          public Job job() {
              return this.jobBuilderFactory.get("lateBindingJobParameterBatchJob")
                      .start(step(null))
                      .build();
          }
      
          @Bean("lateBindingJobParameterBatchStep")
          @JobScope
          public Step step(@Value("#{jobParameters['name']}") String name) {
              return this.stepBuilderFactory.get("lateBindingJobParameterBatchStep")
                      .tasklet(((contribution, chunkContext) -> {
                          System.out.printf("Hello, %s!%n", name);
                          return RepeatStatus.FINISHED;
                      }))
                      .build();
          }
      }​

      • 스프링 EL(Expression Language)을 사용해 값을 전달한다
      • LateBinding으로 구성될 빈은 StepScope나 JobScope를 반드시 가져야한다
      • StepScope: 스텝의 실행범위에 들어갈 때까지 빈 생성을 지연 시킴
      • JobScope: 잡의 실행범위에 들어갈 때까지 빈 생성을 지연 시킴
  • 잡 파라미터 유효성 검증하기
    • 소프트웨어 외부에서 입력을 받아들일 때마다 그 값이 예상대로 유효한지 확인하는 것이 좋다
    • org.springframework.batch.core.JobParametersValidator 인터페이스를 구현하고 해당 구현체를 잡 내에 구성하면 된다
    • JobParametersInvalidException의 발생유무로 valid를 통과시킨다
    • 이미 존재하는 DefaultJobParametersValidator를 이용해 requiredKeys와 optionalKeys라는 선택적인 의존성을 주입할 수 도 있다
    • 커스텀 검증 예제코드
      // JobParameterValidator.java
      public class JobParameterValidator implements JobParametersValidator {
      
          @Override
          public void validate(JobParameters parameters) throws JobParametersInvalidException {
              String fileName = Objects.requireNonNull(parameters).getString("fileName");
      
              if (StringUtils.hasText(fileName)) {
                  throw new JobParametersInvalidException("fileName parameter is missing");
              }
              else if(!StringUtils.endsWithIgnoreCase(fileName, "csv")) {
                  throw new JobParametersInvalidException("fileName parameter does not use the csv file extension");
              }
          }
      
      }​

      • JobParametersInvalidException이 발생하지 않는다면 유효성 검증을 통과했다고 판단한다
    • DefaultJobParametersValidator
      • 필수 파라미터가 누락없이 전달됐는지 확인하는 유효성 검증기 ( SpringBatch 기본 제공 )
      • requiredKeys: 필수 파라미터 목록
      • optionalKeys: 필수가 아닌 파라미터 목록
      • 예제코드
        // HelloWorldBatchWithValidator.java
        @RequiredArgsConstructor
        @Configuration
        public class HelloWorldBatchWithValidator {
        
            private static final String JOB_NAME = "helloWorldBatchWithValidatorJob";
            private static final String STEP_NAME = "helloWorldBatchWithValidatorStep";
            private static final String VALIDATOR_NAME = "helloWorldBatchValidator";
        
            private final JobBuilderFactory jobBuilderFactory;
            private final StepBuilderFactory stepBuilderFactory;
        
        
            @Bean(name = VALIDATOR_NAME)
            public JobParametersValidator validator() {
                DefaultJobParametersValidator validator = new DefaultJobParametersValidator();
        
                validator.setRequiredKeys(new String[]{"fileName"});
                validator.setOptionalKeys(new String[]{"name"});
        
                return validator;
            }
        
        
            @Bean(name = JOB_NAME)
            public Job helloWorldBatchWithValidatorJob() {
                return this.jobBuilderFactory.get(JOB_NAME)
                        .validator(validator())
                        .start(helloWorldBatchWithValidatorStep(null, null))
                        .build();
            }
        
            @Bean
            @JobScope
            public Step helloWorldBatchWithValidatorStep(@Value("#{jobParameters['name']}") String name,
                                                         @Value("#{jobParameters['fileName']}") String fileName) {
                return this.stepBuilderFactory.get(STEP_NAME)
                        .tasklet((contribution, chunkContext) -> {
        
                            System.out.println("Hello, " + name);
                            System.out.println("fileName= " + fileName);
        
                            return RepeatStatus.FINISHED;
                        })
                        .build();
            }
        }​
      • 결과( 필수파라미터인 fileName을 안넣었을 경우 )
        • --job.name=helloWorldBatchWithValidatorJob name=joojimin version=5
        • "The JobParameters do not contain required keys: [fileName]" 오류가 뜬다
    • CompositeJobParametersValidator
      • 원하는 두 개의 유효성 검증기를 사용할 수 있도록 변경된 잡 구성
      • 파라미터로 제공되는 두 개의 유효성 검증기를 전부 통과해야 Job이 실행된다
  • 잡 파라미터 증가시키기
    • 주어진 식별 파라미터 집합(JobParameters)으로 같은 잡은 단 한번만 실행시킬 수 있다
    • 이를 피하는 간단한 방법은 JobParametersIncrementer를 사용하는 것이다
    • org.springframework.batch.core.JobParametersIncrementer
      • 잡에서 사용할 파라미터를 고유하게 생성할 수 있도록 스프링 배치가 제공하는 인터페이스
      • 매 실행 시에 타임 스탬프를 추가할 수도 있다
      • 스프링 배치는 기본적인 구현체를 제공하며 해당 구현체는 run.id long 타입 파라미터의 값을 증가시킨다
    • 예제코드
      // JobParametersIncrementerBatch.java
      @RequiredArgsConstructor
      @Configuration
      public class JobParametersIncrementerBatch {
      
          private static final String JOB_NAME = "jobParametersIncrementerBatchJob";
          private static final String STEP_NAME = "jobParametersIncrementerBatchStep";
      
          private final JobBuilderFactory jobBuilderFactory;
          private final StepBuilderFactory stepBuilderFactory;
      
      
          @Bean(JOB_NAME)
          public Job jobParametersIncrementerBatchJob() {
              return this.jobBuilderFactory.get(JOB_NAME)
                      .start(jobParametersIncrementerBatchStep())
                      .incrementer(new RunIdIncrementer())
                      .build();
          }
      
      
          @Bean(STEP_NAME)
          public Step jobParametersIncrementerBatchStep() {
              return this.stepBuilderFactory.get(STEP_NAME)
                      .tasklet(((contribution, chunkContext) -> {
                          return RepeatStatus.FINISHED;
                      }))
                      .build();
          }
      }​
    • 결과
      • Job: [SimpleJob: [name=jobParametersIncrementerBatchJob]] launched with the following parameters: [{run.id=1}]
      • 파라미터는 jobParametersIncrementerBatchJob, jobName만 주고 실행시킨 결과이다
      • job이 실행될 때 run.id가 자동으로 들어간 것을 확인할 수 있다
    • 추가적으로 유효성 검증기를 사용한다면 RunIdIncrementer를 도입하면서 자동으로 추가되는 파라미터(run.id)의 검증도 추가해줘야한다 ( optional 혹은 required )
    • 커스텀한 JobParametersIncrementer를 사용하고 싶으면 그냥 JobParametersIncrementer를 구현하여 getNext 메서드를 구현하면 된다

 

728x90
반응형

'Study > 스프링배치 완벽 가이드' 카테고리의 다른 글

Day 6. ExecutionContext  (0) 2021.12.26
Day 5. 잡 리스너 이해하기  (0) 2021.11.26
Day 3. 잡과 스텝 이해하기  (0) 2021.11.06
Day 2. 스프링 배치  (0) 2021.11.05
Day 1. 배치와 스프링  (0) 2021.11.01