데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      [SpringBatch 연재 08] CompositeItemProcessor 으로 여러단계에 걸쳐 데이터 Transform하기

      KIDO 24.11.19
      1,364 4 6
      DEVOTEE 요약
      CompositeItemProcessor는 Spring Batch에서 제공하는 클래스로, 여러 ItemProcessor를 하나로 결합하여 순차적으로 데이터를 처리할 수 있게 해줍니다. 이 프로세서를 사용하면 코드의 명확성을 높이고, 재사용성을 강화하며, 유연한 데이터 처리 로직을 구현할 수 있는 반면, 설정이 복잡해지고 성능이 저하될 수 있다는 단점도 있습니다. 예시 코드에서는 두 개의 ItemProcessor를 사용하여 이름과 성별을 소문자로 변경하고, 나이에 20을 더하는 처리 단계를 설정하였습니다.

      CompositeItemProcessor 개요

      • CompositeItemProcessor는 Spring Batch에서 제공하는 ItemProcessor 인터페이스를 구현하는 클래스이다.

      • 여러 개의 ItemProcessor를 하나의 Processor로 연결하여 여러 단계의 처리를 수행할 수 있도록 한다.


      주요 구성 요소

      • Delegates: 처리를 수행할 ItemProcessor 목록이다.

      • TransactionAttribute: 트랜잭션 속성을 설정한다.


      장점

      • 단계별 처리: 여러 단계로 나누어 처리를 수행하여 코드를 명확하고 이해하기 쉽게 만들 수 있다.

      • 재사용 가능성: 각 단계별 Processor를 재사용하여 다른 Job에서도 활용할 수 있다.

      • 유연성: 다양한 ItemProcessor를 조합하여 원하는 처리 과정을 구현할 수 있다.


      단점

      • 설정 복잡성: 여러 개의 Processor를 설정하고 관리해야 하기 때문에 설정이 복잡해질 수 있다.

      • 성능 저하: 여러 단계의 처리 과정을 거치므로 성능이 저하될 수 있다.


      샘플 코드

      • 샘플 코드는 기존 06.06.MyBatisItemReaderSample을 그대로 이용한다.

      LowerCaseItemProcessor 작성하기

      package com.schooldevops.springbatch.batchsample.jobs.mybatis;
      
      import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
      import org.springframework.batch.item.ItemProcessor;
      
      /**
       * 이름, 성별을 소문자로 변경하는 ItemProcessor
       */
      public class LowerCaseItemProcessor implements ItemProcessor<Customer, Customer> {
          @Override
          public Customer process(Customer item) throws Exception {
              item.setName(item.getName().toLowerCase());
              item.setGender(item.getGender().toLowerCase());
              return item;
          }
      }
      • ItemProcessor를 구현하고, process 메소드를 구현한다.

      • 여기서는 이름과 성별을 소문자로 변경한다.

      After20YearsItemProcessor 작성하기

      • 나이에 20년을 더하기 위한 ItemProcessor를 작성한다.

      package com.schooldevops.springbatch.batchsample.jobs.mybatis;
      
      import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
      import org.springframework.batch.item.ItemProcessor;
      
      /**
       * 나이에 20년을 더하는 ItemProcessor
       */
      public class After20YearsItemProcessor implements ItemProcessor<Customer, Customer> {
          @Override
          public Customer process(Customer item) throws Exception {
              item.setAge(item.getAge() + 20);
              return item;
          }
      }
      • 동일하게 ItemProcessor를 구현하고, process 메소드를 구현하였다.

      • 단순히 나이에 20을 더했다.

      CompositeItemProcess 구현하기

          @Bean
          public CompositeItemProcessor<Customer, Customer> compositeItemProcessor() {
              return new CompositeItemProcessorBuilder<Customer, Customer>()
                      .delegates(List.of(
                              new LowerCaseItemProcessor(),
                              new After20YearsItemProcessor()
                      ))
                      .build();
          }
      • 이제 CompositeItemProcessorBuilder를 이용하여 delegates를 통해서 ItemProcessor가 수행할 순서대로 배열을 만들어 전달했다.


      전체 샘플코드

      package com.schooldevops.springbatch.batchsample.jobs.mybatis;
      
      import com.schooldevops.springbatch.batchsample.jobs.models.Customer;
      import lombok.extern.slf4j.Slf4j;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.mybatis.spring.batch.MyBatisPagingItemReader;
      import org.mybatis.spring.batch.builder.MyBatisPagingItemReaderBuilder;
      import org.springframework.batch.core.Job;
      import org.springframework.batch.core.Step;
      import org.springframework.batch.core.job.builder.JobBuilder;
      import org.springframework.batch.core.launch.support.RunIdIncrementer;
      import org.springframework.batch.core.repository.JobRepository;
      import org.springframework.batch.core.step.builder.StepBuilder;
      import org.springframework.batch.item.file.FlatFileItemWriter;
      import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
      import org.springframework.batch.item.support.CompositeItemProcessor;
      import org.springframework.batch.item.support.builder.CompositeItemProcessorBuilder;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.core.io.FileSystemResource;
      import org.springframework.transaction.PlatformTransactionManager;
      
      import javax.sql.DataSource;
      import java.util.List;
      
      @Slf4j
      @Configuration
      public class MyBatisReaderJobConfig {
      
          /**
           * CHUNK 크기를 지정한다.
           */
          public static final int CHUNK_SIZE = 2;
          public static final String ENCODING = "UTF-8";
          public static final String MYBATIS_CHUNK_JOB = "MYBATIS_CHUNK_JOB";
      
          @Autowired
          DataSource dataSource;
      
          @Autowired
          SqlSessionFactory sqlSessionFactory;
      
          @Bean
          public MyBatisPagingItemReader<Customer> myBatisItemReader() throws Exception {
      
              return new MyBatisPagingItemReaderBuilder<Customer>()
                      .sqlSessionFactory(sqlSessionFactory)
                      .pageSize(CHUNK_SIZE)
                      .queryId("com.schooldevops.springbatch.batchsample.jobs.selectCustomers")
                      .build();
          }
      
      
          @Bean
          public FlatFileItemWriter<Customer> customerCursorFlatFileItemWriter() {
              return new FlatFileItemWriterBuilder<Customer>()
                      .name("customerCursorFlatFileItemWriter")
                      .resource(new FileSystemResource("./output/customer_new_v4.csv"))
                      .encoding(ENCODING)
                      .delimited().delimiter("\t")
                      .names("Name", "Age", "Gender")
                      .build();
          }
      
          @Bean
          public CompositeItemProcessor<Customer, Customer> compositeItemProcessor() {
              return new CompositeItemProcessorBuilder<Customer, Customer>()
                      .delegates(List.of(
                              new LowerCaseItemProcessor(),
                              new After20YearsItemProcessor()
                      ))
                      .build();
          }
      
      
          @Bean
          public Step customerJdbcCursorStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws Exception {
              log.info("------------------ Init customerJdbcCursorStep -----------------");
      
              return new StepBuilder("customerJdbcCursorStep", jobRepository)
                      .<Customer, Customer>chunk(CHUNK_SIZE, transactionManager)
                      .reader(myBatisItemReader())
                      .processor(compositeItemProcessor())
                      .writer(customerCursorFlatFileItemWriter())
                      .build();
          }
      
          @Bean
          public Job customerJdbcCursorPagingJob(Step customerJdbcCursorStep, JobRepository jobRepository) {
              log.info("------------------ Init customerJdbcCursorPagingJob -----------------");
              return new JobBuilder(MYBATIS_CHUNK_JOB, jobRepository)
                      .incrementer(new RunIdIncrementer())
                      .start(customerJdbcCursorStep)
                      .build();
          }
      }

      결과

      kido	30	m
      kido2	40	m
      kido3	50	m
      kido4	60	m
      kido5	31	f
      kido6	35	f
      kido7	45	m
      kido8	53	f
      kido9	61	m
      kido10	37	m


      WrapUp

      • 지금까지 ItemProcessor를 여러개 생성하고, 이를 순서대로 적용하는 샘플을 살펴보았다.

      • CompositeItemProcessor를 통해서 리스트의 순서대로 ItemProcessor이 적용된 것을 알 수 있다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      KIDO 님의 최신 블로그

      더보기
      동영상 기고하기