개발놀이터

스프링 배치 도메인 이해 : Step, StepExecution 본문

Spring/Spring Batch

스프링 배치 도메인 이해 : Step, StepExecution

마늘냄새폴폴 2022. 9. 23. 19:02

본 포스팅은 인프런의 정수원님의 스프링 배치 강의를 듣고 정리한 포스팅입니다. 더 자세한 내용은 강의를 참고해주세요. 

 

 

Step

기본 개념

  • Batch Job을 구성하는 독립적인 하나의 단계로서 실제 배치 처리를 정의하고 컨트롤 하는 데 필요한 모든 정보를 가지고 있는 도메인 객체
  • 단순한 단일 테스크 뿐 아니라 입력과 처리 그리고 출력과 관련된 복잡한 비즈니스 로직을 포함하는 모든 설정들을 담고 있다.
  • 배치 작업을 어떻게 구성하고 실행할 것인지 Job의 세부 작업을 Task 기반으로 설정하고 명세해 놓은 객체
  • 모든 Job은 하나 이상의 Step으로 구성됨

 

기본 구현체

  • TaskletStep
    • 가장 기본이 되는 클래스로서 Tasklet 타입의 구현체들을 제어한다.
  • PartitionStep
    • 멀티 스레드 방식으로 Step을 여러개로 분리해서 실행한다.
  • JobStep
    • Step내에서 Job을 실행하도록 한다.
  • FlowStep
    • Step내에서 Flow를 실행하도록 한다.

 

그럼 이제 Step에 대해서 구조를 살펴보도록 하겠습니다. 

Step 안에는 execute라는 메서드가 있고 이 메서드는 Step을 실행하는 역할을 합니다. 실행 결과 상태는 StepExecution에 저장됩니다. 

 

Step 인터페이스를 구현한 추상 클래스인 AbstractStep에는 name, startLimit 등 다양한 필드가 존재합니다. 

 

Job에 대해 좀 더 직관적인 구조도를 보겠습니다.

 

 

Job은 execute 메서드를 통해 Step을 실행합니다. Step 또한 execute를 이용해 tasklet을 실행합니다. tasklet 안에는 ItemReader, ItemProcessor, ItemWriter가 존재합니다. 이 세가지 도메인은 추후에 chunk 지향 처리 부분에서 따로 다루도록 하겠습니다. 

 

간단하게 먼저 설명해드리자면 ItemReader는 데이터를 데이터베이스, 파일, json 등등에서 읽어오는 역할을 합니다. ItemProcessor는 이렇게 가져온 데이터를 비즈니스 로직에 맞춰 가공하는 역할을 합니다. ItemWriter는 가공된 데이터를 다시 저장하는 역할을 합니다. 

 

이렇게 설계하면 객체지향스럽게 만들 수 있고 chunk 지향 처리에서 chunk 단위로 데이터를 가공할 때 유용한 모델입니다. 하지만 비즈니스 로직이 단순하거나 지금처럼 도메인의 구조를 이해하는 데는 굳이 ItemReader, ItemProcessor, ItemWriter로 나눌 이유가 없습니다. 때문에 지금은 tasklet으로만으로 이루어진 Step을 사용할 예정입니다. 

 

하지만 tasklet 또한 만능은 아닌데요. 위에서 대충 짐작 하셨겠지만 chunk를 사용하고 싶다면 ItemReader, ItemProcessor, ItemWriter 이 세가지의 도메인을 사용해야 합니다. 이는 다시 말해서 tasklet을 사용한다면 chunk를 사용할 수 없습니다. 

 

대용량 처리에 특화된 스프링 배치에서 chunk 기반의 처리를 하지 않는다는 것은 매우 이상합니다. 때문에 실무에서는 chunk 지향 처리를 많이 사용합니다. 

 

*주의*

tasklet을 직접 생성하는 것과 chunk 지향 처리, 이 둘이 같을까요? 다를까요? 

 

겉으로 보기엔 달라보입니다. 한쪽은 tasklet을 직접 생성해서 해당 메서드 안에 모든 로직을 다 때려 박았고 한쪽은 ItemReader, ItemProcessor, ItemWriter 이 세가지 도메인으로 작업을 쪼갰으니까요 

 

하지만 이 둘은 같습니다. 위의 그림에서 보시다시피 Step의 구현체가 여러개 있는데 tasklet을 직접 생성하는 것과 chunk 지향 처리를 하는 것은 둘 다 TaskletStep 이라는 구현체로 구현됩니다. 

 

 

 

StepExecution

기본 개념

  • Step에 대한 한번의 시도를 의미하는 객체로서 Step 실행 중에 발생한 정보들을 저장하고 있는 객체
    • 시작시간, 종료시간, 상태 (시작됨, 완료, 실패), commit count, rollback count 등의 속성을 가짐
  • Step이 매번 시도될 때마다 생성되며 각 Step 별로 생성된다.
  • Job이 재시작 하더라도 이미 성공적으로 완료된 Step은 재 실행되지 않고 실패한 Step만 실행된다.
  • 이전 단계 Step이 실패해서 현재 Step을 실행하지 않았다면 StepExecution을 생성하지 않는다. Step이 실제로 시작됐을 때만 StepExecution을 생성한다.
  • JobExecution 과의 관계
    • Step의 StepExecution이 모두 정상적으로 완료되어야 StepExecution이 정상적으로 완료된다.
    • Step의 StepExecution중 하나라도 실패하면 JobExecution은 실패한다.

 

BATCH_STEP_EXECUTION 테이블과 매핑

  • JobExecution과 StepExecution은 1 : N 관계
  • 하나의 Job에 여러개의 Step으로 구성했을 경우 각 StepExecution은 하나의 JobExecution을 부모로 가진다.

 

 

StepExecution에 대해서 중요한 부분만 다시 체크하면서 넘어가도록 하겠습니다. 

 

우선 StepExecution은 앞선 포스팅에서의 JobExecution과 비슷합니다. Step 하나하나에 대한 실행 단위라는 의미이기도 하죠

 

Step이 실행될때마다 StepExecution이 생성됩니다. Job이 실행될때마다 생성되는 JobExecution과 닮은 구석이 있죠?

 

그리고 Job이 재시작 하더라도 이미 성공적으로 완료된 Step은 재실행 되지 않고 실패한 Step만 실행됩니다. 어? 이부분에 대해서 스프링 배치를 조금 공부해보신 분들이라면 어디선가 느낀적 있으실 겁니다. 바로 chunk의 특징이죠

 

Job이 실패해서 재실행 하는 상황에서 chunk는 이미 처리된 데이터에 한해서는 재실행 하지 않고 실패했던 부분부터 다시 실행합니다. 때문에 앞선 데이터들을 보호하고 메모리도 보호할 수 있죠

 

하지만 Step의 구조상 후순위 Step을 실행하기 위해서 전단계의 Step이 반드시 실행되어야 하는 경우도 있을 것입니다. 제 포스팅 중 스프링 배치의 특징에 대해 포스팅 한 것에도 나와있지만 스프링 배치는 세부적인 설정까지도 할 수 있게끔 만들어져 있습니다. 따라서 이런 경우에는 allowStartIfComplete 속성을 true로 주면 Step이 재실행 될 때 성공한 Step이더라도 다시 시작할 수 있게끔 할 수 있습니다. 

 

https://coding-review.tistory.com/183

 

스프링 배치의 특징 ( + 다른 언어의 배치)

생각해보니 스프링 배치를 깊게 공부한다는 사람이 스프링 배치이 뭔지에 대한 포스팅이 없다는게 이상해서 스프링 배치의 특징 (장점) 에 대해서 포스팅 해보겠습니다. 추가적으로 다른 언어

coding-review.tistory.com

 

또한, 이전 단계 Step이 실패해서 현제 Step을 실행하지 않았다면 즉, 앞선 Step이 실패해서 후순위 Step이 실행되지 않았다면 해당 StepExecution은 생성되지 않습니다. 

 

이 얘기는 이런 의미입니다. Step1 Step2 Step3 가 있다고 가정하고 Step1은 성공 Step2는 실패 Step3은 실행되지 않았다고 가정합니다. 

 

Step1 성공 -> StepExecution 생성

Step3 실행X -> StepExecution 생성X

 

여기까진 확실하죠?

 

그럼 실패한 Step2는 StepExecution이 생성될까요? 안될까요? 

 

정답은 생성 된다 입니다. Step이 실행중에 실패한것이기 때문에 실패한것이 실행한 것보다 뒤에 일어난 일이라는 의미죠 그래서 실행은 됐기 때문에 StepExecution이 생성 됩니다. 다시 정리하면 이렇습니다.

 

Step1 성공 -> StepExecution 생성

Step2 실패 -> StepExecution 생성

Step3 실행X -> StepExecution 생성X

 

마지막으로 JobExecution은 StepExecution과 1 : N 관계이기 때문에 StepExecution이 하나라도 실패하면 JobExecution 또한 실패로 마무리 됩니다. 

 

 

여기까지 Step, StepExecution에 대해서 알아봤습니다. 다음 포스팅에서는 ExecutionContext 에 대해서 알아보는 시간을 가져보도록 하겠습니다. 긴 글 읽어주셔서 감사합니다.