프로젝트 개발 중
엔티티를 생성한지 3일 이후에
자동으로 해당 엔티티 필드를 갱신해 만료 상태로 만드는 로직이 필요했다
PostgreSQL 에서는 내장 스케쥴러가 없다는 말도 있고
스케쥴러 사용하는 법이 까다로워서
로깅도 같이 하기 위해 그냥 스프링 내장 스케쥴러를 사용하기로 했다
⚙️ Dependency
@Scheduler 는 Spring Boot Starter 에서 기본으로 제공하는 어노테이션이다
🔨 전역 설정
📝 MyApplication
@EnableScheduling //scheduler 사용
@EnableJpaAuditing //jpa entity 자동 감시
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
내 Application 의 main 메소드가 들어있는 클래스에
@EnableScheduling
어노테이션을 추가해준다
📚 사용법
스케쥴링을 원하는 메소드에 @Scheduled 어노테이션을 추가한다
단, 스케쥴링이 가능한 메소드는 다음과 같은 조건이 있다
- 반환형이 void 이어야함
- 매개변수가 없는 메소드
📌 fixedDelay
해당 메소드가 끝나는 시점을 기준으로 ms 단위로 실행
@Scheduled(fixedDelay = 1000)
protected void scheduleTask() {
...
}
-> 1초마다 실행하는 스케쥴링 메소드
📌 fixedRate
해당 메소드가 시작하는 시점을 기준으로 ms 단위로 실행
병렬로 실행을 원한다면 @Async 어노테이션 가능
@Async
@Scheduled(fixedRate = 1000)
protected void scheduleTask() {
...
}
📌 cron
작업을 예약하여 실행
이번 프로젝트에서 사용할 정책이다
cron 표현식은 다음 사이트에서 도움을 받을 수 있다
단 스프링 스케쥴러에서는 6자리 표기식을 사용하기 때문에
7번째 글자를 지우고 맞는지 확인해보아야 함
이런식로 원하는 정보를 기입하면 Cron 표현식을 만들어준다
내가 적은 Cron 표현식에 따른 다음 스케쥴링도 알려주기 때문에
원하는대로 다음 스케쥴이 동작하는지도 알 수 있다
@Scheduled(cron = "0 0 0 * * ?") //daily at 00:00
protected void scheduleTask() {
...
}
-> 위 식은 매일 00시 자정마다 아래 메소드를 실행하는 어노테이션이다
📚 구현
결국 내가 원하는 것은
매일 00시 자정마다 생성한지 3일이 지난 카드를 가져와서
자동으로 만료시키는 것이다
여기에 로그를 출력하도록 하여
몇 개의 엔티티가 영향을 받았는지도 표시했다
/**
* 매일 00시00분 마다 기간이 지난 카드 자동 만료
*/
@Scheduled(cron = "0 0 0 * * ?") //daily at 00:00
protected void scheduleTask() {
//현재 시간으로 부터 3일 전의 00시 00분 이전 시각
LocalDateTime compareDateTime = LocalDateTime.of(LocalDate.now().minusDays(EXPIRY_DATE_DAY), LocalTime.of(0,0,0));
//그 이전의 카드는 만료되었으므로 모두 가져옴
List<Match> matchList = matchRepository.findAllByIsExpiredFalseAndCreatedAtBefore(compareDateTime);
//만료
matchList.forEach(Match::expire);
log.info("Scheduling System Automatically Expire Matches :: Affected Matches Count - {}", matchList.size());
matchRepository.saveAll(matchList);
}
생성 시간의 비교는 JpaRepository 의 표기 방법 중 CreatedAtBefore 을 통해 비교했다
만료는 boolean 형의 isExpired 필드를 가져와서 비교했다
만약 만료된 리스트를 가져왔을 경우
expire 메소드를 통해 isExpired 를 true 로 변경했다
다른 비즈니스 로직에서는 isExpired 값을 비교하여 수행하도록 바꿨다