스프링 애플리케이션 내에서 특정 작업(Job)을 백그라운드에서 반복하거나 단 한 번만 실행하고 싶을 때가 있을 것이다!
이번 포스트에서는 간단한 Job 인터페이스와 JobScheduler 클래스를 이용해 유연하게 실행되는 잡 시스템을 구현하는 방법을 정리했다.
📌 구조 요약
- CustomJob: 각 Job이 따라야 할 인터페이스
- JobScheduler: 스프링 빈 초기화 시점에 Job을 별도 스레드로 실행
- GoogleToDataJob: 실제 실행할 Job 구현체 예시
1. CustomJob 인터페이스
public interface CustomJob {
String getJobName(); // Job 이름
long getSleepMillis(); // 반복 시 대기 시간(ms)
void execute(); // Job 실행 로직
default boolean isRepeatable() {
return true; // 기본은 반복 실행
}
}
getJobName(): 각 Job마다 고유 이름을 설정한다.
getSleepMillis(): 반복 주기를 밀리초 단위로 반환한다.
execute(): 실제 작업이 실행되는 메서드
isRepeatable(): 기본은 true로 반복 실행되며, false로 오버라이드하면 한 번만 실행된다.
2. JobScheduler 클래스
@Component
public class JobScheduler implements InitializingBean {
private final List<CustomJob> jobs;
@Value("${job.name:}")
private String jobName;
public JobScheduler(List<CustomJob> jobs) {
this.jobs = jobs;
}
@Override
public void afterPropertiesSet() {
for (CustomJob job : jobs) {
if (job.getJobName().equals(jobName)) {
Thread thread = new Thread(() -> {
try {
if (job.isRepeatable()) {
while (!Thread.currentThread().isInterrupted()) {
job.execute();
Thread.sleep(job.getSleepMillis());
}
} else {
job.execute(); // 단일 실행
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception ex) {
System.err.println("[ERROR] " + job.getJobName() + ": " + ex.getMessage());
}
}, job.getJobName() + "-Scheduler");
thread.setDaemon(true);
thread.start();
System.out.println("[JobScheduler] " + job.getJobName() + " 스케줄러 시작됨");
}
}
}
}
@Value("${job.name}") : 프로퍼티값에 따라 해당되는 job이 스레드로 실행이 된다.
3. GoogleToDataJob – 반복 실행 Job 예제
@Component("GoogleToDataJob")
public class GoogleToDataJob implements CustomJob {
private final Logger log = LoggerFactory.getLogger(GoogleToDataJob.class);
@Autowired
private SchduleLogService schduleLogService;
@Value("${spring.manaview.job.sleeptime}")
private long SLEEP_TIME;
@Override
public String getJobName() {
return "GoogleToDataJob";
}
@Override
public long getSleepMillis() {
return SLEEP_TIME;
}
@Override
public void execute() {
MDC.put("scheduleName", "googlega4");
try {
log.info("##[GOOGLE GA MANAVIEW] START!");
} catch (Exception e) {
e.printStackTrace();
}
MDC.clear();
}
}
GoogleToDataJob이름의 job이 실행된다.
반복주기는 프로퍼티에 설정된 값을 따르며 job마다 이름이 다른 log 파일을 위해 MDC에 내가 원하는 파일명으로 put을 한다.
반복하지 않는 Job으로 설정하고 싶다면?
@Override
public long getSleepMillis() {
return 0; // 반복 안 하니까 의미 없음
}
@Override
public boolean isRepeatable() {
return false; // 단일 실행
}
isRepeatable 값을 false 로 지정하면 JobScheduler에서 해당 설정을 감지해 반복없이 한번만 호출하게 된다.
사용한 Gradle 의존성 (필수 모듈만 정리)
이 Job Scheduler를 구성하기 위해 필요한 주요 라이브러리는 다음과 같다.
dependencies {
// Spring 기본 모듈
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
// 스케줄링 및 배치 처리
implementation 'org.springframework.boot:spring-boot-starter-batch' // Spring Batch 기반 반복 작업 구성 가능
// Lombok (코드 간결화용)
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
}
주의할점 : Application.properties 설정
# Spring Batch 자동 실행 방지 (중요!)
spring.batch.job.enabled=false
# 웹 애플리케이션이 아닌 배치 전용 실행 (웹 환경 제거)
spring.main.web-application-type=none
# 반복 주기 (예: 10초 간격)
spring.manaview.job.sleeptime=10000
Spring Batch를 사용하는 경우, 기본적으로 애플리케이션 실행 시 모든 Job이 자동 실행되기 때문에,
직접 스케줄러로 Job을 관리할 때는 자동 실행을 끄는 설정이 반드시 필요하다.
또한, 웹 UI가 필요 없는 콘솔 기반 실행이라면 spring.main.web-application-type=none 도 설정하도록 하자.
배포를 한후에는,
java -jar batch-job-1.0.0.jar --job.name=GoogleToDataJob
실행하면 해당 job 프로그램이 실행하는것을 확인할수 있다.