이전 게시글
https://wn1331.tistory.com/252
Spring Batch 5 - 1. 시작하기
회사에서 신규 프로젝트를 하는데, 배치 서버를 구축하는 업무를 내가 맡게 되었다. 포스팅은 배치 서버를 구축하는 방법, Job을 생성하고 Tasklet 기반 Step과 Chunk 기반 Step에 대해서 다루어 볼 것
wn1331.tistory.com
이전 포스팅에서 했던 프로젝트에서 추가로 Batch 설정을 추가한다.
1. Spring Batch MetaData란?
메타데이터 테이블은 Spring Batch 프레임워크에서 배치 작업의 실행 정보를 저장하는 데이터베이스 테이블이다.
이러한 테이블은 배치 작업이 실행되고 완료되는 동안 발생하는 여러 이벤트 및 정보를 추적하고 기록하는 데 사용된다.

- BATCH_JOB_INSTANCE : 모든 JobInstance에 관련된 정보를 저장한다. 테이블 구조와 각 열의 역할에 대한 설명이 포함되어 있다.
- BATCH_JOB_EXECUTION : JobExecution과 관련된 정보를 저장하며, 각 실행에 대한 상태, 시간 등이 기록된다.
- BATCH_JOB_EXECUTION_PARAMS : JobParameters와 관련된 정보를 포함하며, 각 매개변수에 대한 키/값 쌍이 저장된다.
- BATCH_STEP_EXECUTION : StepExecution과 관련된 정보를 저장하며, 각 단계 실행에 대한 세부 정보가 기록된다.
- BATCH_JOB_EXECUTION_CONTEXT: JobExecution과 관련된 작업 수준 데이터가 저장되며, 오류 발생 후 상태를 검색하는 데 사용될 수 있다.
- BATCH_STEP_EXECUTION_CONTEXT : StepExecution과 관련된 데이터가 저장되며, 실패 후 상태를 검색하는 데 사용될 수 있다
2. application.properties에서 메타데이터 스키마 자동생성 설정

위처럼 작성하면 public 스키마에 메타데이터 테이블들이 자동으로 생성이 된다.
여기서 내가 업무시 발생했던 문제에 대해 얘기하자면, 스키마 기반 멀티테넌시 구조에서 각 스키마마다 회사가 정해져 있는데, 그 회사들에 대한 데이터들이 들어있을 때, Reader들이 어떻게 메타데이터 스키마와 작업을 하는 스키마를 구분하고 데이터를 Read하느냐 였다.
멀티테넌시 구조가 아니더라도, 메타데이터 스키마와 작업 스키마는 분리해야 하는게 당연하다고 하더라. 그래서 이 문제를 꼭 해결하고 넘어가야 겠다고 생각했었다.
*public 스키마가 아닌 다른 스키마에 메타데이터 테이블이 생성되고,
메타데이터를 기록할때 그 스키마에서만 작업 하고 싶으면 어떻게 해야 할까?
이에 대한 해결책은 매우 단순했다. 코드만 한줄 추가해 주면 되는 것.

하지만 그냥은 안된다. 스키마를 먼저 생성해 주어야 한다.

그 다음 기존에 public 스키마에 있는 메타데이터 테이블들과 시퀀스들을 전부 batch_metadata 스키마로 옮겨주자.

마지막으로 자동생성을 꺼주면 완료.

위 작업이 제대로 수행되는지 확인하려면 Job이 있어야 한다. Job을 만들어 보자.
2. Batch Job 이란?
배치를 수행하려면 일단 Job이 있어야 한다.
1) Job이란?

- Batch 계층 구조에서 가장 상위에 있는 개념으로서 하나의 배치작업 자체를 의미
- Job Configuration을 통해 생성되는 객체 단위로서 배치작업을 어떻게 구성하고 실행할 것인지 전체적으로 설정하고 명세해 놓은 객체
- 배치 Job을 구성하기 위한 최상위 인터페이스이며 스프링 배치가 기본 구현체를 제공한다.
- 여러 Step을 포함하고 있는 컨테이너로 반드시 한 개 이상의 Step으로 구성해야 한다.
2) Job의 기본 구현체
- SimpleJob : 순차적으로 Step을 실행시키는 Job
- FlowJob : 특정한 조건과 흐름에 따라 Step을 구성하여 실행시키는 Job, Flow 객체를 실행시켜서 작업을 한다.
쉽게 말해 Job은 하나의 배치 작업 단위이다.
3. Job 생성
기본적으로 Job을 수행하려면 JobConfiguration 객체를 생성해야 한다.
Step은 아직 글을 작성하지 않았으므로 간단한 tasklet으로 생성해보자.
전체 코드
@Configuration
@RequiredArgsConstructor
public class JobConfiguration {
private final PlatformTransactionManager platformTransactionManager;
private final JobRepository jobRepository;
@Bean
public Job exampleJob(Step step1, Step step2) {
return new JobBuilder("exampleJob", jobRepository)
.start(step1)
.next(step2)
.build();
}
@Bean
@JobScope
public Step step1() {
return new StepBuilder("exampleStep1", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("hello step1");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
@Bean
@JobScope
public Step step2() {
return new StepBuilder("exampleStep1", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("hello step2");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
}
@JobScope와 tasklet에 대해서는 Step부분에서 설명하겠다.
Job을 실행하면 가장 먼저 start()로 선언한 step1이 실행된다.
start()의 코드를 까 보면, Step을 파라미터로 받아서, SimpleJobBuilder를 실행한다.

SimpleJobBuilder는 내부적으로 List<Step>을 구현하고 있고, 여기에 파라미터로 받은 Step을 담는다.


다음으로 next()의 코드도 까 보면, next()도 start()와 마찬가지로 Step을 파라미터로 받아서 SimpleJobBuilder의 List<Step>에 값을 추가한다.

그럼 이제 Job을 수행해보기 위해 application.properties에 한줄만 추가해 주도록 하자.

spring.batch.job.enabled의 값을 true로 지정하면, 스프링부트 실행 시 빈으로 등록된 job을 실행한다.
그럼 이제 스프링부트를 실행시켜 보자.

job이 수행되고, step1과 step2가 순차적으로 수행된 것을 볼 수 있다.
이제, batch_metadata 스키마의 메타데이터 테이블에 데이터가 입력된 것까지 확인해 보자.


테스트해보려고 job을 실행시켰다가 truncate했기 때문에 id값은 몇개 뛰어넘어있다. id값은 신경쓰지 않아도 된다.
4. JobParameter
그럼 이제 다시 스프링부트를 종료하고, 재실행 시켜보자.

Step1과 Step2가 이미 complete됐고, not restartable하기 때문에 아무런 액션을 취하지 않았다는 로그가 나타났다. 왜 이런 것일까?
Job은 실행할 때 파라미터 값을 가져야 한다. 파라미터 값이 중복되어 실행되고, complete되었다면 이미 complete된 Job이라 하여 complete한 Step들을 실행시키지 않는다는 것이다. 지금 상황은 파라미터 값을 설정해주지 않아서 파라미터가 없는 Job이 한번 실행하고 똑같은 Job을 실행하고 그 Job은 Step이 이미 완료된 상태여서 이라 보면 된다.
애초에 Job을 실행하는 방법이 잘못된 것이다.
다시 application.properties에 들어가 spring.batch.job.enabled를 false로 바꾸어 주자.

그럼 이제 Job을 실행시켜 주기 위한 트리거로, Controller를 사용해 API를 호출해 보자.
api 디렉토리를 만들고, 그 안에 JobStartController를 생성하고 아래와 같이 작성한다.

위 코드에서 설정한 고유값은 현재시간 밀리초가 되겠다.
이제 localhost:포트번호/start 를 호출하면 Job을 수행한다.
계속 호출해도 step들을 잘 수행하는 것을 볼 수 있다.

5. JobInstance
Job이 실행될 때 생성되는 Job의 논리적인 실행 단위 객체로서 고유하게 식별 가능한 작업 실행을 나타낸다.
Job의 설정과 구성은 동일하지만 Job이 실행되는 시점에 처리하는 내용은 다르기 때문에 Job의 실행을 구분해야 한다.
간단하게 말하면 Job + JobParameter라고 생각하면 된다.
위에서 작성했던 exampleJob이 있고 설정한 파라미터 timestamp가 1697777911605를 한번 수행한 상태(complete)라고 하자.
정확히 똑같은 시간(1697777911605)에 exampleJob을 수행하면 이는 새로운 JobInstance 객체를 생성하지 않는다.(초기 수행시 실패했어도 생성 안함)
다른 시간에 exampleJob을 수행하면 새로운 JobInstance 객체를 생성한다.
JobInstance는 해시값으로 고유한 key를 가지고 있다. Job과는 1:M 관계이다.
6. JobExecution
JobInstance에 대한 한 번의 시도를 의미하는 객체로서 Job 실행 중에 발생한 정보들을 저장하고 있는 객체
- 시작시간, 종료시간, 상태(시작된, 완료, 실패), 종료상태의 속성을 가짐
JobInstance와의 관계
- JobExecution의 실행 결과가 FAILED 또는 COMPLETED 등의 Job 실행 결과 상태를 가지고 있음
- JobExecution의 실행 상태 결과가 COMPLETED명 JobInstance 실행이 완료된 것으로 간주해서 재 실행이 불가능
- JobExecution의 실행 상태 결과가 FAILED면 JobInstance 실행이 완료되지 않은 것으로 간주해서 재실행이 가능함
- JobParameter가 동일한 값으로 Job을 실행할지라도 JobInstance를 계속 실행할 수 있음
- JobExecution의 실행 결과가 COMPLETED될 때까지 하나의 JobInstance 내에서 여러 번의 시도가 생길 수 있음

'Spring > Spring Batch' 카테고리의 다른 글
Spring Batch 5 - 5. ItemReader (0) | 2023.10.22 |
---|---|
Spring Batch 5 - 4. Task 기반 step, Chunk 기반 step (0) | 2023.10.22 |
Spring Batch 5 - 3. Step 개요 (0) | 2023.10.20 |
Spring Batch 5 - 1. 시작하기 (0) | 2023.10.20 |
이전 게시글
https://wn1331.tistory.com/252
Spring Batch 5 - 1. 시작하기
회사에서 신규 프로젝트를 하는데, 배치 서버를 구축하는 업무를 내가 맡게 되었다. 포스팅은 배치 서버를 구축하는 방법, Job을 생성하고 Tasklet 기반 Step과 Chunk 기반 Step에 대해서 다루어 볼 것
wn1331.tistory.com
이전 포스팅에서 했던 프로젝트에서 추가로 Batch 설정을 추가한다.
1. Spring Batch MetaData란?
메타데이터 테이블은 Spring Batch 프레임워크에서 배치 작업의 실행 정보를 저장하는 데이터베이스 테이블이다.
이러한 테이블은 배치 작업이 실행되고 완료되는 동안 발생하는 여러 이벤트 및 정보를 추적하고 기록하는 데 사용된다.

- BATCH_JOB_INSTANCE : 모든 JobInstance에 관련된 정보를 저장한다. 테이블 구조와 각 열의 역할에 대한 설명이 포함되어 있다.
- BATCH_JOB_EXECUTION : JobExecution과 관련된 정보를 저장하며, 각 실행에 대한 상태, 시간 등이 기록된다.
- BATCH_JOB_EXECUTION_PARAMS : JobParameters와 관련된 정보를 포함하며, 각 매개변수에 대한 키/값 쌍이 저장된다.
- BATCH_STEP_EXECUTION : StepExecution과 관련된 정보를 저장하며, 각 단계 실행에 대한 세부 정보가 기록된다.
- BATCH_JOB_EXECUTION_CONTEXT: JobExecution과 관련된 작업 수준 데이터가 저장되며, 오류 발생 후 상태를 검색하는 데 사용될 수 있다.
- BATCH_STEP_EXECUTION_CONTEXT : StepExecution과 관련된 데이터가 저장되며, 실패 후 상태를 검색하는 데 사용될 수 있다
2. application.properties에서 메타데이터 스키마 자동생성 설정

위처럼 작성하면 public 스키마에 메타데이터 테이블들이 자동으로 생성이 된다.
여기서 내가 업무시 발생했던 문제에 대해 얘기하자면, 스키마 기반 멀티테넌시 구조에서 각 스키마마다 회사가 정해져 있는데, 그 회사들에 대한 데이터들이 들어있을 때, Reader들이 어떻게 메타데이터 스키마와 작업을 하는 스키마를 구분하고 데이터를 Read하느냐 였다.
멀티테넌시 구조가 아니더라도, 메타데이터 스키마와 작업 스키마는 분리해야 하는게 당연하다고 하더라. 그래서 이 문제를 꼭 해결하고 넘어가야 겠다고 생각했었다.
*public 스키마가 아닌 다른 스키마에 메타데이터 테이블이 생성되고,
메타데이터를 기록할때 그 스키마에서만 작업 하고 싶으면 어떻게 해야 할까?
이에 대한 해결책은 매우 단순했다. 코드만 한줄 추가해 주면 되는 것.

하지만 그냥은 안된다. 스키마를 먼저 생성해 주어야 한다.

그 다음 기존에 public 스키마에 있는 메타데이터 테이블들과 시퀀스들을 전부 batch_metadata 스키마로 옮겨주자.

마지막으로 자동생성을 꺼주면 완료.

위 작업이 제대로 수행되는지 확인하려면 Job이 있어야 한다. Job을 만들어 보자.
2. Batch Job 이란?
배치를 수행하려면 일단 Job이 있어야 한다.
1) Job이란?

- Batch 계층 구조에서 가장 상위에 있는 개념으로서 하나의 배치작업 자체를 의미
- Job Configuration을 통해 생성되는 객체 단위로서 배치작업을 어떻게 구성하고 실행할 것인지 전체적으로 설정하고 명세해 놓은 객체
- 배치 Job을 구성하기 위한 최상위 인터페이스이며 스프링 배치가 기본 구현체를 제공한다.
- 여러 Step을 포함하고 있는 컨테이너로 반드시 한 개 이상의 Step으로 구성해야 한다.
2) Job의 기본 구현체
- SimpleJob : 순차적으로 Step을 실행시키는 Job
- FlowJob : 특정한 조건과 흐름에 따라 Step을 구성하여 실행시키는 Job, Flow 객체를 실행시켜서 작업을 한다.
쉽게 말해 Job은 하나의 배치 작업 단위이다.
3. Job 생성
기본적으로 Job을 수행하려면 JobConfiguration 객체를 생성해야 한다.
Step은 아직 글을 작성하지 않았으므로 간단한 tasklet으로 생성해보자.
전체 코드
@Configuration
@RequiredArgsConstructor
public class JobConfiguration {
private final PlatformTransactionManager platformTransactionManager;
private final JobRepository jobRepository;
@Bean
public Job exampleJob(Step step1, Step step2) {
return new JobBuilder("exampleJob", jobRepository)
.start(step1)
.next(step2)
.build();
}
@Bean
@JobScope
public Step step1() {
return new StepBuilder("exampleStep1", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("hello step1");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
@Bean
@JobScope
public Step step2() {
return new StepBuilder("exampleStep1", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("hello step2");
return RepeatStatus.FINISHED;
}, platformTransactionManager)
.build();
}
}
@JobScope와 tasklet에 대해서는 Step부분에서 설명하겠다.
Job을 실행하면 가장 먼저 start()로 선언한 step1이 실행된다.
start()의 코드를 까 보면, Step을 파라미터로 받아서, SimpleJobBuilder를 실행한다.

SimpleJobBuilder는 내부적으로 List<Step>을 구현하고 있고, 여기에 파라미터로 받은 Step을 담는다.


다음으로 next()의 코드도 까 보면, next()도 start()와 마찬가지로 Step을 파라미터로 받아서 SimpleJobBuilder의 List<Step>에 값을 추가한다.

그럼 이제 Job을 수행해보기 위해 application.properties에 한줄만 추가해 주도록 하자.

spring.batch.job.enabled의 값을 true로 지정하면, 스프링부트 실행 시 빈으로 등록된 job을 실행한다.
그럼 이제 스프링부트를 실행시켜 보자.

job이 수행되고, step1과 step2가 순차적으로 수행된 것을 볼 수 있다.
이제, batch_metadata 스키마의 메타데이터 테이블에 데이터가 입력된 것까지 확인해 보자.


테스트해보려고 job을 실행시켰다가 truncate했기 때문에 id값은 몇개 뛰어넘어있다. id값은 신경쓰지 않아도 된다.
4. JobParameter
그럼 이제 다시 스프링부트를 종료하고, 재실행 시켜보자.

Step1과 Step2가 이미 complete됐고, not restartable하기 때문에 아무런 액션을 취하지 않았다는 로그가 나타났다. 왜 이런 것일까?
Job은 실행할 때 파라미터 값을 가져야 한다. 파라미터 값이 중복되어 실행되고, complete되었다면 이미 complete된 Job이라 하여 complete한 Step들을 실행시키지 않는다는 것이다. 지금 상황은 파라미터 값을 설정해주지 않아서 파라미터가 없는 Job이 한번 실행하고 똑같은 Job을 실행하고 그 Job은 Step이 이미 완료된 상태여서 이라 보면 된다.
애초에 Job을 실행하는 방법이 잘못된 것이다.
다시 application.properties에 들어가 spring.batch.job.enabled를 false로 바꾸어 주자.

그럼 이제 Job을 실행시켜 주기 위한 트리거로, Controller를 사용해 API를 호출해 보자.
api 디렉토리를 만들고, 그 안에 JobStartController를 생성하고 아래와 같이 작성한다.

위 코드에서 설정한 고유값은 현재시간 밀리초가 되겠다.
이제 localhost:포트번호/start 를 호출하면 Job을 수행한다.
계속 호출해도 step들을 잘 수행하는 것을 볼 수 있다.

5. JobInstance
Job이 실행될 때 생성되는 Job의 논리적인 실행 단위 객체로서 고유하게 식별 가능한 작업 실행을 나타낸다.
Job의 설정과 구성은 동일하지만 Job이 실행되는 시점에 처리하는 내용은 다르기 때문에 Job의 실행을 구분해야 한다.
간단하게 말하면 Job + JobParameter라고 생각하면 된다.
위에서 작성했던 exampleJob이 있고 설정한 파라미터 timestamp가 1697777911605를 한번 수행한 상태(complete)라고 하자.
정확히 똑같은 시간(1697777911605)에 exampleJob을 수행하면 이는 새로운 JobInstance 객체를 생성하지 않는다.(초기 수행시 실패했어도 생성 안함)
다른 시간에 exampleJob을 수행하면 새로운 JobInstance 객체를 생성한다.
JobInstance는 해시값으로 고유한 key를 가지고 있다. Job과는 1:M 관계이다.
6. JobExecution
JobInstance에 대한 한 번의 시도를 의미하는 객체로서 Job 실행 중에 발생한 정보들을 저장하고 있는 객체
- 시작시간, 종료시간, 상태(시작된, 완료, 실패), 종료상태의 속성을 가짐
JobInstance와의 관계
- JobExecution의 실행 결과가 FAILED 또는 COMPLETED 등의 Job 실행 결과 상태를 가지고 있음
- JobExecution의 실행 상태 결과가 COMPLETED명 JobInstance 실행이 완료된 것으로 간주해서 재 실행이 불가능
- JobExecution의 실행 상태 결과가 FAILED면 JobInstance 실행이 완료되지 않은 것으로 간주해서 재실행이 가능함
- JobParameter가 동일한 값으로 Job을 실행할지라도 JobInstance를 계속 실행할 수 있음
- JobExecution의 실행 결과가 COMPLETED될 때까지 하나의 JobInstance 내에서 여러 번의 시도가 생길 수 있음

'Spring > Spring Batch' 카테고리의 다른 글
Spring Batch 5 - 5. ItemReader (0) | 2023.10.22 |
---|---|
Spring Batch 5 - 4. Task 기반 step, Chunk 기반 step (0) | 2023.10.22 |
Spring Batch 5 - 3. Step 개요 (0) | 2023.10.20 |
Spring Batch 5 - 1. 시작하기 (0) | 2023.10.20 |