๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BackEnd๐ŸŒฑ/Spring

Spring Batch๋ž€? ๊ฐ„๋‹จํ•œ ๊ฐœ๋…๊ณผ ์ฝ”๋“œ ์‚ดํŽด๋ณด๊ธฐ

by ์•ˆ์ฃผํ˜• 2023. 7. 29.

์„œ๋ก 

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

  • ๋กœ๊น… ๋ฐ ์ถ”์ 
  • ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ
  • ์ž‘์—… ์ฒ˜๋ฆฌ ํ†ต๊ณ„
  • ์ž‘์—… ์žฌ์‹œ์ž‘
  • ๊ฑด๋„ˆ๋›ฐ๊ธฐ
  • ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ

 

Batch์™€ Scheduler์˜ ์ฐจ์ด

๋ฐฐ์น˜(Batch)๋Š” ๋…ผ๋ฆฌ์  ๋˜๋Š” ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๊ด€๋ จ๋œ ์ผ๋ จ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ์ผ๊ด„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ์Šค์ผ€์ค„๋Ÿฌ(Scheduler)๋Š” ์ฃผ์–ด์ง„ ์ž‘์—…์„ ๋ฏธ๋ฆฌ ์ •์˜๋œ ์‹œ๊ฐ„์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋‚˜ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ ๋ฐฐ์น˜๋Š” ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๊ด„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋ฟ, ํŠน์ • ์ฃผ๊ธฐ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ์Šค์ผ€์ค„๋ง๊ณผ๋Š” ๊ด€๋ จ์ด ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Spring Batch๋Š” ์Šค์ผ€์ค„๋Ÿฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์„ ๋ฟ์ด์ง€ ์Šค์ผ€์ค„๋Ÿฌ ์ž์ฒด๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. (= ๋ฐฐ์น˜๋Š” ๋Œ€๋Ÿ‰ ์ฒ˜๋ฆฌ๋ฅผ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ด์ง€, ์ผ์ • ์ฃผ๊ธฐ๋งˆ๋‹ค ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜์ง€ ์•Š๋Š”๋‹ค.) ๋”ฐ๋ผ์„œ ์ž‘์—… ์Šค์ผ€์ค„๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Quartz ๋“ฑ๊ณผ Spring Batch๋ฅผ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์€ ์ ์ ˆํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. (https://yeonyeon.tistory.com/310)
 

Batch ์‚ฌ์šฉ ์‚ฌ๋ก€

์ผ๋งค์ถœ ์ง‘๊ณ„

์ปค๋จธ์Šค ์‚ฌ์ดํŠธ์—์„œ๋Š” ํ•˜๋ฃจ์— ๊ฑฐ๋ž˜๊ฑด์ด 50๋งŒ์—์„œ 100๋งŒ ๊ฑด๊นŒ์ง€ ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋Š” ์ตœ์†Œ 100๋งŒ์—์„œ 200๋งŒ ํ–‰ ์ด์ƒ์ด ๋  ์ˆ˜ ์žˆ๋Š”๋ฐ, ํ•œ ๋‹ฌ ๋™์•ˆ์€ ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๊ฐ€ 5000๋งŒ์—์„œ 1์–ต๊นŒ์ง€ ์Œ“์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง‘๊ณ„ํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์กฐํšŒ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๊ณ  ์„œ๋ฒ„์— ๋งŽ์€ ๋ถ€๋‹ด์ด ๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งค์ผ ์ƒˆ๋ฒฝ์— ์ „๋‚ ์˜ ๋งค์ถœ ์ง‘๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด ๋‘์–ด, ์™ธ๋ถ€์—์„œ ์š”์ฒญ์ด ์˜ค๋ฉด ์ „๋‚ ์— ์ง‘๊ณ„ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. 

https://jojoldu.tistory.com/324?category=635883

๊ตฌ๋… ์„œ๋น„์Šค 

์ •ํ•ด์ง„ ์‹œ๊ฐ„์— ๊ตฌ๋…์ž๋“ค์—๊ฒŒ ๋ฉ”์ผ์„ ์ผ๊ด„ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์„œ๋น„์Šค ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค. ์ „์†กํ•  ๋ฐ์ดํ„ฐ ๋‚ด์—ญ๊ณผ ๊ตฌ๋…์ž ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ตฌ๋…์„ ์‹ ์ฒญํ•œ ํšŒ์›๋“ค์—๊ฒŒ ๊ทœ์น™์ ์œผ๋กœ ๋ฉ”์ผ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ๋ฐฑ์—…

๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์šด์˜ํ•˜๊ฒŒ ๋˜๋ฉด, ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ฃผ๊ธฐ์ ์ธ ๋ฐ์ดํ„ฐ ๋ฐฑ์—…์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฐ ๋ฐฑ์—… ์ž‘์—…์€ ์‹œ์Šคํ…œ์— ์ƒ๋‹นํ•œ ๋ถ€๋‹ด์„ ์ค„ ์ˆ˜ ์žˆ๊ธฐ์— ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ์ž ํŠธ๋ž˜ํ”ฝ์ด ์ƒ๋Œ€์ ์œผ๋กœ ์ ์€ ์‹œ๊ฐ„๋Œ€์— ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ํ†ตํ•ด ๋ฐฑ์—… ์ž‘์—…์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

Spring Batch์˜ ์šฉ์–ด

์Šคํ”„๋ง ๋ฐฐ์น˜์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ์š” ์šฉ์–ด๋“ค๊ณผ ์˜๋ฏธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Job
    • Job์€ ์ „์ฒด ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๊ณผ์ •์„ ์ถ”์ƒํ™”ํ•œ ๊ฐœ๋…์œผ๋กœ, ํ•˜๋‚˜ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ Step์„ ํฌํ•จํ•˜๋ฉฐ, ์Šคํ”„๋ง ๋ฐฐ์น˜ ๊ณ„์ธต์—์„œ ๊ฐ€์žฅ ์ƒ์œ„์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฐ Job์€ ๊ณ ์œ ํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง€๋ฉฐ, ์ด ์ด๋ฆ„์€ ์‹คํ–‰์— ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ํ•จ๊ป˜ JobInstance๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • JobInstance
    • JobInstance๋Š” ํŠน์ • Job์˜ ์‹ค์ œ ์‹คํ–‰ ์ธ์Šคํ„ด์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, "๋งค์ผ ์•„์นจ 8์‹œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌ"ํ•˜๋Š” Job์„ ๊ตฌ์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, 1์›” 1์ผ, 1์›” 2์ผ ๋“ฑ ๋งค์ผ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด JobInstance๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    • ํ•œ๋ฒˆ ์ƒ์„ฑ๋œ JobInstance๋Š” ํ•ด๋‹น ๋‚ ์งœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋ฉฐ, ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ ๊ฐ™์€ JobInstance๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์—ฌ ์ž‘์—…์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • JobParameters
    • JobParameters๋Š” JobInstance๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ตฌ๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์ž…๋‹ˆ๋‹ค.
    • Job์ด ์‹คํ–‰๋  ๋•Œ ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ณตํ•˜๋ฉฐ, JobInstance๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ์—ญํ• ๋„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์Šคํ”„๋ง ๋ฐฐ์น˜๋Š” String, Double, Long, Date ์ด๋ ‡๊ฒŒ 4๊ฐ€์ง€ ํƒ€์ž…์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • JobExecution
    • JobExecution์€ JobInstance์˜ ํ•œ ๋ฒˆ์˜ ์‹œํ–‰ ์‹œ๋„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, 1์›” 1์ผ์— ์‹คํ–‰๋œ JobInstance๊ฐ€ ์‹คํŒจํ–ˆ์„ ๋•Œ ์žฌ์‹œ๋„ํ•˜๋ฉด, ๊ฐ™์€ JobInstance์— ๋Œ€ํ•œ ์ƒˆ๋กœ์šด JobExcecution์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    • JobExecution์€ ์‹คํ–‰ ์ƒํƒœ, ์‹œ์ž‘์‹œ๊ฐ„, ์ข…๋ฃŒ์‹œ๊ฐ„, ์ƒ์„ฑ์‹œ๊ฐ„ ๋“ฑ JobInstance์˜ ์‹คํ–‰์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • Step
    • Step์€ Job์˜ ํ•˜์œ„ ๋‹จ๊ณ„๋กœ์„œ ์‹ค์ œ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์ž‘์—…์ด ์ด๋ฃจ์–ด์ง€๋Š” ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.
    • ํ•œ ๊ฐœ ์ด์ƒ์˜ Step์œผ๋กœ Job์ด ๊ตฌ์„ฑ๋˜๋ฉฐ, ๊ฐ Step์€ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
    • ๊ฐ Step ๋‚ด๋ถ€์—์„œ๋Š” ItemReader, ItemProcessor, ItemWriter๋ฅผ ์‚ฌ์šฉํ•˜๋Š” chunk ๋ฐฉ์‹ ๋˜๋Š” Tasklet ํ•˜๋‚˜๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • StepExecution
    • StepExecution์€ Step์˜ ํ•œ ๋ฒˆ์˜ ์‹คํ–‰์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, Step์˜ ์‹คํ–‰ ์ƒํƒœ, ์‹คํ–‰ ์‹œ๊ฐ„ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
    • JobExecution๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ, ๊ฐ Step์˜ ์‹คํ–‰ ์‹œ๋„๋งˆ๋‹ค ์ƒˆ๋กœ์šด StepExecution์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
    • ๋˜ํ•œ, ์ฝ์€ ์•„์ดํ…œ์˜ ์ˆ˜, ์“ด ์•„์ดํ…œ์˜ ์ˆ˜, ์ปค๋ฐ‹ ํšŸ์ˆ˜, ์Šคํ‚ตํ•œ ์•„์ดํ…œ์˜ ์ˆ˜ ๋“ฑ์˜ Step ์‹คํ–‰์— ๋Œ€ํ•œ ์ƒ์„ธ ์ •๋ณด๋„ ํฌํ•ฉ ํ•ฉ๋‹ˆ๋‹ค.
  • ExecutionContext
    • ExecutionContext๋Š” Step ๊ฐ„ ๋˜๋Š” Job ์‹คํ–‰ ๋„์ค‘ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์ €์žฅ์†Œ์ž…๋‹ˆ๋‹ค.
    • JobExecutionContext์™€ StepExecutionContext ๋‘ ์ข…๋ฅ˜๊ฐ€ ์žˆ์œผ๋ฉฐ, ๋ฒ”์œ„์™€ ์ €์žฅ ์‹œ์ ์— ๋”ฐ๋ผ ์ ์ ˆํ•˜๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • Job์ด๋‚˜ Step์ด ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ, ExecutionContext๋ฅผ ํ†ตํ•ด ๋งˆ์ง€๋ง‰ ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์žฌ์‹œ๋„ ๋˜๋Š” ๋ณต๊ตฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • JobRepository
    • JobRepository๋Š” ๋ฐฐ์น˜ ์ž‘์—…์— ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค.
    • Job ์‹คํ–‰์ •๋ณด(JobExecution), Step ์‹คํ–‰์ •๋ณด(StepExecution), Job ํŒŒ๋ผ๋ฏธํ„ฐ(JobParameters)๋“ฑ์„ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • Job์ด ์‹คํ–‰๋  ๋•Œ, JobRepository๋Š” ์ƒˆ๋กœ์šด JobExecution๊ณผ StepExecution์„ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
  • JobLauncher
    • JobLauncher๋Š” Job๊ณผ JobParameters๋ฅผ ๋ฐ›์•„ Job์„ ์‹คํ–‰ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” ์ „๋ฐ˜์ ์ธ Job์˜ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ, JobRepository๋ฅผ ํ†ตํ•ด ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ItemReader
    • ItemReader๋Š” ๋ฐฐ์น˜ ์ž‘์—…์—์„œ ์ฒ˜๋ฆฌํ•  ์•„์ดํ…œ์„ ์ฝ์–ด์˜ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์—ฌ๋Ÿฌ ํ˜•์‹์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค(์˜ˆ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํŒŒ์ผ, ๋ฉ”์‹œ์ง€ ํ ๋“ฑ)๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๋Š” ๋‹ค์–‘ํ•œ ItemReader ๊ตฌํ˜„์ฒด๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.
  • ItemProcessor
    • ItemProcessor๋Š” ItemReader๋กœ๋ถ€ํ„ฐ ์ฝ์–ด์˜จ ์•„์ดํ…œ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” ์„ ํƒ์ ์ธ ๋ถ€๋ถ„์œผ๋กœ์„œ, ํ•„์š”์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜ ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ItemWriter
    • ItemWriter๋Š” ItemProcessor์—์„œ ์ฒ˜๋ฆฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์ข…์ ์œผ๋กœ ๊ธฐ๋กํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
    • ItemWriter ์—ญ์‹œ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์˜ ๊ตฌํ˜„์ฒด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ธฐ๋กํ•˜๊ฑฐ๋‚˜, ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Tasklet
    • Tasklet์€ ๊ฐ„๋‹จํ•œ ๋‹จ์ผ ์ž‘์—…, ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฆฌ์†Œ์Šค์˜ ์ •๋ฆฌ ๋˜๋Š” ์‹œ์Šคํ…œ ์ƒํƒœ์˜ ์ฒดํฌ ๋“ฑ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ์ด๋Š” ์Šคํ”„๋ง ๋ฐฐ์น˜์˜ Step ๋‚ด์—์„œ ๋‹จ์ผ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ, ์ผ๋ฐ˜์ ์œผ๋กœ ItemReader, ItemProcessor, ItemWriter์˜ ๋ฌถ์Œ์„ ๊ฐ€์ง€๋Š” Chunk ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹๊ณผ๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค.
    • Tasklet์˜ execute ๋ฉ”์„œ๋“œ๋Š” Step์˜ ๋ชจ๋“  ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ณ„์† ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
  • JobOperator
    • JobOperator๋Š” ์™ธ๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ, Job์˜ ์‹คํ–‰๊ณผ ์ค‘์ง€, ์žฌ์‹œ์ž‘ ๋“ฑ์˜ ๋ฐฐ์น˜ ์ž‘์—… ํ๋ฆ„์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
    • ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด JobLauncher์™€ JobRepository์— ๋Œ€ํ•œ ์ง์ ‘์ ์ธ ์ ‘๊ทผ ์—†์ด๋„ ๋ฐฐ์น˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ƒํƒœ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • JobExplorer
    • JobExplorer๋Š” Job์˜ ์‹คํ–‰ ์ด๋ ฅ์„ ์กฐํšŒํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • JobRepository์—์„œ ์ œ๊ณตํ•˜๋Š” ์ •๋ณด์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, JobRepository๋Š” ์ฃผ๋กœ Job์˜ ์‹คํ–‰ ๋„์ค‘์ธ ์ƒํƒœ์— ๋Œ€ํ•ด ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ˜๋ฉด, JobExplorer๋Š” ์ฃผ๋กœ ์ฝ๊ธฐ ์ „์šฉ ์ ‘๊ทผ์— ์ดˆ์ ์„ ๋งž์ถ”๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋ฉ”ํƒ€ ํ…Œ์ด๋ธ”

๋จผ์ € ์Šคํ”„๋ง ๋ฐฐ์น˜๋Š” ๋ฐฐ์น˜ ์ž‘์—…์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์•„๋ž˜ 6๊ฐœ์˜ ํ…Œ์ด๋ธ”๋“ค์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ฐฐ์น˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ํ…Œ์ด๋ธ”๋“ค์˜ ์ปฌ๋Ÿผ ๊ฐ’๋“ค์ด ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.

https://docs.spring.io/spring-batch/docs/current/reference/html/schema-appendix.html

1. BATCH_JOB_INSTANCE

CREATE TABLE BATCH_JOB_INSTANCE  (
  JOB_INSTANCE_ID BIGINT  PRIMARY KEY ,
  VERSION BIGINT,
  JOB_NAME VARCHAR(100) NOT NULL ,
  JOB_KEY VARCHAR(32) NOT NULL
);

BATCH_JOB_INSTANCE ํ…Œ์ด๋ธ”์€ JobInstance์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๋ฅผ ๊ฐ€์ง€๋ฉฐ, ์ „์ฒด ๊ณ„์ธต ๊ตฌ์กฐ์˜ ์ตœ์ƒ์œ„ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  • JOB_INSTANCE_ID: ์‹คํ–‰๋œ JobInstance์˜ ID
  • VERSION: ๋ฐฐ์น˜ ํ…Œ์ด๋ธ”์˜ ๋‚™๊ด€์  ๋ฝ ์ „๋žต์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’
  • JOB_NAME: ์‹คํ–‰๋œ Job์˜ ์ด๋ฆ„์œผ๋กœ, null์ด ์•„๋‹ˆ ์—ฌ์•ผํ•จ
  • JOB_KEY: JobParameter๋กœ ์ƒ์„ฑ๋œ JobInstance์˜ ํ‚ค(Job ์ค‘๋ณต ์ˆ˜ํ–‰ ์ฒดํฌ๋ฅผ ์œ„ํ•œ ๊ณ ์œ ํ‚ค)

2. BATCH_JOB_EXECUTION

CREATE TABLE BATCH_JOB_EXECUTION  (
  JOB_EXECUTION_ID BIGINT  PRIMARY KEY ,
  VERSION BIGINT,
  JOB_INSTANCE_ID BIGINT NOT NULL,
  CREATE_TIME TIMESTAMP NOT NULL,
  START_TIME TIMESTAMP DEFAULT NULL,
  END_TIME TIMESTAMP DEFAULT NULL,
  STATUS VARCHAR(10),
  EXIT_CODE VARCHAR(20),
  EXIT_MESSAGE VARCHAR(2500),
  LAST_UPDATED TIMESTAMP,
  constraint JOB_INSTANCE_EXECUTION_FK foreign key (JOB_INSTANCE_ID)
  references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ;

BATCH_JOB_EXECUTION ํ…Œ์ด๋ธ”์€ JobExecution์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค. JobExcution์€ JobInstance๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ์‹œ์ž‘์‹œ๊ฐ„, ์ข…๋ฃŒ์‹œ๊ฐ„, ์ข…๋ฃŒ์ฝ”๋“œ ๋“ฑ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • JOB_EXECUTION_ID: ์‹คํ–‰๋œ JobExecution์˜ ID
  • VERSION: ๋ฐฐ์น˜ ํ…Œ์ด๋ธ”์˜ ๋‚™๊ด€์  ๋ฝ ์ „๋žต์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’
  • JOB_INSTANCE_ID: BATCH_JOB_INSTANCE ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ ํ‚ค
  • CREATE_TIME: JobExecution์ด ์ƒ์„ฑ๋œ ์‹œ๊ฐ„
  • START_TIME: JobExecution์ด ์‹คํ–‰๋œ ์‹œ๊ฐ„
  • END_TIME: JobExecution์ด ์ข…๋ฃŒ๋œ ์‹œ๊ฐ„(์„ฑ๊ณต๊ณผ ์‹คํŒจ ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ์‹œ๊ฐ„์„ ์˜๋ฏธ)
  • STATUS: JobExecution์˜ ์ƒํƒœ(BatchStatus์˜ Enum ํƒ€์ž…)
  • EXIT_CODE: JobExecution์˜ ์ข…๋ฃŒ ์ฝ”๋“œ
  • EXIT_MESSAGE: JobExecution์˜ ์ข…๋ฃŒ ๋ฉ”์‹œ์ง€. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ์—๋Ÿฌ๋ฉ”์‹œ์ง€
  • LAST_UPDATED: JobExcution์ด ์ˆ˜์ •๋œ ์‹œ๊ฐ„

3. BATCH_JOB_EXECUTION_PARAMS

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)
);

BATCH_JOB_EXECUTION_PARAMS ํ…Œ์ด๋ธ”์€ JobParameters์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค.

  • JOB_EXECUTION_ID: ์‹คํ–‰๋œ JobExecution์˜ ID(BATCH_JOB_EXECUTION ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ ํ‚ค)
  • TYPE_CD: JobPameter์˜ ํƒ€์ž… ์ฝ”๋“œ(string, date, long, double)
  • KEY_NAME: JobPameter์˜ ์ด๋ฆ„
  • STRING_VAL: JobPameter์˜ String๊ฐ’(TYPE_CD๊ฐ€ String์ผ ๋•Œ ๊ฐ’์ด ์กด์žฌ)
  • DATETIME: JobPameter์˜ Date๊ฐ’(TYPE_CD๊ฐ€ Date ์ผ ๋•Œ ๊ฐ’์ด ์กด์žฌ)
  • LONG_VAL: JobPameter์˜ Long๊ฐ’. (TYPE_CD๊ฐ€ Long์ผ ๋•Œ ๊ฐ’์ด ์กด์žฌ)
  • DOUBLE_VAL: JobPameter์˜ Double๊ฐ’. (TYPE_CD๊ฐ€ Double์ผ ๋•Œ ๊ฐ’์ด ์กด์žฌ)
  • IDENTIFYING: JobInstance์˜ ํ‚ค์˜ ์ƒ์„ฑ์— ํฌํ•จ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€

4. BATCH_JOB_EXECUTION_CONTEXT

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

BATCH_JOB_EXECUTION_CONTEXT ํ…Œ์ด๋ธ”์€ ์ž‘์—…์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ JobExecution๋งˆ๋‹ค ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ JobExecutionContext๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ExecutionContext ๋ฐ์ดํ„ฐ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ JobInstance๊ฐ€ ์‹คํŒจ ์‹œ ์ค‘๋‹จ๋œ ์œ„์น˜์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • JOB_EXECUTION_ID: ์‹คํ–‰๋œ JobExecution์˜ ID
  • SHORT_CONTEXT: ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋œ JobExecutionContext ์ •๋ณด
  • SERIALIZED_CONTEXT: ์ง๋ ฌํ™”ํ•˜์—ฌ ์ €์žฅ๋œ JobExecutionContext ์ •๋ณด

5. BATCH_STEP_EXECUTION

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 TIMESTAMP NOT NULL,
  START_TIME TIMESTAMP DEFAULT NULL ,
  END_TIME TIMESTAMP 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(20) ,
  EXIT_MESSAGE VARCHAR(2500) ,
  LAST_UPDATED TIMESTAMP,
  constraint JOB_EXECUTION_STEP_FK foreign key (JOB_EXECUTION_ID)
  references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ;

BATCH_STEP_EXECUTION ํ…Œ์ด๋ธ”์€ StepExecution ๊ฐ์ฒด์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. BATCH_JOB_EXECUTION ํ…Œ์ด๋ธ”๊ณผ ์œ ์‚ฌํ•˜๋ฉฐ, ์ƒ์„ฑ๋œ ๊ฐ JobExecution์— ๋Œ€ํ•ด ํ•ญ์ƒ ๋‹จ๊ณ„๋‹น ํ•˜๋‚˜ ์ด์ƒ์˜ ํ•ญ๋ชฉ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. STEP์˜ EXECUTION ์ •๋ณด์ธ ์ฝ์€ ์ˆ˜, ์ปค๋ฐ‹ ์ˆ˜, ์Šคํ‚ต ์ˆ˜ ๋“ฑ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€๋กœ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • STEP_EXECUTION_ID: ์‹คํ–‰๋œ StepExecution์˜ ID
  • VERSION: ๋ฐฐ์น˜ ํ…Œ์ด๋ธ”์˜ ๋‚™๊ด€์  ๋ฝ ์ „๋žต์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’
  • STEP_NAME: ์‹คํ–‰๋œ StepExecution์˜ Step ์ด๋ฆ„
  • JOB_EXECUTION_ID: ์‹คํ–‰๋œ JobExecution์˜ ID
  • START_TIME: StepExecution์ด ์‹œ์ž‘๋œ ์‹œ๊ฐ„
  • END_TIME: StepExecution์ด ์ข…๋ฃŒ๋œ ์‹œ๊ฐ„(์„ฑ๊ณต๊ณผ ์‹คํŒจ ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ์‹œ๊ฐ„์„ ์˜๋ฏธ)
  • STATUS: StepExecution์˜ ์ƒํƒœ(BatchStatus์˜ Enum ํƒ€์ž…)
  • COMMIT_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ปค๋ฐ‹ํ•œ ํšŸ์ˆ˜
  • READ_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ฝ์€ ๋ฐ์ดํ„ฐ ์ˆ˜
  • FILTER_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ํ•„ํ„ฐ๋ง๋œ ๋ฐ์ดํ„ฐ ์ˆ˜
  • WRITE_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ž‘์„ฑ ๋ฐ ์ปค๋ฐ‹๋œ ๋ฐ์ดํ„ฐ ์ˆ˜
  • READ_SKIP_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ฝ๊ธฐ๋ฅผ ์Šคํ‚ตํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜
  • WRITE_SKIP_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ž‘์„ฑ์„ ์Šคํ‚ตํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜
  • PROCESS_SKIP_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ์ฒ˜๋ฆฌ๋ฅผ ์Šคํ‚ตํ•œ ๋ฐ์ดํ„ฐ ์ˆ˜
  • EXIT_CODE: StepExecution์˜ ์ข…๋ฃŒ ์ฝ”๋“œ
  • ROLLBACK_COUNT: StepExecution ์‹คํ–‰ ์ค‘, ๋กค๋ฐฑ ํšŸ์ˆ˜
  • EXIT_MESSAGE: StepExecution์˜ ์ข…๋ฃŒ ๋ฉ”์‹œ์ง€. ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ์—๋Ÿฌ๋ฉ”์‹œ์ง€
  • LAST_UPDATED: StepExecution์ด ์ˆ˜์ •๋œ ์‹œ๊ฐ„

6. BATCH_STEP_EXECUTION_CONTEXT

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

BATCH_STEP_EXECUTION_CONTEXT ํ…Œ์ด๋ธ”์€ StepExecutionContext์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜๋ฉฐ, ์Šคํ… ์‹คํ–‰๋‹น ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ExecutionContext๊ฐ€ ์žˆ์œผ๋ฉฐ, ํŠน์ • ์Šคํ… ์‹คํ–‰์„ ์œ„ํ•ด ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ExecutionContext ๋ฐ์ดํ„ฐ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ JobInstance๊ฐ€ ์‹คํŒจ ์‹œ ์ค‘๋‹จ๋œ ์œ„์น˜์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • STEP_EXECUTION_ID: ์‹คํ–‰๋œ StepExecution์˜ ID
  • SHORT_CONTEXT: ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋œ StepExecutionContext ์ •๋ณด
  • SERIALIZED_CONTEXT: ์ง๋ ฌํ™”ํ•˜์—ฌ ์ €์žฅ๋œ StepExecutionContext ์ •๋ณด

๊ฐ ๋ฉ”ํƒ€ ํ…Œ์ด๋ธ”๋“ค์— ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด๋ณด๊ณ  ์‹ถ์œผ์‹  ๋ถ„์€ https://jojoldu.tistory.com/326๋ฅผ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
 

Spring Batch ์‚ฌ์šฉํ•˜๊ธฐ

Spring Batch์—์„œ ๊ฐ Job์€ ์—ฌ๋Ÿฌ Step์˜ ๋ชจ์Œ์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ๊ฐ Step์€ ํŠน์ •ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ ๋‹จ์œ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

1. ๋‹จ์ผ Step ๊ตฌ์„ฑํ•˜๊ธฐ

@Slf4j
@Configuration
@EnableBatchProcessing
public class SimpleJobConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Autowired
    public SimpleJobConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job exampleJob() {
        return jobBuilderFactory.get("exampleJob")
                .start(step())
                .build();
    }

    @Bean
    public Step step() {
        return stepBuilderFactory.get("step")
                .tasklet((contribution, chunkContext) -> {
                    log.info("Performing step!");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” JobBuilderFactory์™€ StepBuilderFactory๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Job๊ณผ Step์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. exampleJob์ด๋ผ๋Š” Job์€ ๋‹จ์ผ step๋งŒ์„ ํฌํ•จํ•˜๋ฉฐ, ์ด step์—์„œ๋Š” tasklet์„ ํ†ตํ•ด ํŠน์ •ํ•œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  RepeatStatus.FINISHED๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

2. ๋‹ค์ค‘ Step ๊ตฌ์„ฑํ•˜๊ธฐ

Sequential Flow

์œ„ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ Step์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Slf4j
@Configuration
@EnableBatchProcessing
public class MultiStepJobConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Autowired
    public MultiStepJobConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job exampleJob() {
        return jobBuilderFactory.get("exampleJob")
                .start(startStep())
                .next(nextStep())
                .next(lastStep())
                .build();
    }

    @Bean
    public Step startStep() {
        return stepBuilderFactory.get("startStep")
                .tasklet((contribution, chunkContext) -> {
                    log.info("Start Step!");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step nextStep() {
        return stepBuilderFactory.get("nextStep")
                .tasklet((contribution, chunkContext) -> {
                    log.info("Next Step!");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    @Bean
    public Step lastStep() {
        return stepBuilderFactory.get("lastStep")
                .tasklet((contribution, chunkContext) -> {
                    log.info("Last Step!!");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}

exampleJob์ด๋ผ๋Š” Job์€ startStep, nextStep, lastStep์˜ ์„ธ ๊ฐœ์˜ Step์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. start() ๋ฉ”์„œ๋“œ๋กœ ์ตœ์ดˆ ์‹คํ–‰๋  Step์„ ์„ค์ •ํ•˜๊ณ , next() ๋ฉ”์„œ๋“œ๋กœ ๊ทธ๋‹ค์Œ์— ์ˆ˜ํ–‰๋  Step์„ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

3. Flow๋ฅผ ํ†ตํ•œ Step ๊ตฌ์„ฑํ•˜๊ธฐ

Conditional Flow

์œ„ ์ด๋ฏธ์ง€์ฒ˜๋Ÿผ ์ด์ „ Step์˜ ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Slf4j
@Configuration
@EnableBatchProcessing
public class FlowJobConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Autowired
    public FlowJobConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job exampleJob() {
        // Job ์ƒ์„ฑ
        return jobBuilderFactory.get("exampleJob")
            .start(startStep())
                .on(ExitStatus.FAILED.getExitCode()) // startStep์˜ ExitStatus๊ฐ€ FAILED์ผ ๊ฒฝ์šฐ
                .to(failOverStep()) // failOver Step์„ ์‹คํ–‰ ์‹œํ‚จ๋‹ค.
                .on("*") // failOver Step์˜ ๊ฒฐ๊ณผ์™€ ์ƒ๊ด€์—†์ด
                .to(writeStep()) // write Step์„ ์‹คํ–‰ ์‹œํ‚จ๋‹ค.
                .end() // Flow๋ฅผ ์ข…๋ฃŒ์‹œํ‚จ๋‹ค.
            .from(startStep()) // startStep์ด FAILED๊ฐ€ ์•„๋‹ˆ๊ณ  COMPLETED์ผ ๊ฒฝ์šฐ
                .on(ExitStatus.COMPLETED.getExitCode())
                .to(processStep()) // process Step์„ ์‹คํ–‰ ์‹œํ‚จ๋‹ค
                .on("*") // process Step์˜ ๊ฒฐ๊ณผ์™€ ์ƒ๊ด€์—†์ด
                .to(writeStep()) // write Step์„ ์‹คํ–‰ ์‹œํ‚จ๋‹ค.
                .end() // Flow๋ฅผ ์ข…๋ฃŒ ์‹œํ‚จ๋‹ค.
            .from(startStep()) // startStep์˜ ๊ฒฐ๊ณผ๊ฐ€ FAILED, COMPLETED๊ฐ€ ์•„๋‹Œ ๋ชจ๋“  ๊ฒฝ์šฐ
                .on("*")
                .to(writeStep()) // write Step์„ ์‹คํ–‰์‹œํ‚จ๋‹ค.
                .on("*") // write Step์˜ ๊ฒฐ๊ณผ์™€ ์ƒ๊ด€์—†์ด
                .end() // Flow๋ฅผ ์ข…๋ฃŒ์‹œํ‚จ๋‹ค.
            .end()
            .build();
    }

    @Bean
    public Step startStep() {
        // ์ฒซ๋ฒˆ์งธ Step ์ƒ์„ฑ
        return stepBuilderFactory.get("startStep")
            .tasklet((contribution, chunkContext) -> {
                log.info("Start Step!");

                String result = "COMPLETED";
                // String result = "FAIL";
                // String result = "UNKNOWN";

                // Flow์—์„œ on์€ RepeatStatus๊ฐ€ ์•„๋‹Œ ExitStatus๋ฅผ ๋ฐ”๋ผ๋ณธ๋‹ค.
                if ("COMPLETED".equals(result)) 
                    contribution.setExitStatus(ExitStatus.COMPLETED);
                else if ("FAIL".equals(result))  
                    contribution.setExitStatus(ExitStatus.FAILED);
                else if ("UNKNOWN".equals(result))  
                    contribution.setExitStatus(ExitStatus.UNKNOWN);

                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public Step failOverStep() {
        // ์‹คํŒจ ์‹œ ์ˆ˜ํ–‰ํ•  Step ์ƒ์„ฑ
        return stepBuilderFactory.get("failOverStep")
            .tasklet((contribution, chunkContext) -> {
                log.info("FailOver Step!");
                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public Step processStep() {
        // ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ Step ์ƒ์„ฑ
        return stepBuilderFactory.get("processStep")
            .tasklet((contribution, chunkContext) -> {
                log.info("Process Step!");
                return RepeatStatus.FINISHED;
            })
            .build();
    }

    @Bean
    public Step writeStep() {
        // ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋กํ•˜๊ธฐ ์œ„ํ•œ Step ์ƒ์„ฑ
        return stepBuilderFactory.get("writeStep")
            .tasklet((contribution, chunkContext) -> {
                log.info("Write Step!");
                return RepeatStatus.FINISHED;
            })
            .build();
    }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ Job์„ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • startStep(): ์‹œ์ž‘ Step์„ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ExitStatus๋ฅผ COMPLETED, FAILED, UNKNOWN ์ค‘ ํ•˜๋‚˜๋กœ ์„ค์ •ํ•˜์—ฌ ๋‹ค์Œ Step์˜ ์ˆ˜ํ–‰์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค.
  • failOverStep(): startStep()์˜ ExitStatus๊ฐ€ FAILED์ผ ๊ฒฝ์šฐ ์ˆ˜ํ–‰๋  Step์ž…๋‹ˆ๋‹ค.
  • processStep(): startStep()์˜ ExitStatus๊ฐ€ COMPLETED์ผ ๊ฒฝ์šฐ ์ˆ˜ํ–‰๋  Step์ž…๋‹ˆ๋‹ค.
  • writeStep(): ์œ„์˜ ๋ชจ๋“  ์ƒํ™ฉ ํ›„์— ์ˆ˜ํ–‰๋  Step์ž…๋‹ˆ๋‹ค.

๊ฐ Step์€ Tasklet์„ ํ†ตํ•ด ์‹ค์ œ ์ˆ˜ํ–‰๋  ๋กœ์ง์„ ์ •์˜ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, on() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ExitStatus์— ๋”ฐ๋ผ ์ˆ˜ํ–‰๋  Step์„ ์ง€์ •ํ•˜๊ณ  end()๋ฅผ ํ†ตํ•ด Flow๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. 
 

STEP์„ ๊ตฌ์„ฑํ•˜๋Š” Tasklet๊ณผ Chunk ์ง€ํ–ฅ ์ฒ˜๋ฆฌ

Job์€ Step๋“ค์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, Step์€ ์ฃผ๋กœ Tasklet ๋ฐฉ์‹๊ณผ chunk ๋ฐฉ์‹์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

https://www.codeusingjava.com/boot/batch/hello

* Tasklet ๋ฐฉ์‹

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

Tasklet ๋ฐฉ์‹์˜ ์ž‘์—… ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Tasklet ์ธํ„ฐํŽ˜์ด์Šค์˜ execute() ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์—์„œ ์‹คํ–‰๋œ๋‹ค.
  • execute() ๋ฉ”์„œ๋“œ๋Š” RepeatStatus๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ ์ด๋Š” Tasklet์˜ ์‹คํ–‰ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • RepeatStatus.FINISHED๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด, ํ•ด๋‹น Tasklet์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋œ ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
  • RepeatStatus.CONTINUABLE๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด, Tasklet์ด ๊ณ„์† ์‹คํ–‰๋˜์–ด์•ผ ํ•จ์„ ์˜๋ฏธํ•œ๋‹ค.
@Configuration
public class TaskletStepConfiguration {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Step taskletStep() {
        return stepBuilderFactory.get("taskletStep")
                .tasklet((contribution, chunkContext) -> {
                    // ์—ฌ๊ธฐ์— Tasklet ๋กœ์ง ์ž‘์„ฑ
                    System.out.println("Tasklet step executed");

                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}

Tasklet๋ฐฉ์‹์€ ์ž‘์—…์˜ ๋‹จ์ˆœ์„ฑ๊ณผ ๋ช…ํ™•์„ฑ ๋•Œ๋ฌธ์— ํŠน์ • ์ƒํ™ฉ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Chunk ๋ฐฉ์‹์ด ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

* Chunk ๋ฐฉ์‹

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

Chunk๋ฐฉ์‹์˜ ์ž‘์—… ํ๋ฆ„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ฐ Chunk ์ฒ˜๋ฆฌ๋Š” Reader, Processor, Writer ์„ธ ๋‹จ๊ณ„๋กœ ๊ตฌ์„ฑ๋œ๋‹ค.
  • Reader๋Š” ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์™€์„œ Chunk๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜, ํŒŒ์ผ, ๋˜๋Š” ๋ฉ”์‹œ์ง€ ํ ๋“ฑ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.
  • Processor๋Š” ์ฝ์–ด์˜จ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ํ•„์š”ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด ์ฒ˜๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ ๊ฒ€์ฆ, ํ•„ํ„ฐ๋ง, ๋ณ€ํ™˜ ๋“ฑ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • Writer๋Š” ์ฒ˜๋ฆฌ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ตœ์ข…์ ์œผ๋กœ ์ €์žฅํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Configuration
public class ChunkStepConfiguration {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public ItemReader<String> reader() {
        return new ListItemReader<>(Arrays.asList("Ahn", "Ju", "Hyeong"));
    }

    @Bean
    public ItemProcessor<String, String> processor() {
        return item -> item.toUpperCase();
    }

    @Bean
    public ItemWriter<String> writer() {
        return items -> items.forEach(System.out::println);
    }

    @Bean
    public Step chunkStep() {
        return stepBuilderFactory.get("chunkStep")
                .<String, String>chunk(10)
                .reader(reader())
                .processor(processor())
                .writer(writer())
                .build();
    }
}

์œ„ ์ฝ”๋“œ์—์„œ ItemReader๋Š” "Ahn", "Ju", "Hyeong"์ด๋ผ๋Š” ๋ฌธ์ž์—ด ๋ฆฌ์ŠคํŠธ๋ฅผ ์ฝ์–ด์™€์„œ ItemProcessor์—์„œ ๊ฐ ๋ฌธ์ž์—ด์„ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ItemWriter์—์„œ ๊ฐ ํ•ญ๋ชฉ์„ ์ฝ˜์†”์— ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์ข€ ๋” ์„ธ๋ถ€์ ์ธ JPA ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์˜ ์˜ˆ์‹œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Configuration
@EnableBatchProcessing
public class BookOrderJobConfiguration {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    // Job ์„ค์ •
    @Bean
    @JobScope
    public Job bookOrderJob() throws Exception {
        return jobBuilderFactory.get("bookOrderJob")
                .start(bookOrderStep()) // "bookOrderStep"์ด๋ผ๋Š” ์ด๋ฆ„์˜ Step์„ ์‹œ์ž‘์ ์œผ๋กœ ์„ค์ •
                .build();
    }

    // Step ์„ค์ •
    @Bean
    @StepScope
    public Step bookOrderStep() throws Exception {
        return stepBuilderFactory.get("bookOrderStep")
                .<Book, Order>chunk(10) // ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ๋ฐ์ดํ„ฐ ํ•ญ๋ชฉ์˜ ํฌ๊ธฐ(chunk size)๋ฅผ 10์œผ๋กœ ์„ค์ •
                .reader(bookReader()) // Reader ์„ค์ •
                .processor(bookOrderProcessor()) // Processor ์„ค์ •
                .writer(orderWriter()) // Writer ์„ค์ •
                .build();
    }

    // Reader ์„ค์ • - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ๋ถ€ํ„ฐ ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๋Š” ์—ญํ• 
    @Bean
    @StepScope
    public JpaPagingItemReader<Book> bookReader() throws Exception {
        Map<String, Object> parameterValues = new HashMap<>();
        parameterValues.put("stock", 5); // stock ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ 5๋กœ ์„ค์ •

        return new JpaPagingItemReaderBuilder<Book>()
                .name("JpaPagingItemReader")
                .entityManagerFactory(entityManagerFactory)
                .queryString("SELECT b FROM Book b WHERE b.stock <= :stock ORDER BY b.id ASC") // ์žฌ๊ณ ๊ฐ€ 5 ์ดํ•˜์ธ ์ฑ…์„ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ
                .parameterValues(parameterValues)
                .pageSize(10) // ํŽ˜์ด์ง• ์‚ฌ์ด์ฆˆ๋ฅผ 10์œผ๋กœ ์„ค์ •
                .build();
    }

    // Processor ์„ค์ • - ์ฝ์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• 
    @Bean
    @StepScope
    public ItemProcessor<Book, Order> bookOrderProcessor() {
        return book -> {
            Order order = new Order();
            order.setBook(book);
            order.setQuantity(10);  // Reorder quantity
            return order; // ์žฌ๊ณ ๊ฐ€ ๋ถ€์กฑํ•œ ์ฑ…์— ๋Œ€ํ•ด ์žฌ์ฃผ๋ฌธ์„ ์ƒ์„ฑ
        };
    }

    // Writer ์„ค์ • - ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์—ญํ• 
    @Bean
    @StepScope
    public JpaItemWriter<Order> orderWriter() {
        return new JpaItemWriterBuilder<Order>()
                .entityManagerFactory(entityManagerFactory)
                .build();
    }
}

์œ„ ์ฝ”๋“œ๋Š” ์žฌ๊ณ ๊ฐ€ ํŠน์ • ์ˆ˜๋Ÿ‰ ์ดํ•˜์ธ Book์— ๋Œ€ํ•ด์„œ ์žฌ์ฃผ๋ฌธ(Order)์„ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์žฌ์ฃผ๋ฌธ ์ž‘์—…์€ bookOrderJob์ด๋ผ๋Š” Job์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, bookOrderStep์ด๋ผ๋Š” ๋‹จ์ผ Step์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

Paging Size์™€ Chunk Size ๋ž€?

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

  • Paging์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ •ํ•œ ํฌ๊ธฐ์˜ ํŽ˜์ด์ง€๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š”๋ฐ, ์˜ˆ๋ฅผ ๋“ค์–ด ๋งŒ์•ฝ ๋ฐ์ดํ„ฐ๊ฐ€ 1000๊ฐœ๊ฐ€ ์žˆ๊ณ  ํŽ˜์ด์ง€ ํฌ๊ธฐ๋ฅผ 100์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ์ด 10ํŽ˜์ด์ง€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜๋ˆ„์–ด ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • Chunk๋Š” Spring Batch์—์„œ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. Chunk size๋Š” ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ(์ปค๋ฐ‹)๋  ๋ฐ์ดํ„ฐ ํ•ญ๋ชฉ์˜ ์ˆ˜๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ๋งŒ์•ฝ Chunk size๋ฅผ 10์œผ๋กœ ์„ค์ •ํ•œ๋‹ค๋ฉด, ๊ฐ ํŠธ๋žœ์žญ์…˜์€ 10๊ฐœ์˜ ๋ฐ์ดํ„ฐ ํ•ญ๋ชฉ์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Paging Size์™€ Chunk Size์˜ ๊ด€๊ณ„

Paging Size๊ฐ€ 5์ด๊ณ , Chunk Size๊ฐ€ 10์ผ ๊ฒฝ์šฐ, 2๋ฒˆ์˜ Read๊ฐ€ ์ด๋ฃจ์–ด์ง„ ํ›„์— 1๋ฒˆ์˜ Transaction์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ•œ ๋ฒˆ์˜ Transaction์„ ์œ„ํ•ด 2๋ฒˆ์˜ ์ฟผ๋ฆฌ ์ˆ˜ํ–‰์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ, ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์„ ํšจ์œจ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ํšจ๊ณผ์ ์ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์œ„ํ•ด์„  Spring Batch์—์„œ ๊ถŒ์žฅํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํŽ˜์ด์ง€ ํฌ๊ธฐ๋ฅผ ์ƒ๋‹นํžˆ ํฌ๊ฒŒ ์„ค์ •ํ•˜๊ณ  ํŽ˜์ด์ง€ ํฌ๊ธฐ์™€ ์ผ์น˜ํ•˜๋Š” ์ปค๋ฐ‹ ๊ฐ„๊ฒฉ(Chunk Size)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ฆ‰ Paging Size์™€ Chunk Size์„ ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PagingReader ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ ์‹œ ๊ฐ ์ฟผ๋ฆฌ์— Offset๊ณผ Limit๋ฅผ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด๋Š” PageSize๋ฅผ ์ง€์ •ํ•˜๋ฉด Batch์—์„œ Offset๊ณผ Limit๋ฅผ ์ง€์ •ํ•ด ์ค๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ˜๋“œ์‹œ Order By๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
 

Batch ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleBatchJobTest {

    // JobLauncherTestUtils๋Š” Spring Batch๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋กœ, 
    // Job์„ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋จ.
    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    // JobRepositoryTestUtils๋Š” Job ์‹คํ–‰์— ๊ด€๋ จ๋œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋Š”๋ฐ ์‚ฌ์šฉ,
    // ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•จ.
    @Autowired
    private JobRepositoryTestUtils jobRepositoryTestUtils;

    @Before
    public void clearMetaData() {
        jobRepositoryTestUtils.removeJobExecutions();
    }

    @Test
    public void testJob() throws Exception {
        // Job์„ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ JobExecution ๊ฐ์ฒด๋กœ ๋ฐ›์Œ.
        JobExecution jobExecution = jobLauncherTestUtils.launchJob();

        // ์‹คํ–‰๋œ Job์˜ ์ƒํƒœ๊ฐ€ COMPLETED(์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋จ)์ธ์ง€ ํ™•์ธ
        assertThat(jobExecution.getStatus()).isEqualTo(BatchStatus.COMPLETED);
    }
}

๊ธฐ๋ณธ์ ์œผ๋กœ ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 
 

์ฐธ๊ณ 

๋Œ“๊ธ€