묻고 답해요
150만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 배치
JobExecution과 JobExecutionContext와의 관계
Q1) JobExecution과 JobExecutionContext는 job이 실행될때마다 생기는게 맞나요?Q2) 맞다면 강의 예제와 같이 같은 job을 두 번 실행(첫번째 실패, 두번째 성공) 했을때 두번째 JobExecutionContext가 첫번째 JobExeuctionContext에 저장된 내용을 알수있다는게 이해가 되지 않습니다. 하지만 실제로 참고해서 동작하고 있고요. 그렇다면 같은 job이고 같은 jobParameter로 연속해서 실행하면 이전 잡을 복사해서 생성이 되나요? 아니면 다른 방법이 있나요?
-
미해결[스프링 배치 입문] 예제로 배우는 핵심 Spring Batch
질문입니다.
Program Arguments가 없어서 VM options에 -Dspring.batch.job.names=helloWorldJob이와같이 설정하여 실행하였고 test파일의 경우 -Dspring.batch.job.names=helloWorldJob -DfileName=test.csv 이렇게 설정하였는데fileName의 경우 null이 나오는데 현재 저의 코드는 package com.example.SpringBatchTutorial.job.ValidatedParam; import lombok.RequiredArgsConstructor; import org.junit.Test; import org.springframework.batch.core.*; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.JobScope; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /* desc : 파일 이름 파라미터 전달 그리고 검증 run : --spring.batch.job.names=helloWorldJob - fileName=test.csv * */ @Configuration @RequiredArgsConstructor public class ValidatorParamJobConfig { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; @Autowired private JobLauncher jobLauncher; @Autowired private Job validatedParamJob; @Test public void testJob() throws Exception { // JobParameters에 fileName을 명시적으로 추가 JobParameters jobParameters = new JobParametersBuilder() .addString("fileName", "test.csv") // fileName 전달 .toJobParameters(); // Job 실행 jobLauncher.run(validatedParamJob, jobParameters); } @Bean public Job ValidatedParamJob(Step ValidatedParamStep){ return jobBuilderFactory.get("ValidatedParamJob") .incrementer(new RunIdIncrementer()) .start(ValidatedParamStep) .build(); } @JobScope @Bean public Step ValidatedParamStep(Tasklet ValidatedParamTasklet){ return stepBuilderFactory.get("ValidatedParamStep") .tasklet(ValidatedParamTasklet) .build(); } @Bean @StepScope public Tasklet ValidatedParamTasklet(@Value("#{jobParameters['fileName']}") String fileName){ return new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { System.out.println(fileName); System.out.println("Validated Param Tasklet"); return RepeatStatus.FINISHED; } }; } }이렇게 되어있습니다.
-
미해결스프링 배치
특정 job만 실행
해당 강의를 보고 jenkins를 이용해서 spring batch를 실행하려고합니다. 현재 application.yml은 아래와 같이 enabled: false로 해놓았습니다.spring: batch: job: fail-on-job-failure: true enabled: false jdbc: initialize-schema: never젠킨스의 파이프라인인데 파라미터 주입은 잘되었습니다. 그런데 job은 생성이 되었다고 로그가 찍히고 spring도 실행중인데 명시해놓은 job을 실행을 안하네요...2025-03-08T12:51:30.900+09:00 INFO 5801 --- [ main] l.l.e.job.LostItemSearchJobConfig : 🔵 Job 생성: dailyElasticStoreJob 2025-03-08T12:51:32.179+09:00 INFO 5801 --- [ main] l.l.LostNoMoreBatchApplication : Started LostNoMoreBatchApplication in 13.915 seconds (process running for 15.394) Abjava -Duser.timezone=Asia/Seoul -jar /var/jenkins_home/LOST-NO-MORE.jar \\ --spring.batch.job.names=dailyElasticStoreJob \\ --spring.batch.job.parameters="START_DATE=${params.START_DATE},END_DATE=${params.END_DATE}"application.yml에서 enabled: true로하고 job:name=dailyElasticStoreJob로하니깐 되고 어떤게 문제인지 모르겠습니다. 파이프라인은 문제가 없는데 자꾸 spring이 실행 중이고 job을 구동하지 않네요. 직접 JobLauncher를 이용해야하는 방법 밖에 없는 지 궁금합니다.
-
미해결[스프링 배치 입문] 예제로 배우는 핵심 Spring Batch
소스 다운 받을 수 없는건가요?
소스 다운 받을 수 없는건가요?
-
미해결[스프링 배치 입문] 예제로 배우는 핵심 Spring Batch
학습에 사용하는 문서 자료 같은 건 없나요?
학습에 사용하는 문서 자료 같은 건 없나요?
-
미해결스프링 배치
Batch 성능 질문
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.이 강의를 들으면서 프로젝트에 Batch를 도입하려고합니다.Jenkins에서 파라미터로 Spring Batch에 pageNo이라는 인자에 주입하고 Tasklet 인터페이스를 implements한 커스텀 tasklet에서 공공데이터 포털 api를 호출하여 데이터를 가져오고 이를 db에 적재를 하려고 합니다. Job을 1번 실행하면 Step도 1번 실행하는 방식인데 1번 배치 사이클이 끝나면 pageNo을 1 증가해서 다시 배치를 돌리는 방식입니다. 그래서 아래와 같이 2가지 구현을 생각했습니다. Job은 1번 호출하지만 Step을 반복 실행 VS Job을 반복 호출=======================================전자 방식-장점: 메타 테이블에 JobExecution 등 db에 삽입 및 조회가 많이 일어나지 않는다.-단점: 특정 pageNo이 오류가 날 시 몇번째 pageNo에서 오류가 났는 지 확인을 해야하는데 이를 StepExecutionContext에서 디코딩 후 자바 역직렬화를 해야한다.(DB에서 조회만으로 확인하기 힘듦)=======================================후자 방식-장점: DB에서 조회만으로 pageNo이 몇번째에서 오류가 발생했는 지 쉽게 확인 할 수 있다.-단점: DB에 접근이 많다.=======================================제가 직접 느낀바로는 위와 같습니다. 저도 아직 공부한지 얼마 안되어서 잘 모르기에 Chat GPT에게 물어봤습니다. 아래와 같이 Job을 반복 호출하는건 성능상 좋지 않다고 합니다. GPT의 답변이 정답이 아닐 수 있기에 확인도 받고싶고 만약 Step을 반복적으로 호출하는게 더 낫다면 API용 Spring 서버에서 관리자가 로그인하면 이를 StepExecutionContext에서 가져와서 역직렬화 후 브라우저에 보여주는게 더 나은가싶습니다. 선생님께 자문을 구하고싶습니다.
-
미해결스프링 배치
ItemReaderAdapter 종료
ItemReaderAdapter를 사용할 일이 있어서 사용하던 중 조회해온 데이터가 null 일 경우에 배치가 종료가 되어야는데 종료가 안되고 무한히 반복됩니다.. 무엇이 문제일까요??reader(itemReader) .writer(itemWriter) .faultTolerant() .skip(Throwable.class) //모든 예외조건에 대해서 skip 실행 .skipLimit(Integer.MAX_VALUE) .allowStartIfComplete(false) .build();if (객체!= null) { adapter.setTargetObject(this); adapter.setTargetMethod(""); adapter.setArguments(new Object[]{object}); return adapter; } else { return null; } }
-
미해결스프링 배치
[ 강좌 Git 브랜치 문의 ] 섹션 9 > JdbcCursorItemReader, JpaCursorItemReader
섹션 9 > JdbcCursorItemReader, JpaCursorItemReader 강좌 Git 브랜치 정보 문의드립니다. 계속 찾아봐도 못찾겠습니다. Part6.X.X 를 모두 뒤져도 안나옵니다.아시는분 댓글 부탁드립니다 ^^; 강의에서의 파일 중에 특히 리소스 폴더의 아래 2개 파일을 도저히 못찾겠습니다.data-mysql.sqlschema-mysql.sql
-
미해결스프링 배치
Spring Batch 배포 질문
안녕하세요, 강사님.강사님 강의로 Spring Batch를 공부하고 있는 사람입니다. 다름이 아니라 배포 방법에 있어서 마땅한 방법이 떠오르지 않아 질문을 드리게 되었습니다.AWS Cloud를 활용한 배포에서 하나의 EC2 내부에서 API 서버와 Batch 서버를 같이 돌리게 되면 성능 저하 이슈가 발생하는 것으로 알고 있습니다. 그래서 API 서버용 EC2 1, Batch 서버용 EC2 2 별도의 EC2로 관리하면 성능 저하가 발생하지 않을까라고 생각을 하는데 실제 프로젝트에서도 별도의 프로젝트로 API 서버와 Batch 서버 프로젝트를 따로 개발하나요?
-
미해결스프링 배치
spring batch 버전
현재 spring batch 5.xx 버전인걸로 아는데 4.xx버전과 일부 차이가있다고 들었습니다. 프로젝트 구성 및 의존성 설정 파트에 spring 버전도 2.xx로 되어있어서 현재 저는 spring 3.xx이고 batch도 5.xx 버전을 이용하고있어서 크게 문제가 없는 지 궁금합니다. 버전이 업그레이드되면서 deprecated되거나 더 편한 기능들에 대해서는 따로 제공이 없나요?
-
미해결스프링 배치
retry count 관련 질문
안녕하세요 강사님 좋은 강의 감사드립니다.해당 RetryListener 예제에서 조금 이해되지 않는 부분이 있어 글 남깁니다.CustomItemProcessor에서 아이템이 2일 때 예외가 1번 발생하고 CustomItemWriter에서도 아이템이 2일 때 예외가 1번 발생(예외 총 2번 발생)CustomRetryListener의 onError() 메소드가 총 2번 호출되는데 두 번째 호출될 때 context.getRetryCount()가 2로 찍혀야 되는 것이 아닌가 생각이 들었습니다.제가 해당 예제를 따라서 실습할 때도 그렇고 강의 화면을 봐도 retry count가 모두 1로 찍히고 있습니다.동일한 아이템이어도 process할 때와 write할 때 retry count가 다르게 적용되는 것일까요..?확인해주시고 답변 주시면 많은 도움이 될 것 같습니다.감사합니다.
-
미해결스프링 배치
StepExecutionListener 의 afterStep 에서 return ExitStatus.FAILED 에 의한 동작에 의문이 갑니다.
안녕하세요 선생님, 개인적으로 이 강의를 다 보고,혼자서 이것저것 해보고 있는데, 뭔가 이해가 안되는 현상이 있어서 질문드립니다. 일단 제가 작성한 아주 간단한 Batch 코드를 먼저 공유하겠습니다. (Spring boot 3 + Spring Batch core 5 버전입니다.)```javapackage coding.toast.batch.job.reader.config; import coding.toast.batch.job.reader.listener.EmptyReadFailureListener; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; 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.item.support.ListItemReader; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import java.util.List; @Configuration @RequiredArgsConstructor public class CustomItemStreamConfig { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; @Bean public Job customItemStreamConfigJob() { return new JobBuilder("customItemStreamConfigJob", jobRepository) .start(customItemStreamConfigStep()) .next(simpleStep2()) .build(); } private Step simpleStep2() { return new StepBuilder("simpleStep", jobRepository) .tasklet((contribution, chunkContext) -> { System.out.println("이게 나오면 안됩니다!"); return RepeatStatus.FINISHED; }, transactionManager).build(); } @Bean public Step customItemStreamConfigStep() { return new StepBuilder("customItemStreamConfigStep", jobRepository) .<Integer, Integer>chunk(5, transactionManager) .reader(new ListItemReader<>(List.of())) // 고의적으로 비워놨습니다! .writer(chunk -> { chunk.getItems().forEach(System.out::println); }) .listener(emptyReadFailureListener()) .build(); } @Bean public EmptyReadFailureListener emptyReadFailureListener() { return new EmptyReadFailureListener(); } } 여기서 EmptyReadFailureListener 타입의 인스턴스를 빈으로 등록하고, 이를 Step 의 listener 로 등록했습니다. 이 리스너의 역할은 ItemReader 로 부터 읽은 것이 아무것도 없을 때 Step 이 FAILED 되도록 하기 위한 것입니다. 자세한 내용은 아래와 같습니다.package coding.toast.batch.job.reader.listener; import org.springframework.batch.core.*; public class EmptyReadFailureListener implements StepExecutionListener { @Override public ExitStatus afterStep(StepExecution stepExecution) { if (stepExecution.getReadCount() > 0) { return stepExecution.getExitStatus(); } else { return ExitStatus.FAILED.addExitDescription("exit Because Of No Item Read"); } } } 위처럼 작성했습니다.저는 위의 Job 을 실행하면...Job 의 BatchStatus 와 ExitStatus 모두 FAILED 가 된다.simpleStep 은 이전 Step 에서 ExitStatus Failed 가 됐으므로 실행되지 않는다. ... 하지만 정확히 정반대의 결과가 나왔습니다 😅😅 batch_job_execution 테이블의 결과: batch_step_execution 테이블의 결과: 보시는 바와 같이 Job 자체는 Completed 로 끝이 났습니다.또한 실행되지 않았으면 했던 두번째 step(=simpleStep) 도 실행이 되버리더라구요... 질문:1. 왜 이렇게 동작하는 걸까요? 혹시 ExitStatus 에 대해서 뭔가 잘못 알고 있는 걸까요?2. 그리고 이를 해결하기 위해서는 EmptyReadFailureListener 를 어떻게 고치면 좋을까요? 아니면 혹시 더 좋은 방법이 있을까요??
-
미해결스프링 배치
jdbc, jpa 커서방식 조회 방식 차이 질문 (강사님께 답변 받고 싶습니다)
안녕하세요커서방식에서 Jdbc는 최초 open 메서드 호출 때 db에 쿼리를 날려 ResultSet 첫번 째 row를 가져온 상태에서 read() 를 반복해서 db resultset 커서를 하나씩 이동 시켜 스트리밍 방식으로 가져오는 걸로 알고 있습니다.jpa도 마찬가지로 최초 open 메서드에 쿼리를 날려 작업 결과를 가져오는데 이 때 ResultStream을 반환하고 read에서 실제 db와 상호작용하지 않고 list의 반복자 패턴으로 하나씩 가져온다고 이해했는데요. read때 db와 상호작용을 하지 않는다고 하면 처음 ResultStream에 결과를 다 가져온다는 소리인데 이렇게 되면 메모리 효율이 떨어지지 않나요?
-
미해결스프링 배치
Multithread step과 AsyncItemProcessor
read 작업이 무겁고(느림) processor 작업에는 I/O(api call 등,,)가 많다고 가정했을때 Multithread step(reader는 스레드안전)과 AsyncItemProcessor를 함께 사용해도 문제가 없을까요??Multithread step 자체도 별도의 스레드 풀에서 동작하고AsyncItemProcessor도 다른 스레드 풀에서 작동하고reader(reader는 스레드안전)에서 데이터를 가져올때 이미 작업범위가 정해져있기 때문에 동시성으로 부터도 안전해 보여서요!
-
미해결스프링 배치
job 재실행
안녕하세요 강사님 좋은 강의 감사드립니다.강의의 코드를 따라하며 실습을 하고 있는데 job이 실패했을 경우에만 재시작이 가능하고 completed로 정상적으로 종료되었을 경우에는 재실행이 안된다는 것을 숙지하고 있습니다.(파라미터 없거나 동일할 경우)근데 현재 flow 관련 실습을 하다보니 job 실행 시 아무런 파라미터도 주지 않고 Job이 completed로 정상적으로 끝났음에도 불구하고 Job이 재실행되는 경우가 있는데 이는 어떤 이유 때문인지 알려주실 수 있을까요?step 내부에서 contribution을 통해 extiStatus를 FAILED로 설정한 것 때문인지, 이외에도 다른 이유가 있는지 궁금합니다.확인해주시고 답변 주시면 많은 도움이 될 것 같습니다.감사합니다.
-
미해결스프링 배치
bean 생명주기 문제 도와주세요(@Scope("step"), @Autowired)
안녕하세요. 문제가 있는데 해결 방법이 떠오르지 않아 문의드립니다.현재 스프링 배치 4.1.4 버전을 사용하고 있고, 스프링 부트는 2.1.4 버전을 회사에서 사용하고 있습니다. 스프링 배치에서 JobListener를 이용해 에러가 발생할 시 잡의 마지막 부분에서 SMS 전송 시스템을 세팅해서 SMS 전송하도록 사용하려고 하는데요.문제는 아래와 같습니다.@Scope("step") 어노테이션을 사용해야 Spel 의 값을 가져올 수 있음@Component 어노테이션을 달고, JobListener에서 @AutoWired 어노테이션을 사용하려고 할 때 @Scope("step") 영향 때문인지 해당 bean을 찾지 못함 즉 문제를 정리하자면, SMS class에서 Spel 의 값을 가져와야 값을 세팅할 수 있으므로, @Scope("step") 어노테이션을 달아야하는데 이 어노테이션을 붙이면 bean의 생명주기 영향으로 @AutoWired 어노테이션이 작동하지 않는 것 같습니다. 이럴 땐 어떻게 해결해야 하나요?그리고 @Scope("step")을 아직 학습하진 않았으나 찾아보니 스프링 배치에서 사용하는 특별한 scope로 step이 실행되고 있을 때에만 해당 bean이 컨텍스트에서 유지되는 것으로 학습했습니다. 이것이 맞나요? --추가JobExecutionLisener - beforeJob 메서드 부분에 jobExecution.getExecutionContext(); 로 ExecutionContext 인스턴스를 가져온 뒤, 해당 인스턴스에 Map 형태로 값을 집어 넣습니다.예를 들어, jobContext.put("jobId", jobId) 이런 형식으로 값을 세팅해놓고, 각 스텝에서 이 값들을 사용할 땐 @Value("#{jobExecutionContext['jobId']}" 이런 형태로 Spel를 사용합니다. 추가적으로 궁금한 부분은 Value 어노테이션에 어떻게 jobExecutionContext 값이 먹히는지 모르겠습니다.원래는 이름이 똑같아야 하지 않나요? ExecutionContext 인스턴스니까 @Value("#{ExecutionContext['jobId']} 이렇게 사용을 해야할 거 같은데 왜 앞에 job이 붙어야 하는지 잘 모르겠습니다.
-
미해결스프링 배치
Multi-threaded-step과 Partitioning 차이 확인
안녕하세요 Multi-threaded-step과 Partitioning 차이가 제가 알고 있는 게 맞는지 궁금하여 질문드립니다.Multi-threaded-step은 하나의 Step 안에서 Chuk 크기만큼읽기-처리-쓰기 이 한 사이클을 여러 스레드들이 독립적으로 수행되어 단일 스레드보다 속도 향상을 할 수 있고PartitionStep은 하나의 Master Step 안에 파티셔닝 할 Slave Step을 정의하고 이 Step은 공유되고 gridSize 만큼 스레드가 생성되어 해당 스레드에는 독립적으로 스택에 StepExecution 참조를 할당받아 마치 여러 Step들이 스레드 개수만큼 수행되는 건가요? 즉 Multi-threaded-step은 Step in Muti-Thread 이고Partitioning은 Step in Multi-Step 인거죠?
-
미해결스프링 배치
jdbcCursorReader, jdbcPagingReader 질문
안녕하세요 jdbc 를 통해 데이터를 읽어오는 점에 대해 질문이 있습니다.@Bean public ItemReader<Customer> jdbcCursorItemReader() { return new JdbcCursorItemReaderBuilder<Customer>() .name("jdbcCursorItemReader") .fetchSize(10) .dataSource(dataSource) .sql( "select id, firstName, lastName, birthDate from customer where firstName like ? order by lastName, firstName") .beanRowMapper(Customer.class) .queryArguments("A%") .build(); }여기서 fetchSIze 는 db 에서 한번에 가져올 레코드 수이고 @Bean public ItemReader<Customer> jdbcPagingReader() throws Exception { Map<String ,Object> paramMap = new HashMap<>(); paramMap.put("firstName", "H%"); return new JdbcPagingItemReaderBuilder<Customer>() .name("jdbcPagingReader") .dataSource(dataSource) .fetchSize(3) .pageSize(3) .queryProvider(pagingQueryProvider()) .parameterValues(paramMap) .beanRowMapper(Customer.class) .build(); }여기 페이징 방식에는 fetchSize, pageSize 두개를 둘 다 사용할 수 있던데 차이가 정확인 뭔가요? pageSize는 설정 시 쿼리에 Limit 설정 수가 추가되어 나가는 것은 확인했습니다.
-
미해결스프링 배치
step muti-thread 질문
안녕하세요 step 에서의 멀티 스레드 동작 방식에 대해 질문이 있습니다. 4개의 스레드 풀을 관리하는 taskExecutor 를 사용한다고 하였을 때 제일 맨 처음 스레드가 db 에서 fetch size 만큼 가져오고 시작하게 되는데 각 스레드들은 독립적으로 읽기-처리-쓰기 한 싸이클을 독립적으로 처리하게 되는게 맞나요.?아니면 또 스레드1이 읽은 데이터를 스레드 2가 처리하는 방식인가요.?요약은 스레드1이 읽은 데이터를 쓰기 작업까지 한 싸이클을 끝까지 독립적으로 수행하는가 아닌가 입니다.
-
미해결스프링 배치
itemSteam open update close 질문
안녕하세요 강의 중 궁금한 사항이 있어 질문드립니다.chunkSize 가 만약 5이고 데이터 사이즈가 10이라면step을 실행할 때 최초 reader, writer 의 open 메서드를 실행시키고 최초 update 실행 후 chunk process (read, process, write) 5번 주기로 update를 실행, 마지막으로 reader, writer close 해주는걸로 알고 있는데요 여기서 update는 총 3번 진행되는게 맞는건가요? -> 최초 1회, 5번일 때 1회, 10번일때 1회