스프링
@jw1010110
수강평 작성수
2
평균평점
5.0
게시글
질문&답변
@StepScope 또는 @JobScope와 JobOperator
@Configuration class TestConfig( private val jobOperator: JobOperator, private val dataSource: DataSource, private val jobRepository: JobRepository, private val transactionManager: PlatformTransactionManager, ) { companion object { private const val CHUNK_SIZE = 200 } private val logger = KotlinLogging.logger {} @Scheduled(cron = "*/30 * * * * *") fun runtestJob() { val jobParameters = JobParametersBuilder() .addString("startTime", LocalDateTime.now().toString()) .toJobParameters() jobOperator.start(testJob(),jobParameters) } @Bean fun testJob(): Job = JobBuilder("testJob",jobRepository) .start(testStep()) .build() @Bean fun testStep(): Step = StepBuilder("testStep", jobRepository) .chunk(CHUNK_SIZE) .transactionManager(transactionManager) .reader(testReader()) .writer(testWriter()) .build() @Bean @StepScope fun testReader(): JdbcPagingItemReader = JdbcPagingItemReaderBuilder() .name("testReader") .dataSource(dataSource) .pageSize(CHUNK_SIZE) .selectClause("select img_name, pk") .fromClause("from card_img") .sortKeys(mapOf("pk" to Order.ASCENDING)) .rowMapper { rs, _ -> rs.getString("img_name") } .build() @Bean @StepScope fun testWriter(): ItemWriter = ItemWriter { chunk -> logger.info { "${chunk.items.size}개 전달받았습니다." } } }이 코드를 실행하고 나는 30초마다 x개 전달받았습니다. 라는 로그가 남길 기대한다.하지만 로그는 아래와 같이 최초 1회만 로그가 남는다. 이유를 모르겠다,,! 하루종일 삽질중이다..2026-01-04T19:49:30.143+09:00 INFO 98079 --- [ cached-async-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=testJob]] launched with the following parameters: [{JobParameter{name='startTime', value=2026-01-04T19:49:30.024757, type=class java.lang.String, identifying=true}}] 2026-01-04T19:49:30.205+09:00 INFO 98079 --- [ cached-async-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [testStep] 2026-01-04T19:49:30.224+09:00 INFO 98079 --- [ cached-async-1] com.clip.batch.fcm.TestConfig : 15개 전달받았습니다. 2026-01-04T19:49:30.231+09:00 INFO 98079 --- [ cached-async-1] o.s.batch.core.step.AbstractStep : Step: [testStep] executed in 24ms 2026-01-04T19:49:30.259+09:00 INFO 98079 --- [ cached-async-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=testJob]] completed with the following parameters: [{JobParameter{name='startTime', value=2026-01-04T19:49:30.024757, type=class java.lang.String, identifying=true}}] and the following status: [COMPLETED] in 100ms 2026-01-04T19:50:00.050+09:00 INFO 98079 --- [ cached-async-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=testJob]] launched with the following parameters: [{JobParameter{name='startTime', value=2026-01-04T19:50:00.006744, type=class java.lang.String, identifying=true}}] 2026-01-04T19:50:00.065+09:00 INFO 98079 --- [ cached-async-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [testStep] 2026-01-04T19:50:00.069+09:00 INFO 98079 --- [ cached-async-1] o.s.batch.core.step.AbstractStep : Step: [testStep] executed in 4ms 2026-01-04T19:50:00.089+09:00 INFO 98079 --- [ cached-async-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=testJob]] completed with the following parameters: [{JobParameter{name='startTime', value=2026-01-04T19:50:00.006744, type=class java.lang.String, identifying=true}}] and the following status: [COMPLETED] in 31ms
- 1
- 5
- 85
질문&답변
@StepScope 또는 @JobScope와 JobOperator
파라미터 정상 전달 확인 했고, 잡이 정상 실행된 것도 확인 했어!
- 1
- 5
- 85
질문&답변
@StepScope 또는 @JobScope와 JobOperator
@Configuration class SampleScheduler( private val jobOperator: JobOperator, private val sampleSchedulerContentService: SampleSchedulerContentService, private val dataSource: DataSource, private val jobRepository: JobRepository, private val transactionManager: PlatformTransactionManager, private val jobRegistry: JobRegistry, ) { companion object { private const val CHUNK_SIZE = 200 } private val logger = KotlinLogging.logger {} @Scheduled(cron = "0 0 19,22 * * *") fun runSampleJob() { val hour = LocalDateTime.now().hour val content = when (hour) { 19 -> sampleSchedulerContentService.findFirstSchedulerContent() 22 -> sampleSchedulerContentService.findSecondSchedulerContent() else -> throw IllegalArgumentException("Invalid hour for FCM scheduling") } val jobParameters = JobParametersBuilder() .addString("title", content.title) .addString("content", content.content) .addString("startTime", LocalDateTime.now().toString()) .toJobParameters() val job = jobRegistry.getJob("sampleJob") ?: throw IllegalStateException("Job not found: sampleJob") jobOperator.start(job, jobParameters) } @Bean fun sampleJob(sampleStep: Step): Job = JobBuilder("sampleJob",jobRepository) .start(sampleStep) .build() @Bean fun sampleStep( sampleReader: JdbcPagingItemReader, sampleWriter: ItemWriter, jdbcTemplate: JdbcTemplate ): Step = StepBuilder("sampleStep", jobRepository) .chunk(CHUNK_SIZE) .transactionManager(transactionManager) .listener(object : StepExecutionListener { override fun beforeStep(se: StepExecution) { val cnt = jdbcTemplate.queryForObject( "select count(*) from member where is_allow_notify = true and firebase_token is not null", Long::class.java ) logger.info { "PRE-CHECK count=$cnt" } } override fun afterStep(se: StepExecution): ExitStatus { logger.info { "readCount=${se.readCount}, writeCount=${se.writeCount}, commitCount=${se.commitCount}" } return se.exitStatus } }) .reader(fcmReader) .writer(fcmWriter) .build() @Bean @StepScope fun sampleReader(): JdbcPagingItemReader { val reader = JdbcPagingItemReaderBuilder() .name("sampleReader") .dataSource(dataSource) .pageSize(CHUNK_SIZE) .selectClause("select firebase_token, pk") .fromClause("from member") .whereClause("is_allow_notify = true and firebase_token is not null") .sortKeys(mapOf("pk" to Order.ASCENDING)) .rowMapper { rs, _ -> rs.getString("firebase_token") } .build() logger.info { "sampleReader bean created: " + System.identityHashCode(reader) } return reader } @Bean @StepScope fun sampleWriter( @Value("#{jobParameters['title']}") title: String?, @Value("#{jobParameters['content']}") content: String?, ): ItemWriter { val itemWriter = ItemWriter { chunk -> logger.info { "${chunk.items.size}개의 fcmToken을 대상으로 multicastFcm 비동기처리 하였습니다." } } logger.info { "sampleWriter bean created: " + System.identityHashCode(itemWriter) } return itemWriter } }형 위 코드처럼 돌리면 처음 1회는 리더에서 쿼리 조건에 해당하는 만큼 레코드를 읽어오고 writer까지 잘 돌거든? 근데 2번째 실행부터 reader에서 0개를 읽어오네? 그러니까 당연히 writer도 동작 안하고,,reader 빈도 스탭마다 새로 잘 생성되는 걸 확인했는데 왜 실행하면 0개 읽고 끝내버릴까,,!?리스너에서 대상 래코드가 잘 존재하는 것도 확인했어!
- 1
- 5
- 85
질문&답변
Batch6: jobOperator.startNextInstance() throws UnexpectedJobExecutionException
@EnableJdbcJobRepository를 메인 어플리케이션 클래스에 추가하고 모든 것이 해결되었다..인프런의 대표 배치 마스터 kill9형도 예상하지 못할 정도의 기초적인 실수를 해서 형의 시간을 빼앗은 점,, 깊이 사죄한다.덕분에 공식 마이그레이션 문서를 다시 읽다가 발견했다.곧 세상으로 나올 배치6 강의 기다리고 있겠다.고맙다!!
- 1
- 5
- 72
질문&답변
Batch6: jobOperator.startNextInstance() throws UnexpectedJobExecutionException
헤이 봇, 나는 이미 너가 알려준 방법을 gpt에게 전달 받아봤다. 너가 말한 메서드는 이미 지원 종료다.나는 그저 기다린다. kill9,,,,
- 1
- 5
- 72




