카테고리 없음

배치가 뭐길래 : Spring Batch 5

nextcoder 2025. 5. 3. 13:21

배치

Spring Batch는 스케쥴러를 지원하지 않음 (공식 웹사이트 첨부 필요)

Spring Batch의 목적은 대용량 처리

 

스케쥴러 : 일정 주기로 실행하는 것

Quatz는 뭔지?

 

Job(배치 할 것)은 여러개의 Step으로 구성됨

Step은 ItemReader, ItemProcesor, ItemWriter로 구성됨

 

Job을 실현하기 위해 JobLauncher가 필요

 

모두 JobRepository 통해서 데이터랑 통신하는듯?

 

Job : Job 이름 정의 가능, Step 정의와 순서, Job 재사용 가능성 정의

JobInstance :논리적으로 Job을 실행, (어제 실행한 Job인지, 오늘 실행한 Job인지.. JobParameters를 이용해서 구분)

JobExecution : Job을 실행하는 단일 시도. 실패했던 JobInstance에 대한 새로운 실행을 하면 새로운 JobExecution이 생성

- BatchStatus : 실행 상태 (실행중이면 started, 실패하면 failed, 성공하면 completed)

- ExitStatus : 실행 결과. ExitCode 등을 포함

 

Step : Job은 하나 이상의 Step으로 구성됨

배치 작업의 독립적이고 순차적인 단계를 캡슐화하는 도메인 객체

모든 Job은 이상의 Step으로 이뤄짐

Step의 내용은 개발자의 재량

 

StepExecution

- Status 실행 상태

- ExitStatus 실행의 결과

Read Count, WriteCounte, CommitCount, RollbackCount, FilterCount등의 정보를 담고 있음

 

JobRepository : Job, Step 구현을 위한 CRUD 작업을 제공

 

JobLancher : Jobt을 시작하기 위한 간단한 인터페이스

 

메타데이터를 그대로 사용할 수 있음. 비즈니스 로직에 집중하게 해줌

 

 

아이템 : 작업에 사용하는 데이터 (예를 들면 DB의 한 row)

 

ItemReader : Step에서 한 항목씩 검색한다. 모든 항목이 소진된 경우 null을 반환한다. (db 조회)

 

ItemWriter : 여러 출력 항목을 나타냄 (db에 저장 등) (마지막에)

 

ItemProcessor : 비즈니스 처리르 담당. 항목이 유효하지 않으면 null을 반환

 

프레임워크 사용하므로 JobLauncer나 JobRepo는 구현 안해도 됨

 

Tasklet

- 익명 Tasklet

- chunk 

chunk : 각 커밋 사이에 처리되 row의 수

성공 시 cunk 만큼 커밋, 실패 시 chunk 만큼 롤백

 

write에서는 chunk를 한버에 write

 

ItemReader

- cursor, paging

cursor : 1 connection에 1 item 가져옴

paging : 1 connection에 N개의 Item 가져옴

 

page size는 chunk size와 동일하게 설정하는 것이 좋음

page size > chunk size면 하나의 트랜잭션 처리를 위해 paging을 여러번 해야됨

 

ItemProcessor (Optional)

비즈니스 코드를 담는 곳

데이터 실패 시 null을 반환 (?)

 

실패 여부에 따라 조건문으로 step을 실행할 수 있음

 

테스트 코드 작성이 필요. QA 하기 어려움. 통합테스트도 필요, 각각의 단위 테스트도 필요

 

관리 도구

: Cron (리눅스 작업 스케쥴러)

: Quartz + Admin (스케쥴러 프레임워크 + 관리자 페이지 구현)

: CI Tool (ex. Jenkins)

 

젠킨스 장점 : 실행 이력 / 로그 관리에도 좋음

 

✅ Spring Batch ItemReader에서 로그가 안 찍히는 이유 정리

🔹 문제 상황

ItemReader 설정 코드에 아래처럼 로그를 찍었지만, 로그가 한 번만 출력되고 이후에는 보이지 않음:

java
복사편집
@Bean public ItemReader<Long> setSubscriptionEndDtItemReader() { log.debug("setSubscriptionEndDtItemReader execute."); // ← 로그 여기 return new JpaCursorItemReaderBuilder<Long>() .name("setSubscriptionEndDtItemReader") .queryString(...) .entityManagerFactory(emf) .build(); }

🔹 원인

  • 이 로그는 ItemReader 빈 생성 시점에만 실행됨.
  • 실제로 read() 메서드가 실행될 때 찍히는 로그가 아님.
  • 따라서 Step 실행 중에 Reader가 동작하는지 확인할 수 없음.

✅ 왜 그런가?

구분설명
@Bean 메서드 Spring이 어플리케이션 시작 시 한 번만 실행해서 빈을 생성함
log.debug(...) 위 메서드 안에 있으면 빈 생성 시점에만 실행됨
read() 메서드 Step 실행 중에 호출되지만, JpaCursorItemReader 내부이므로 우리가 로그를 못 넣음

✅ 해결 방법

1. read() 호출 시점에 로그를 보고 싶다면

JpaCursorItemReader를 감싸는 커스텀 ItemReader를 만들어서 read()를 직접 override해야 함:

java
복사편집
@Bean public ItemReader<Long> setSubscriptionEndDtItemReader() { JpaCursorItemReader<Long> delegate = new JpaCursorItemReaderBuilder<Long>() .name("setSubscriptionEndDtItemReader") .entityManagerFactory(emf) .queryString(query) .build(); return new ItemReader<Long>() { @Override public Long read() throws Exception { Long id = delegate.read(); log.debug(">>> read() called. id: {}", id); return id; } }; }

✅ 핵심 요약

질문정리
로그가 왜 안 찍혀? @Bean 메서드 안에서만 찍으면, 빈 생성 시점에만 로그가 출력
실제 Step에서 Reader가 호출될 때 로그 찍으려면? read() 메서드를 오버라이드해서 직접 로그 넣어야 함
JpaCursorItemReader는 왜 로그 못 넣어? 내부 구현에 접근할 수 없고, 우리가 read()를 override하지 않았기 때문

 

 

 

 

 

 

 

레퍼런스

https://www.youtube.com/watch?v=1xJU8HfBREY