300===Dev Framework/Spring Batch

Spring Tasklet - 배치 작업의 작은 영웅 🦸‍♂️

블로글러 2024. 11. 7. 09:31

안녕하세요! 오늘은 Spring Batch의 가장 작은 실행 단위인 Tasklet에 대해 알아보겠습니다.

Tasklet이 뭔가요? 🤔

작은 집안일을 하나씩 처리하는 것처럼:

  • 단일 작업을 처리하는 최소 실행 단위
  • 한 번에 하나의 작업만 처리
  • Chunk 기반 처리보다 단순한 작업에 적합

기본 구조 📝

1. 기본 Tasklet 인터페이스

public interface Tasklet {
    RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) 
        throws Exception;
}

2. 간단한 구현 예시

@Component
public class SimpleTasklet implements Tasklet {

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        System.out.println("Simple Tasklet executed!");
        return RepeatStatus.FINISHED;
    }
}

실전 활용 예시 💪

1. 파일 정리 Tasklet

@Component
@Slf4j
public class FileCleanupTasklet implements Tasklet {

    private final String directoryPath;

    public FileCleanupTasklet(String directoryPath) {
        this.directoryPath = directoryPath;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        File directory = new File(directoryPath);
        File[] files = directory.listFiles();

        if (files != null) {
            for (File file : files) {
                if (file.delete()) {
                    log.info("Deleted file: {}", file.getName());
                }
            }
        }

        return RepeatStatus.FINISHED;
    }
}

2. 데이터베이스 초기화 Tasklet

@Component
@RequiredArgsConstructor
public class DatabaseCleanupTasklet implements Tasklet {

    private final JdbcTemplate jdbcTemplate;

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        jdbcTemplate.execute("TRUNCATE TABLE temporary_data");
        return RepeatStatus.FINISHED;
    }
}

3. 알림 발송 Tasklet

@Component
@RequiredArgsConstructor
public class NotificationTasklet implements Tasklet {

    private final NotificationService notificationService;

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        JobParameters jobParameters = 
            chunkContext.getStepContext().getStepExecution().getJobParameters();
        String message = jobParameters.getString("message");

        notificationService.sendAlert(message);
        return RepeatStatus.FINISHED;
    }
}

Job 설정에서 Tasklet 사용 🔧

@Configuration
@RequiredArgsConstructor
public class TaskletJobConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job taskletJob() {
        return jobBuilderFactory.get("taskletJob")
            .start(taskletStep())
            .next(anotherTaskletStep())
            .build();
    }

    @Bean
    public Step taskletStep() {
        return stepBuilderFactory.get("taskletStep")
            .tasklet(new SimpleTasklet())
            .build();
    }
}

Tasklet vs Chunk 🆚

Tasklet 적합 상황

  1. 단순 파일 조작
  2. 저장 프로시저 실행
  3. 데이터베이스 초기화
  4. 단순 알림 발송

Chunk 적합 상황

  1. 대량 데이터 처리
  2. 트랜잭션 단위 처리 필요
  3. 페이징 처리 필요

고급 기능 🚀

1. 상태 체크와 재시도

@Component
public class RetryableTasklet implements Tasklet {

    @Override
    @Retryable(value = {SQLException.class}, 
               maxAttempts = 3,
               backoff = @Backoff(delay = 1000))
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        // 재시도가 필요한 작업 로직
        return RepeatStatus.FINISHED;
    }

    @Recover
    public RepeatStatus recover(SQLException e, StepContribution contribution, 
                              ChunkContext chunkContext) {
        // 복구 로직
        return RepeatStatus.FINISHED;
    }
}

2. 조건부 실행

@Component
public class ConditionalTasklet implements Tasklet {

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                              ChunkContext chunkContext) throws Exception {
        ExecutionContext jobContext = 
            chunkContext.getStepContext().getStepExecution().getJobExecution()
                       .getExecutionContext();

        if (jobContext.containsKey("skipTasklet")) {
            return RepeatStatus.FINISHED;
        }

        // 실제 작업 수행
        return RepeatStatus.FINISHED;
    }
}

모범 사례 🌟

  1. 명확한 책임 분리

    @Component
    public class DataProcessingTasklet implements Tasklet {
     private final DataService dataService;
     private final ValidationService validationService;
    
     // 각 서비스가 명확한 책임을 가짐
    }
  2. 적절한 예외 처리

    @Override
    public RepeatStatus execute(StepContribution contribution, 
                           ChunkContext chunkContext) throws Exception {
     try {
         // 작업 로직
         return RepeatStatus.FINISHED;
     } catch (Exception e) {
         contribution.setExitStatus(ExitStatus.FAILED);
         throw e;
     }
    }

References 📚

  1. Spring Batch Official Documentation
    https://docs.spring.io/spring-batch/docs/current/reference/html/step.html#taskletStep

  2. Baeldung Tasklet Tutorial
    https://www.baeldung.com/spring-batch-tasklet-chunk

  3. Spring Batch Operations Guide
    https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html#springBatchTasklets

728x90