개발놀이터

스프링 배치 + 스케줄러를 이용해 나만의 배치만들어보기 본문

Spring/Spring Batch

스프링 배치 + 스케줄러를 이용해 나만의 배치만들어보기

마늘냄새폴폴 2022. 8. 25. 06:18

저번 포스팅과 이어지는 내용입니다.

 

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

 

스프링 배치를 이용해서 휴면 계정을 관리해보자

스프링 배치에 대한 전반적인 내용은 아래의 포스팅을 참고해주세요. 때문에 스프링 배치에서 사용되는 단어에 대한 설명은 생략합니다. https://coding-review.tistory.com/172 스프링 부트 배치 개념 대

coding-review.tistory.com

 

때문에 배치와 관련된 코드는 생략하도록 하겠습니다. 

 

 

스프링 배치 vs 스케줄러

배치에 대해 잘 알고계시지 못하는 분이 배치와 스케줄러를 같다고 생각하십니다. 하지만 둘은 엄연히 다른 존재입니다. 스프링 배치는 대용량 데이터를 단발성으로 처리해야 하는 경우에 사용하는 프레임워크입니다. 스케줄러는 내가 원하는 타이밍에 원하는 메서드를 실행시킬 수 있는 라이브러리입니다. 

 

 

자 이제 한번 시작해보도록 하겠습니다. 

 

우선 스케줄러에도 다양한 라이브러리가 있습니다. 대표적으로는 Quartz가 있죠. 하지만 스프링에서는 강력한 스케줄러를 제공합니다. 바로 어노테이션을 이용한 스케줄링인데요. 스프링 안에 내장되어있기 때문에 따로 라이브러리를 설정하지 않아도 되는 편리함도 있습니다. 

 

본격적으로 시작하기에 앞서 우리는 크론(cron)표현식 이라는 것을 알아야 합니다. 

 

크론 표현식이 뭐냐? 내가 원하는 시간을 특정한 문자를 통해 나타낼 수 있는 표현식입니다. 그런데 표현식 표현식 하니까 어디서 들어본 것도 같습니다. 

 

맞습니다. 바로 정규 표현식입니다. 즉, 크론 표현식은 정규 표현식처럼 문자열을 이용해 특정 패턴을 표현한 것이라고 생각해주시면 되겠습니다. 크론 표현식은 정규 표현식에 포함되어 있습니다. 따라서 크게 보면 크론 표현식도 정규 표현식이죠. 하지만 크론 표현식이라는 정규 표현식 중에서도 따로 이름을 가진 표현식 입니다. 

 

 

자 그럼 크론 표현식에 대해서 간단하게 알아보고 넘어가도록 하겠습니다. 

 

 

크론 표현식

크론 표현식은 7개의 필드와 숫자 그리고 특수문자로 이루어져 있습니다. 7개의 필드는 다음과 같습니다. 

 

 

출처 : https://zamezzz.tistory.com/197

 

필드에 대해서는 알아봤고 이제 크론 표현식에서 사용가능한 특수문자에 대해서 알아보겠습니다. 

 

  • * : 모든 값을 뜻합니다.
  • ? : 특정 한 값이 없음을 뜻합니다. (*와 비슷한 느낌입니다.)
  • - (하이폰) : 범위를 뜻합니다. 예를 들어서 월요일에서 수요일까지는 MON-WED로 표현합니다.
  • , (콤마) : 특별한 값일 때만 동작합니다. 예를 들어서 MON,WED,FRI 로 표현합니다.
  • / : (시작시간) / (단위) 예를 들어서 0분부터 매5분마다는 0/5 로 표현합니다.
  • L : 일에서 사용하면 마지막 일, 요일에서는 마지막 요일(토요일)을 나타냅니다.
  • W : 가장 가까운 평일 예를 들어서 15W는 15일에서 가장 가까운 평일 (월~금) 을 나타냅니다.
  • # : 몇째주의 무슨 요일을 표현합니다. 예를 들어서 3#2는 2번째주 수요일입니다.

 

이제 필드와 특수문자에 대해서 알아봤습니다. 그럼 크론 표현식이 대충 어떻게 생겼는지 알아보겠습니다.

 

"0 0 0 0 0 0 0" 맨 처음부터 초 / 분 / 시 / 일 / 월 / 요일 / 년 이렇게 일곱개로 구성되어 있습니다. 

 

어떻게 구성해야 할지 되게 막막하실겁니다. 특수문자중에서 가장 많이 쓰이는 것만 알아두시면 됩니다. 저는 여태까지 크론 표현식에서 *, - (하이폰), / 이렇게 세개 말고는 안써봤습니다. 저기서도 하이폰과 별표는 많이 안쓰이고 압도적으로 많이 쓰이는건 / (슬래쉬) 입니다. 

 

cf) * (에스터리크 / 별) 참고로 이 별표는 정식명칭이 에스터리크입니다. 다른 특수문자들도 다 이름이 있습니다만 제일 많이 쓰이는 특수문자이기 때문에 알고계시면 어디가서 아는척 할 수 있습니다. (저도 저거 하나밖에 몰라요 ㅎㅎ..)

 

* 별표는 '모든' 을 뜻합니다. 즉 초 자리에 *를 찍는다면 '매초마다' 라고 해석할 수 있는것이지요 

 

ex) 매초마다 = "* 0 0 0 0 0 0" 

 

/ (슬래쉬) 는 시작 시간과 단위를 적으면 특정 시간마다라고 표현할 수 있습니다. * (별표) 의 상위 버전인 것이죠. 즉, '10분마다' 라고 표현하고 싶다면 0/10 이라고 적으면 됩니다. 굳이 해석하자면 0분부터 10분마다 이겠죠 

 

 

 

자 이제 크론 표현식에 대해서 알았으니 본격적으로 들어가보도록 하겠습니다. 

 

 

우선 배치를 시작하기 위해서는 JobLauncher라는 것을 알아야합니다. JobLauncher는 JobInstance와 JobExecution을 생성해서 Job을 직접적으로 실행할 수 있는 객체입니다. 

 

JobInstance와 JobExecution에 대해서는 저번 포스팅에서 설명해두었으니 혹시 이 부분을 모르시는 분들은 저번 포스팅을 확인해주세요.

 

https://coding-review.tistory.com/172?category=1063479 

 

스프링 부트 배치 개념

대략 10만명의 회원을 거느리는 웹 서비스를 운영한다고 가정했을 때 우린 매일마다 회원들의 상태변화를 감지하고 운용할 수 있어야 합니다. 가령 오늘까지 우리 서비스에 접속하지 않은지 1년

coding-review.tistory.com

 

 

그럼 코드를 보겠습니다. 

 

package com.capston.chatting.service.scheduler;

import com.capston.chatting.config.batch.InactiveMemberJob;
import com.capston.chatting.entity.Member;
import com.capston.chatting.enums.MemberStatus;
import com.capston.chatting.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class ScheduleService {

    private final InactiveMemberJob config;
    private final JobLauncher jobLauncher;
    private final MemberRepository memberRepository;

    @Scheduled(cron = "0/5 * * * * *")
    public void runInactiveMemberScheduler() {
        try {
            jobLauncher.run(config.inactiveMemberJob(), new JobParameters());
        } catch(Exception e) {
            log.error(e.getMessage());
        }
    }
}

 

이렇게 만들면 끝입니다. 우리가 만들었던 InactiveMemberJob을 주입받고 JobLauncher도 주입받습니다. 그리고 JobLauncher 안에 있는 run 메서드를 실행해주는데 파라미터로 Job, JobParameters를 넘겨주면 됩니다. 

 

그리고 시작해보면 5초마다 해당 메서드가 실행되는 것을 알 수 있습니다. 

 

근데... 뭔가 이상합니다?  

 

2022-08-25 02:12:25.352  INFO 13104 --- [MessageBroker-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=inactiveMemberJob]] launched with the following parameters: [{}]
2022-08-25 02:12:25.597  INFO 13104 --- [MessageBroker-3] o.s.batch.core.job.SimpleStepHandler     : Executing step: [inactiveMemberStep]
2022-08-25 02:12:25.797  INFO 13104 --- [MessageBroker-3] o.s.batch.core.step.AbstractStep         : Step: [inactiveMemberStep] executed in 200ms
2022-08-25 02:12:25.960  INFO 13104 --- [MessageBroker-3] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=inactiveMemberJob]] completed with the following parameters: the following status: [COMPLETED] in 545ms

 

우리가 Job에 적었던 InactiveMemberJob execution 이라던가 Step에 적었던 InactiveMemberStep execution로그가 안보입니다. 그리고 Reader에 분명 쿼리가 보여야하는데 쿼리가 안보입니다. 

 

뭐지? Job이 실행이 안된건가? 

 

Job은 내부적으로 돌아가고 있습니다. 때문에 안심하셔도 될 것 같네요. 단지 @Bean으로 등록된 메서드에 적혀있는 로그들은 Bean이 초기화 될 때 한번만 생성되므로 우리가 적은 로그들은 보이지 않을 것입니다. 

 

해당 문제에 대한 스택오버플로우에 제가 올린 질문이 있으니 궁금하신 분들은 한번 보시는것도 좋을 것 같습니다. 

 

https://stackoverflow.com/questions/73450739/spring-batch-when-i-run-using-joblauncher-i-think-the-job-is-not-running/73454379?noredirect=1#comment129731886_73454379 

 

Spring Batch When I run using joblauncher, I think the job is not running

If I restart the project, the job I registered works well and the log is taken well. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _`...

stackoverflow.com

 

다음 포스팅에서는 JobInstance와 JobParameter에 대해서 포스팅하도록 하겠습니다.