본문 바로가기
Spring/SpringBoot

[SpringBoot] SpringBatch 사용하기 (1) - Scheduler를 이용하여 Tasklet, Chunk 배치 만들기

by J4J 2023. 8. 22.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 SpringBatch 사용하기 첫 번째인 Scheduler를 이용하여 Tasklet, Chunk 배치 만드는 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

SpringBatch란?

 

SpringBatch는 Spring에서 작업해야 되는 대용량 처리들을 반복적으로 수행할 수 있도록 도와주는 배치 프레임워크입니다.

 

 

 

서비스 운영을 하다 보면 사용자의 요청과 관련 없이 일정 시간마다 작업이 이루어져야 하는 것들이 발생하기 마련입니다.

 

예를 들면, 일정 기간이 지난 사용자들의 계정을 잠김 처리를 수행하던가 또는 매일 발생된 로그들을 이용하여 분석된 결괏값을 새롭게 적재하는 것들이 있습니다.

 

이런 기능들을 구현할 때 일정 시간마다 개발자가 로직이 동작되도록 작업을 항상 할 수 없기 때문에 배치를 만들게 되고 해당 배치는 매번 정해진 시간마다 동작되어 다른 부가적인 요소 없이 로직이 처리되도록 합니다.

 

 

 

배치를 만들 수 있는 것들은 다양하게 존재합니다.

 

대표적으로 AWS를 예시로 말씀드릴 수 있습니다.

 

이 외에도 Spring 진영에서는 SpringBatch라는 것을 활용해 볼 수 있습니다.

 

SpringBatch를 활용하면 어떤 작업이 수행되어야 한다는 Job을 정의하고 생성된 Job을 일정한 주기마다 동작될 수 있도록 Scheduler에 등록할 수 있습니다.

 

이를 통해 다른 부가적인 요소 없이 우리가 원하는 시간마다 동일한 동작을 수행할 수 있도록 설정할 수 있습니다.

 

 

 

Spring Batch 공식 문서를 확인해 보면 다음과 같은 특징들이 있다고 설명하고 있습니다.

 

  • Transaction Management
  • Chunk based processing
  • Declarative I/O
  • Start/Stop/Restart
  • Retry/Skip
  • Web based administration interface

 

 

 

또한 로깅 및 추적, 작업 처리 통계, 리소스 관리 등도 대용량으로 처리될 수 있도록 지원하고 있습니다.

 

 

반응형

 

 

기본 설정

 

다른 설명들을 할 때 소스 코드와 함께 활용될 예정이기 때문에 SpringBatch 사용을 위한 기본적인 설정만 먼저 하도록 하겠습니다.

 

그리고 버전 별로 기능이 deprecated 되거나 또는 작성 방식이 조금씩 상이해집니다.

 

해당 코드는 Java17과 Spring Boot 버전 3.1.2에서 작성되었으니 참고 부탁드립니다.

 

 

 

[ 1. 의존성 추가 ]

먼저 의존성 추가입니다.

 

SpringBatch를 사용할 때는 기본적으로 배치와 관련된 메타데이터를 저장하는 DB가 필요합니다.

 

그래서 SpringBatch 관련 의존성을 추가하면서 DB 관련된 설정도 각자의 상황에 맞게 다음과 같이 해주시면 됩니다.

 

dependencies {
	// mysql
	runtimeOnly 'com.mysql:mysql-connector-j'

	// jpa
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

	// jpa log spy
	implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0'

	// batch
	implementation 'org.springframework.boot:spring-boot-starter-batch'
}

 

 

 

[ 2. DB 리소스 추가 ]

 

SpringBatch와는 관련 없고 DB 연결을 위한 리소스 추가입니다.

 

application.properties 또는 application.yml 파일에 다음과 같이 정보들을 추가해 줍니다.

 

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/batch?serverTimezone=UTC
spring.datasource.username=
spring.datasource.password=

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true

 

 

 

[ 3. Config 클래스 파일 추가 ]

 

package com.batch.config;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    // SpringBatch 사용을 위한 Config
}

 

package com.batch.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class ScheduleConfig {
    // Batch Scheduler 사용을 위한 Config
}

 

 

 

 

[ 4. SpringBatch 메타데이터 스키마 추가 ]

 

IntelliJ를 사용하는 경우 "Shift + Shfit"를 누르면 다음과 같이 전체 파일들을 검색하는 창이 나옵니다.

 

그리고 해당 창의 검색어에 "schema-mysql.sql"을 입력하면 다음과 같이 하나가 조회되는 것을 볼 수 있습니다.

 

메타데이터 스키마 쿼리

 

 

 

그러면 다음과 같이 SpringBatch에 사용되는 메타데이터 스키마 쿼리를 확인해 볼 수 있는데 해당 쿼리를 연결되는 DB에서 실행하여 테이블들을 모두 생성해 줍니다.

 

-- Autogenerated: do not edit this file

CREATE TABLE BATCH_JOB_INSTANCE  (
	JOB_INSTANCE_ID BIGINT  NOT NULL PRIMARY KEY ,
	VERSION BIGINT ,
	JOB_NAME VARCHAR(100) NOT NULL,
	JOB_KEY VARCHAR(32) NOT NULL,
	constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;

CREATE TABLE BATCH_JOB_EXECUTION  (
	JOB_EXECUTION_ID BIGINT  NOT NULL PRIMARY KEY ,
	VERSION BIGINT  ,
	JOB_INSTANCE_ID BIGINT NOT NULL,
	CREATE_TIME DATETIME(6) NOT NULL,
	START_TIME DATETIME(6) DEFAULT NULL ,
	END_TIME DATETIME(6) DEFAULT NULL ,
	STATUS VARCHAR(10) ,
	EXIT_CODE VARCHAR(2500) ,
	EXIT_MESSAGE VARCHAR(2500) ,
	LAST_UPDATED DATETIME(6),
	constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
	references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
	JOB_EXECUTION_ID BIGINT NOT NULL ,
	PARAMETER_NAME VARCHAR(100) NOT NULL ,
	PARAMETER_TYPE VARCHAR(100) NOT NULL ,
	PARAMETER_VALUE VARCHAR(2500) ,
	IDENTIFYING CHAR(1) NOT NULL ,
	constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
	references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;

CREATE TABLE BATCH_STEP_EXECUTION  (
	STEP_EXECUTION_ID BIGINT  NOT NULL PRIMARY KEY ,
	VERSION BIGINT NOT NULL,
	STEP_NAME VARCHAR(100) NOT NULL,
	JOB_EXECUTION_ID BIGINT NOT NULL,
	CREATE_TIME DATETIME(6) NOT NULL,
	START_TIME DATETIME(6) DEFAULT NULL ,
	END_TIME DATETIME(6) DEFAULT NULL ,
	STATUS VARCHAR(10) ,
	COMMIT_COUNT BIGINT ,
	READ_COUNT BIGINT ,
	FILTER_COUNT BIGINT ,
	WRITE_COUNT BIGINT ,
	READ_SKIP_COUNT BIGINT ,
	WRITE_SKIP_COUNT BIGINT ,
	PROCESS_SKIP_COUNT BIGINT ,
	ROLLBACK_COUNT BIGINT ,
	EXIT_CODE VARCHAR(2500) ,
	EXIT_MESSAGE VARCHAR(2500) ,
	LAST_UPDATED DATETIME(6),
	constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
	references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;

CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT  (
	STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
	SHORT_CONTEXT VARCHAR(2500) NOT NULL,
	SERIALIZED_CONTEXT TEXT ,
	constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
	references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ENGINE=InnoDB;

CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT  (
	JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
	SHORT_CONTEXT VARCHAR(2500) NOT NULL,
	SERIALIZED_CONTEXT TEXT ,
	constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
	references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;

CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_STEP_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_STEP_EXECUTION_SEQ);

CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_EXECUTION_SEQ);

CREATE TABLE BATCH_JOB_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_SEQ);

 

 

 

[ 5. 테스트에 사용될 스키마 ]

 

SpringBatch의 기본 설정은 4번까지입니다.

 

지금 생성할 스키마는 아래 여러 테스트 코드들을 작성할 때 활용될 예정이고 JPA를 사용할 것이기 때문에 관련 Entity, Repository 설정들도 해두겠습니다.

 

// Schema
create table log (
    no bigint auto_increment primary key,
    contents varchar(300),
    create_at datetime,
    update_at datetime
);

// Entity
package com.batch.entity;

import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Entity
@Table(name = "log")
public class LogEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long no;
    private String contents;
    private String createAt;
    private String updateAt;

    /**
     * contents의 값을 수정한다.
     *
     * @param contents 수정될 contents
     */
    public void updateContents(String contents) {
        this.contents = contents;
        this.updateAt = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}


// Repository
package com.batch.repository;

import com.batch.entity.LogEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface LogJpaRepository extends JpaRepository<LogEntity, Long> {

}

 

 

 

 

Job과 Step

 

SpringBatch 소스를 작성하기 전 알고 있어야 되는 Job과 Step이라는 개념이 있습니다.

 

 

 

먼저 Job은 배치 처리가 이루어지는 가장 큰 단위입니다.

 

Job의 특징은 다음과 같습니다.

 

  • JobLauncher에 의해 실행되는 객체
  • 1개 이상의 Step을 보유

 

 

 

다음으로 Step은 Job에 의해 실행되는 단계별 실행 단위입니다.

 

Step의 특징은 다음과 같습니다.

 

  • Job에 의해 실행되며 Job에서 설정한 순서대로 Step이 동작
  • Tasklet 또는 Chunk 중 하나를 선택하여 배치 처리 수행

 

 

 

 

Tasklet

 

SpringBatch에서 Tasklet은 태스크 처리를 여러 단계로 구분하지 않고 하나의 태스크만 수행하기 위한 용도로 사용됩니다.

 

기본적인 배치 처리를 수행할 때 데이터를 조회하고, 데이터를 쓰는 작업이 진행될 텐데 이 모든 작업을 한 번의 트랜잭션 처리를 통해 수행되기 때문에 조회 및 쓰기가 모두 적용될 때까지 대기하게 됩니다.

 

그래서 Tasklet은 대용량 처리를 수행할 때는 적합하지 않습니다.

 

하지만 단계를 구분하지 않고 하나의 태스크에 모든 작업을 정의하기 때문에 간단한 처리를 수행할 때는 오히려 Tasklet을 사용하는 것이 더 효과적입니다.

 

위의 설정을 기반으로 하여 Tasklet을 통해 로그 데이터를 일정 주기마다 적재하는 배치를 만들어보겠습니다.

 

 

 

[ 1. JobConfig 클래스 파일 추가 ]

 

package com.batch.jobConfig;

import com.batch.entity.LogEntity;
import com.batch.repository.LogJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
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;
import org.springframework.transaction.PlatformTransactionManager;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Configuration
@RequiredArgsConstructor
@Slf4j
public class LogRegisterJobConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final LogJpaRepository logJpaRepository;

    /**
     * 로그 등록을 위한 Job
     *
     * @return 로그 등록 Job
     */
    @Bean
    public Job logRegisterJob() {
        return new JobBuilder("logRegisterJob", jobRepository)
                .start(contentsLogRegisterStep()) // job이 처음 시작될 때 실행되는 step
                .next(dateLogRegisterStep()) // 이전 step이 끝나면 실행되는 step
                .preventRestart() // job 실행이 실패할 경우 재시작 막기
                .build();
    }

    /**
     * contents가 입력된 로그 등록을 위한 Step
     *
     * @return contents 입력 로그 등록 Step
     */
    @Bean
    public Step contentsLogRegisterStep() {
        return new StepBuilder("contentsLogRegisterStep", jobRepository)
                .tasklet(contentsLogRegisterTasklet(), transactionManager) // step에서 실행될 tasklet
                .build();
    }

    /**
     * contents가 입력된 로그 등록을 위한 Tasklet
     *
     * @return contents 입력 로그 등록 Tasklet
     */
    @Bean
    public Tasklet contentsLogRegisterTasklet() {
        return (contribution, chunkContext) -> {
            // 로그 데이터 적재
            logJpaRepository.save(
                    LogEntity
                            .builder()
                            .contents("contentsLogRegisterTasklet")
                            .build()
            );

            // 배치 종료
            return RepeatStatus.FINISHED;
        };
    }

    /**
     * date가 입력된 로그 등록을 위한 Step
     *
     * @return date 입력 로그 등록 Step
     */
    @Bean
    public Step dateLogRegisterStep() {
        return new StepBuilder("dateLogRegisterStep", jobRepository)
                .tasklet(dateLogRegisterTasklet(), transactionManager) // step에서 실행될 tasklet
                .build();
    }

    /**
     * date가 입력된 로그 등록을 위한 Tasklet
     *
     * @return date 입력 로그 등록 Tasklet
     */
    @Bean
    public Tasklet dateLogRegisterTasklet() {
        return (contribution, chunkContext) -> {
            // 로그 데이터 적재
            logJpaRepository.save(
                    LogEntity
                            .builder()
                            .createAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                            .updateAt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                            .build()
            );

            // 랜덤값이 조건을 넘는 경우 배치 종료
            if (Math.random() * 10 >= 8) {
                return RepeatStatus.FINISHED;
            }

            // 랜덤값이 조건을 넘지 않는 경우 tasklet 처리 반복
            return RepeatStatus.CONTINUABLE;
        };
    }
}

 

 

 

[ 2. Schedule 클래스 파일 추가 ]

 

package com.batch.schedule;

import com.batch.jobConfig.LogRegisterJobConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class LogSchedule {

    private final JobLauncher jobLauncher;
    private final LogRegisterJobConfig logRegisterJobConfig;

    @Scheduled(cron = "0/10 * * * * ?") // 10초마다 Job 실행, cron 표현식 활용 (초 분 시 일 월 요일)
    public void logRegister() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        jobLauncher.run(
                logRegisterJobConfig.logRegisterJob(),
                new JobParametersBuilder()
                        .toJobParameters()
        );
    }
}

 

 

 

위의 소스를 정리해 보면 10초마다 로그 등록을 위한 Job을 실행하며 해당 Job에는 2개의 Step으로 구분하여 배치 처리가 이루어진다고 말할 수 있습니다.

 

또한 Schedule 등록은 cron 표현식을 활용한 것을 볼 수 있는데 cron 표현식을 제외하고도 fixedDelay, fixedRate 등을 사용할 수도 있습니다.

 

 

 

 

Chunk

 

SpringBatch에서 Chunk는 태스크 처리를 Chunk 단위를 이용하여 여러 개로 묶어 병렬적으로 처리될 수 있도록 도와줍니다.

 

또한 Tasklet처럼 모든 작업을 한 번에 처리하지 않기 때문에 Chunk 단위별로 트랜잭션 처리가 이루어집니다.

 

그래서 대용량 처리를 할 땐 Tasklet보다 Chunk를 이용하는 것이 적합합니다.

 

 

 

Chunk에는 다음과 같은 3가지 개념이 있습니다.

 

  • ItemReader → 데이터를 조회
  • ItemProcessor → 조회된 데이터를 처리
  • ItemWriter → 처리된 데이터를 저장

 

 

 

이번에는 Chunk를 이용하여 DB에서 데이터를 조회해 온 뒤 값을 수정하는 작업을 배치로 만들어보겠습니다.

 

 

 

[ 1. JobConfig 클래스 파일 추가 ]

 

package com.batch.jobConfig;

import com.batch.entity.LogEntity;
import com.batch.repository.LogJpaRepository;
import jakarta.persistence.EntityManagerFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Slf4j
@Configuration
@RequiredArgsConstructor
public class LogUpdateJobConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final LogJpaRepository logJpaRepository;
    private final EntityManagerFactory entityManagerFactory;

    /**
     * 로그 수정을 위한 Job
     *
     * @return 로그 수정 Job
     */
    @Bean
    public Job logUpdateJob() {
        return new JobBuilder("logUpdateJob", jobRepository)
                .start(logUpdateStep()) // job이 처음 시작될 때 실행되는 step
                .preventRestart() // job 실행이 실패할 경우 재시작 막기
                .build();
    }

    /**
     * 로그 수정을 위한 Step
     *
     * @return 로그 수정 Step
     */
    @Bean
    public Step logUpdateStep() {
        return new StepBuilder("logUpdateStep", jobRepository)
                .<LogEntity, LogEntity>chunk(20, transactionManager) // transaction 처리를 20개 단위씩 묶어 처리, Generic은 <reader에서 넘겨주는 객체, writer로 넘겨줄 객체>
                .reader(logUpdateItemReader())
                .processor(logUpdateItemProcessor())
                .writer(logUpdateItemWriter())
                .build();
    }

    /**
     * 로그 수정을 위한 ItemReader (데이터 조회)
     *
     * @return 로그 수정 ItemReader
     */
    @Bean
    public ItemReader<LogEntity> logUpdateItemReader() {
        return new JpaPagingItemReaderBuilder<LogEntity>()
                .name("logRegisterItemReader")
                .entityManagerFactory(entityManagerFactory)
                .pageSize(10) // 10개 단위로 페이징 처리하며 조회
                .queryString("select l from LogEntity l") // JPQL을 이용하여 수정할 데이터 조회
                .build();
    }

    /**
     * 로그 수정을 위한 ItemProcessor (데이터 수정 작업 처리) (ItemProcessor Generic은 <reader에서 넘겨주는 객체, writer로 넘겨줄 객체>)
     *
     * @return 로그 수정 ItemProcessor
     */
    @Bean
    public ItemProcessor<LogEntity, LogEntity> logUpdateItemProcessor() {
        // reader에서 넘겨준 객체를 1개 단위로 처리 (한 번에 chunkSize 만큼 수행)
        return logEntity -> {
            logEntity.updateContents("updateContents");
            return logEntity;
        };
    }

    /**
     * 로그 수정을 위한 ItemWriter (데이터 쓰기)
     *
     * @return 로그 수정 ItemWriter
     */
    @Bean
    public ItemWriter<LogEntity> logUpdateItemWriter() {
        // processor에서 넘겨준 객체를 chunk 단위로 처리 (한 번에 chunkSize 만큼 수행)
        return logEntities -> {
            for (LogEntity logEntity : logEntities) {
                logJpaRepository.save(logEntity);
            }
        };
    }
}

 

 

 

[ 2. Schedule 추가 ]

 

package com.batch.schedule;

import com.batch.jobConfig.LogUpdateJobConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class LogSchedule {

    private final JobLauncher jobLauncher;
    private final LogUpdateJobConfig logUpdateJobConfig;

    @Scheduled(fixedRate = 20000) // 메서드가 실행된 시간부터 20초마다 Job 실행
    public void logUpdate() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException {
        jobLauncher.run(
                logUpdateJobConfig.logUpdateJob(),
                new JobParametersBuilder()
                        .toJobParameters()
        );
    }
}

 

 

 

위의 소스를 정리해 보면 20초마다 등록되어 있는 로그들을 모두 조회하여 contents 값을 "updateContents"로 모두 수정하고 있습니다.

 

또한 Schedule 등록은 위에서 얘기했던 fixedRate를 사용하여 배치가 20초마다 실행될 수 있도록 설정했습니다.

 

 

 

 

 

 

 

 

이상으로 SpringBatch 사용하기 첫 번째인 Scheduler를 이용하여 Tasklet, Chunk 배치 만드는 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글