๐ŸŒฑSpring Batch (2) - Tasklet ๋ฐฉ์‹, Chunk ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ๊ฐ„๋‹จํ•œ ๊ตฌํ˜„

Dohyeon Kongยท2024๋…„ 7์›” 9์ผ
0

Spring๐ŸŒฑ

๋ชฉ๋ก ๋ณด๊ธฐ
9/11
post-thumbnail

Spring Boot Batch ์ข…๋ฅ˜๊ฐ€ ๋ฌด์—‡์ด ์žˆ์„๊นŒ?๐Ÿง

Step์ด ๊ตฌ์„ฑํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๋‘๊ฐ€์ง€ ๋ฐฉ์‹์ด ์กด์žฌํ•œ๋‹ค.

  • Tasklet ๋ฐฉ์‹
  • Chunk ๋ฐฉ์‹

Tasklet ๋ฐฉ์‹

Tasklet ๋ฐฉ์‹์€ Tasklet์„ ์ด์šฉํ•œ Task ๊ธฐ๋ฐ˜์˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด๋ฉฐ,
Batch์˜ Step ๋‹จ๊ณ„์—์„œ ๋‹จ์ผํ•œ ๋ ˆ์ฝ”๋“œ(row)๋‚˜ ํŒŒ์ผ์„ ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ์˜๋ฏธํ•œ๋‹ค. Tasklet ๋ฐฉ์‹์€ ๊ฐ๊ฐ์˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

  • ์ผ๋ฐ˜์ ์ธ ์ˆ˜ํ–‰ ์ ˆ์ฐจ๊ฐ€ ํŒŒ์ผ์„ ์ฝ๊ณ  ์ฒ˜๋ฆฌํ•œ ๋‹ค์Œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ฐ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ์ด๋Ÿฌํ•œ ๋‹จ์ผ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค.

Chunk ๋ฐฉ์‹

Chunk ๋ฐฉ์‹์€ Chuck์„ ์ด์šฉํ•œ Chunk ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด๋ฉฐ, Batch์˜ Step ๋‹จ๊ณ„์—์„œ ๋‹จ์ผ ํ•œ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฌถ์–ด์„œ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ์˜๋ฏธํ•œ๋‹ค.
Chunk๋ฐฉ์‹์€ ๋ฌถ์ธ ๋ ˆ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉฐ ์‹คํŒจ๋ฅผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

  • Chunk ๋ฐฉ์‹์€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Chunk๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.
  • Parallel Chunk ๋ฐฉ์‹์€ Chunk๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ณ‘๋ ฌ์ ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  • ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋˜๊ณ , ์ค‘๋ณต ๋ ˆ์ฝ”๋“œ ์ฒ˜๋ฆฌ๋‚˜ ์‹คํŒจํ•œ ๋ ˆ์ฝ”๋“œ ์ฒ˜๋ฆฌ ๋“ฑ ์˜ˆ์™ธ ์ƒํ™ฉ์— ๋Œ€ํ•œ ๋Œ€์ฒ˜๊ฐ€ ์šฉ์ดํ•˜๋‹ค. <=>Chunk ๋ฐฉ์‹๊ณผ์˜ ์ฐจ์ด์ 
  • Ex. ํŒŒ์ผ์„ ์ฝ์–ด ๋“ค์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์ด๋‚˜ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—… ๋“ฑ์ด ์กด์žฌํ•œ๋‹ค.

Chunk๋ž€?

Chunk๋ž€ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ •ํ•œ ํฌ๊ธฐ๋กœ ๋‚˜๋ˆˆ ๋ฐ์ดํ„ฐ ์…‹(Data Set)์„ ์˜๋ฏธํ•œ๋‹ค. ์ฆ‰, ๊ฐ ์ปค๋ฐ‹ ์‚ฌ์ด์— ์ฒ˜๋ฆฌ๋  row(item) ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

  • Chunk ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„๊ฒŒ ๋˜๋ฉด ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ  ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์„ฑ๊ณต ์‹œ Chunk๋งŒํผ ์ปค๋ฐ‹, ์‹คํŒจ ์‹œ Chunk๋งŒํผ ๋กค๋ฐฑ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

'๋‹จ์ผํ•œ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค'์˜ ์˜๋ฏธ

  • Chunk ๋ฐฉ์‹์—์„œ๋Š” โ€˜Reader-Processor-Writer ๋ฐฉ์‹โ€™์„ ์ด์šฉํ•œ๋‹ค.
  • ์ด๋Ÿฌํ•œ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์€ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
  • โ€˜Readerโ€™๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ๋“ค์ด๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • โ€˜Processorโ€™๋Š” ์ฝ์–ด ๋“ค์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜๊ฑฐ๋‚˜ ํ•„ํ„ฐ๋งํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • โ€˜Writerโ€™๋Š” ๊ฐ€๊ณต๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

Chunk ๊ธฐ๋ฐ˜์˜ Step์˜ Reader-Processor-Writer ์ฒ˜๋ฆฌ ๋ฐฉ์‹์˜ ํ๋ฆ„

  1. ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜จ๋‹ค. (Item Reader)
  2. ์ฝ์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. (Item Processor)
  3. ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค. (Item Writer)

ItemReader

Cursor์™€ Paging ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

ItemProcessor

  • Chunk-oriented Tasklet์„ ๊ตฌ์„ฑํ•  ๋–„ ์„ ํƒ ์š”์†Œ์ด๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณต/ํ•„ํ„ฐ๋ง ํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰(writer์—์„œ๋„ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•œ ์—ญํ• ์„ ํ•˜๋ฉฐ ๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ๊ฐ€ ์„ž์ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.)ํ•˜๋ฉฐ Step์— ์—ฌ๋Ÿฌ ๋กœ์ง์ด ํ•„์š”ํ•  ๋•Œ ๋„์ž…์„ ๊ณ ๋ คํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ์ฆ๊ฐ€ํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์‹คํŒจํ–ˆ์„ ๋•Œ null์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ writer์— ์ „๋‹ฌ๋˜์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.

ItemWriter

Chunk ๋‹จ์œ„ ๋งŒํผ ํ•œ๋ฒˆ์— ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

Chunk ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹


Tasklet๊ณผ Chunk๋ฅผ ๋น„๊ตํ•˜๊ธฐ๐Ÿคบ

๊ตฌ๋ถ„TaskletChunk
์‹คํ–‰ ์‹œ์ STEP ์‹คํ–‰ ์ค‘STEP ์‹คํ–‰ ์ „
์‹คํ–‰ ๋ฐฉ์‹โ€˜์ž‘์€ ๋‹จ์œ„โ€™๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์‹คํ–‰โ€˜ํ•˜๋‚˜์˜ ํฐ ๋ฉ์–ด๋ฆฌโ€™๋กœ ์‹คํ–‰
์ปค๋ฐ‹ ๋ฐฉ์‹โ€˜Taskletโ€™ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌ ํ›„ ์ปค๋ฐ‹โ€˜Chunkโ€™ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌ ํ›„ ์ปค๋ฐ‹
๋ฐฐ์น˜ ์„ฑ๋Šฅโ€˜์ž‘์€ ๋‹จ์œ„โ€™์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์‹œ ์œ ๋ฆฌ'๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐโ€™ ์ฒ˜๋ฆฌ ์‹œ ์œ ๋ฆฌ
์žฌ์‹œ์ž‘์‹คํŒจํ•œ ํƒœ์Šคํฌ๋ฆฟ๋งŒ ๋‹ค์‹œ ์‹คํ–‰์‹คํŒจ ์‹œ ์ฒญํฌ ์ „์ฒด ๋‹ค์‹œ ์‹คํ–‰
๊ฐ€๋…์„ฑ์ฝ”๋“œ๊ฐ€ ๋ถ„ํ• ๋˜์–ด ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ ธ ๊ฐ€๋…์„ฑ ์ €ํ•˜
์œ ์ง€๋ณด์ˆ˜์ˆ˜์ •์ด ์šฉ์ด์ˆ˜์ •์ด ์–ด๋ ค์›€

Parallel Chunk ๋ฐฉ์‹

Parallel Chunk ๋ฐฉ์‹์ด๋ž€ Chunk ๋ฐฉ์‹์˜ ์ฒ˜๋ฆฌ์—์„œ ๋”์šฑ ๋น ๋ฅธ ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ์œ„ํ•ด Chunk๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ โ€˜Chunk๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌโ€™ํ•œ๋‹ค. ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๋Ÿฌ ๋Œ€์˜ ์„œ๋ฒ„์—์„œ ๋™์‹œ์— ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Parallel Chunk ์ˆ˜ํ–‰ ๊ณผ์ •

  1. ๋ฐ์ดํ„ฐ๋ฅผ ์ ์ ˆํ•œ ํฌ๊ธฐ๋กœ ๋ถ„ํ• ํ•œ๋‹ค.
  2. ๋ถ„ํ• ๋œ ๊ฐ๊ฐ์˜ ๋ถ€๋ถ„์„ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์—…์„ ๋ถ„๋ฐฐํ•œ๋‹ค.
  3. ๊ฐ๊ฐ์˜ ๋ถ€๋ถ„์„ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌํ•œ๋‹ค.
  4. ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ํ•ฉ์นœ๋‹ค.

Remote Chunking ๋ฐฉ์‹

Remote Chunking ๋ฐฉ์‹์ด๋ž€ ์—ฌ๋Ÿฌ ๋Œ€์˜ ์„œ๋ฒ„์—์„œ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๋ฉฐ ์„œ๋ฒ„ ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๊ณ , ๊ฐ๊ฐ์˜ ์„œ๋ฒ„์—์„œ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.


๊ตฌํ˜„ ๋ฐฉ๋ฒ•(tasklet ๊ธฐ๋ฐ˜ ๋ฐฐ์น˜)

1. build.gradle์— ์˜์กด์„ฑ ์ถ”๊ฐ€

//build.gradle
implementation 'org.springframework.boot:spring-boot-starter-batch'
testImplementation 'org.springframework.batch:spring-batch-test'implementation 'org.springframework.boot:spring-boot-starter-batch'

2. BatchConfig ํด๋ž˜์Šค์—์„œ ์›ํ•˜๋Š” Job๊ณผ Step์„ ์ž‘์„ฑํ•œ๋‹ค.

package com.springboot.springbatchtutorial.config;

import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
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 org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@RequiredArgsConstructor  // Lombok ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ final ๋˜๋Š” @NonNull ํ•„๋“œ์— ๋Œ€ํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.
public class BatchConfig extends DefaultBatchConfiguration {
    @Bean
    public Job testJob(JobRepository jobRepository, PlatformTransactionManager transactionManager) throws DuplicateJobException {
        // Job์„ ์ •์˜ํ•˜๊ณ  ์‹œ์ž‘ํ•  Step์„ ์ง€์ •ํ•œ๋‹ค.
        // JobBuilder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ "testJob"์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์žก์„ ๋งŒ๋“ ๋‹ค.
        Job job = new JobBuilder("testJob", jobRepository)
                .start(testStep(jobRepository, transactionManager))
                .build();
        return job;
    }

    public Step testStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        // Step์„ ์ •์˜ํ•˜๊ณ  Tasklet์„ ์ง€์ •ํ•œ๋‹ค.
        // StepBuilder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ "testStep"์ด๋ผ๋Š” ์ด๋ฆ„์˜ ์Šคํ…์„ ๋งŒ๋“ ๋‹ค.
        Step step = new StepBuilder("testStep", jobRepository)
                .tasklet(testTasklet(), transactionManager) // testTasklet์„ tasklet์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ํŠธ๋žœ์žญ์…˜ ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
                .build();
        return step;
    }

    public Tasklet testTasklet() {
        // Tasklet์„ ์ •์˜ํ•˜๊ณ  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž‘์„ฑ
        // Tasklet์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        return ((contribution, chunkContext) -> {
            System.out.println("***** 10์ดˆ๋งˆ๋‹ค 'Hello batch' ์ถœ๋ ฅ!! *****"); // ์ฝ˜์†”์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
            // ์›ํ•˜๋Š” ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง ์ž‘์„ฑ
            return RepeatStatus.FINISHED; // ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
        });
    }
}

3. BatchScheduler ํด๋ž˜์Šค์—์„œ @Schedueld ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ Job์˜ ์‹คํ–‰ ์กฐ๊ฑด ์„ค์ •

package com.springboot.springbatchtutorial.scheduler;

import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.*;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@RequiredArgsConstructor // Lombok ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ final ๋˜๋Š” @NonNull ํ•„๋“œ์— ๋Œ€ํ•œ ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.
@Component // Spring Bean์œผ๋กœ ๋“ฑ๋ก๋จ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
public class BatchScheduler {

    private final JobLauncher jobLauncher;
    private final JobRegistry jobRegistry;

    @Bean
    public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(){
        // JobRegistryBeanPostProcessor ๋นˆ์„ ์ƒ์„ฑํ•œ๋‹ค.
        JobRegistryBeanPostProcessor jobProcessor = new JobRegistryBeanPostProcessor();
        jobProcessor.setJobRegistry(jobRegistry);
        return jobProcessor;
    }

    @Scheduled(cron = "0/10 * * * * *") // 10์ดˆ๋งˆ๋‹ค ์‹คํ–‰, // 10์ดˆ๋งˆ๋‹ค ์Šค์ผ€์ค„๋งํ•˜์—ฌ ์‹คํ–‰๋œ๋‹ค.
    public void runJob() {
        String time = LocalDateTime.now().toString(); // ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
        try {
            Job job = jobRegistry.getJob("testJob"); // jobRegistry์—์„œ "testJob"์„ ๊ฐ€์ ธ์˜จ๋‹ค.
            JobParametersBuilder jobParam = new JobParametersBuilder().addString("time", time); // JobParametersBuilder์— ํ˜„์žฌ ์‹œ๊ฐ„์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
            jobLauncher.run(job, jobParam.toJobParameters()); // Job์„ ์‹คํ–‰ํ•œ๋‹ค.
        } catch (NoSuchJobException e) {
            throw new RuntimeException(e); // Job์„ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
        } catch (JobInstanceAlreadyCompleteException |
                 JobExecutionAlreadyRunningException |
                 JobParametersInvalidException |
                 JobRestartException e
        ) {
            throw new RuntimeException(e); // ์—ฌ๋Ÿฌ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.
        }
    }
}

4. Application ํด๋ž˜์Šค ๋‚ด @EnableScheduling ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€ํ•˜๊ธฐ

package com.springboot.springbatchtutorial;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringBatchTutorialApplication {

	public static void main(String[] args) {
		SpringApplication.run(
				SpringBatchTutorialApplication.class, args);
	}

}

application.properties์— ์„ค์ • ์ฝ”๋“œ ์ถ”๊ฐ€

# Spring Batch ์ž‘์—… ์ž๋™ ์‹คํ–‰ ๋น„ํ™œ์„ฑํ™”
spring.batch.job.enabled=false
# Spring Batch๊ฐ€ ์‹œ์ž‘๋  ๋•Œ ์Šคํ‚ค๋งˆ๋ฅผ ํ•ญ์ƒ ์ดˆ๊ธฐํ™” (ํ•„์š”์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ)
spring.batch.jdbc.initialize-schema=always
# ๋นˆ ์ •์˜ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ํ—ˆ์šฉ (๋™์ผํ•œ ์ด๋ฆ„์˜ ๋นˆ์„ ๋‹ค๋ฅธ ์„ค์ •์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Œ)
spring.main.allow-bean-definition-overriding=true

์ „์ฒด์ ์ธ ์‹คํ–‰ ํ๋ฆ„๐Ÿ„๐Ÿปโ€โ™‚๏ธ

  1. SpringBatchTutorialApplication ํด๋ž˜์Šค๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•œ๋‹ค.
  2. BatchConfig ํด๋ž˜์Šค์—์„œ testJob๊ณผ testStep์ด ์ •์˜๋œ๋‹ค.
  3. BatchScheduler ํด๋ž˜์Šค์—์„œ ๋งค 10์ดˆ๋งˆ๋‹ค testJob์ด ์‹คํ–‰๋˜๋„๋ก ์Šค์ผ€์ค„๋ง๋œ๋‹ค.
  4. testJob์ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค testTasklet์ด ํ˜ธ์ถœ๋˜์–ด "10์ดˆ๋งˆ๋‹ค 'Hello batch' ์ถœ๋ ฅ!!" ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

์ „์ฒด ํŒŒ์ผ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

์‹ค์ œ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๐Ÿ”ซ


๊ทธ๋Ÿผ ์–ด๋””์— Spring Batch๊ฐ€ ์‚ฌ์šฉ์ด ๋ ๊นŒ?๐Ÿง

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์˜ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ „์†ก์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํŒŒ์ผ๋กœ, ๋˜๋Š” ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ์ž‘์—…์— ์‚ฌ์šฉ๋œ๋‹ค.
    ์˜ˆ์‹œ: A๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ B๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ๋ชจ๋“  ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์ „ํ•˜๋Š” ์ž‘์—….

  • ๋‹ค์–‘ํ•œ ์†Œ์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœ(Extract)ํ•˜๊ณ , ํ•„์š”ํ•œ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜(Transform)ํ•œ ํ›„, ์ตœ์ข…์ ์œผ๋กœ ํƒ€๊ฒŸ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ํŒŒ์ผ์— ์ ์žฌ(Load)ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
    ์˜ˆ์‹œ: ์—ฌ๋Ÿฌ CSV ํŒŒ์ผ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๊ณ , ํŠน์ • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•œ ํ›„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์žฌํ•˜๋Š” ์ž‘์—….

  • ์ •๊ธฐ์ ์œผ๋กœ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…์— ์‚ฌ์šฉ๋œ๋‹ค.
    ์˜ˆ์‹œ: ๋งค์›” ๋งˆ์ง€๋ง‰ ๋‚ ์— ์ง€๋‚œ ๋‹ฌ์˜ ๋งค์ถœ ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘๊ณ„ํ•˜์—ฌ PDF ํ˜•์‹์˜ ๋ณด๊ณ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—….

  • ๋กœ๊ทธ ๋ฐ์ดํ„ฐ, ํŠธ๋žœ์žญ์…˜ ๋ฐ์ดํ„ฐ ๋“ฑ์˜ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ์ง‘๊ณ„ํ•˜๋Š” ์ž‘์—…์— ์‚ฌ์šฉ๋œ๋‹ค.
    ์˜ˆ์‹œ: ํ•˜๋ฃจ ๋™์•ˆ์˜ ์›น ์„œ๋ฒ„ ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ๋ฐฉ๋ฌธ์ž ์ˆ˜, ํŽ˜์ด์ง€ ๋ทฐ ์ˆ˜ ๋“ฑ์„ ์ง‘๊ณ„ํ•˜๋Š” ์ž‘์—….

  • ์ •๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ฐฑ์—…ํ•˜๊ฑฐ๋‚˜ ๋ณต์›ํ•˜๋Š” ์ž‘์—…์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ์‹œ: ๋งค์ผ ๋ฐค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋ฐฑ์—…ํ•˜๊ณ , ๋ฐฑ์—… ํŒŒ์ผ์„ ํŠน์ • ์œ„์น˜์— ์ €์žฅํ•˜๋Š” ์ž‘์—….

  • ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ(์˜ˆ: CSV, XML, JSON ๋“ฑ)์„ ์ฝ๊ณ , ์ฒ˜๋ฆฌํ•œ ํ›„ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” ์ž‘์—…์— ์‚ฌ์šฉ๋œ๋‹ค.
    ์˜ˆ์‹œ: ์ˆ˜๋ฐฑ๋งŒ ๊ฑด์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ CSV ํŒŒ์ผ์„ ์ฝ์–ด ๊ฐ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์ž‘์—….

  • ์ •๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ๊ฒ€์ฆํ•˜๊ณ , ์ด์ƒ์ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ„๋„๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ์‹œ: ๋งค์ฃผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ๊ฒ€์ฆํ•˜๊ณ , ๋ˆ„๋ฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ ์„œ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๊ด€๋ฆฌ์ž์—๊ฒŒ ์•Œ๋ฆฌ๋Š” ์ž‘์—….

  • ํŠน์ • ์ด๋ฒคํŠธ๋‚˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋Œ€๋Ÿ‰์˜ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•˜๋Š” ์ž‘์—…์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ์‹œ: ํŠน์ • ํ”„๋กœ๋ชจ์…˜ ๊ธฐ๊ฐ„์— ์ด๋ฒคํŠธ์— ์ฐธ์—ฌํ•œ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•˜๋Š” ์ž‘์—….

  • ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ์ ์šฉํ•˜์—ฌ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์˜ˆ์‹œ: ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๊ณ ๊ฐ ์ดํƒˆ์„ ์˜ˆ์ธกํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์ž‘์—….

์ด์™€ ๊ฐ™์ด Spring Boot Batch๋ฅผ ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ฐ˜๋ณต์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ด์•ผํ•˜๋Š” ์ž‘์—…์ด๋‚˜ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ž‘์—…์„ ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ž๋™ํ™”ํ•˜๊ธฐ ํŽธํ•  ๊ฒƒ์ด๋‹ค.


Spring Boot 2 vs Spring Boot 3 ์ฃผ์˜ํ•ด์•ผ ํ•  ์ 

  • ์ด์ „ ๋ฒ„์ „๊นŒ์ง€ @EnableBatchProcessing ๋ฅผ ํ†ตํ•ด ๋ฐฐ์น˜ ์‹คํ–‰์— ํ•„์š”ํ•œ Bean๋“ค์ด AutoConfiguration ์— ์˜ํ•ด ๋“ฑ๋ก์ด ๋˜์—ˆ๋‹ค๋ฉด, ์ด์ œ๋Š” ์•„๋ฌด๋Ÿฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค.
    ์˜คํžˆ๋ ค ์žˆ์œผ๋ฉด ์ด์ „๊ณผ ๊ฐ™์ด ๋ฐฐ์น˜ ํ”Œ๋กœ์šฐ๊ฐ€ ๋™์ž‘์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. SpringBoot ๋ฅผ ์ด์šฉํ•˜๊ณ  ์žˆ๊ณ , ์ด์ „์— ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ BatchRunner ๋ฅผ ์ƒ์„ฑํ–ˆ์—ˆ๋‹ค๋ฉด, @EnableBatchProcessing ์„ ๋นผ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • Ordering ์„ ํ†ตํ•ด, Configuration ์„ ๊ฐ๊ฐ ๊ตฌ์„ฑํ•˜์—ฌ ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ Multiple Job ์„ ๊ตฌ์„ฑํ•œ ์ผ€์ด์Šค๋„ ์žˆ์—ˆ๋Š”๋ฐ, ์ด์ œ๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. (1 Application 1 Job ์ „๋žต)

  • JobBuilderFactory, StepBuilderFactory ๋Š” ์ด์ œ Deprecated ๋˜์—ˆ๋‹ค.
    (TO BE) ์ด์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด JobBuilder, StepBuilder ๋ฅผ ์ด์šฉํ•˜์—ฌ Job, Step ์„ ๊ตฌ์„ฑํ•ด์•ผํ•œ๋‹ค.

  • Step์˜ chunk, tasklet ์—์„œ transactionManager ๋ฅผ ์ธ์ž๋กœ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ ๊ธฐ์žฌํ•œ ๊ฒƒ ์ด์™ธ์—๋„ Spring Boot 3๋กœ ๋„˜์–ด์˜ค๋ฉด์„œ Spring Boot Batch์— ๋งŽ์€ ๋ณ€ํ™”๋“ค์ด ์ƒ๊ฒผ๋‹ค. ๋งŒ์•ฝ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ตฌ๋™ํ•  ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์ฐพ์•„๋ณด๋Š”๊ฑธ ์ถ”์ฒœํ•œ๋‹ค.๐Ÿ˜ซ


์ฐธ๊ณ  ๋ฌธํ—Œ

profile
์ฒœ์ฒœํžˆ, ๊พธ์ค€ํžˆ, ๊ทธ๋ฆฌ๊ณ  ๋๊นŒ์ง€

0๊ฐœ์˜ ๋Œ“๊ธ€