스프링 스케줄러는 Java에서 제공하는 스프링 프레임워크의 일부로, 애플리케이션 내에서 정기적으로 실행되어야 하는 작업을 스케줄링하는데 사용됩니다.
스프링 스케줄러는 주로 백그라운드에서 반복적으로 수행해야 하는 작업을 위해 사용되며, 정해진 시간이나 주기에 따라 작업을 실행할 수 있게 해줍니다.
@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerApplication.class, args);
}
}
먼저 위의 코드와 같이 스프링 부트 메인 클래스에 @EnableScheduling
을 어노테이션을 선언하여 스케줄링을 사용한다고 명시해줘야 합니다.
@Component
public class ConsoleScheduler {
@Scheduled(cron = "10 * * * * *")
public void printDate () {
System.out.println(new Date().toString());
}
}
실제 스케줄링 작업을 진행할 클래스에 @Scheduled
어노테이션을 추가해준 후 Cron으로 스케줄링 할 주기를 맞춰줍니다.
이 때 스케줄링 작업을 진행할 클래스는 반드시 스프링 빈에 등록된 클래스여야 합니다. @Component
로 스프링 빈으로 등록해줍니다.
참고로 Cron 방식 외에도 fixed delay(작업 완료 후 정해진 시간이 지난 후 다음 작업 수행), fixed rate(작업 실행 시작 시점 이후 정해진 간격으로 반복 실행) 등 다양한 방식들이 있습니다.
저는 Cron 방식으로 스케줄링을 진행하였습니다.
cron 표현식을 사용했을 때 사용할 time zone으로 따로 설정하지 않으면 기본적으로 Local의 time zone입니다.
아래와 같이 Cron 방식을 이용해서 스케줄링 주기 및 시간을 설정하실 수 있습니다.
1 2 3 4 5 6 // 순서
* * * * * * // 실행주기 문자열
// 순서별 정리
1. 초(0-59)
2. 분(0-59)
3. 시간(0-23)
4. 일(1-31)
5. 월(1-12)
6. 요일(0-7)
"0 0 * * * *" = 매 시간마다 실행한다.
"*/10 * * * * *" = 매 10초마다 실행한다.
"0 0 8-10 * * *" = 매일 8, 9, 10시에 실행한다
"0 0 6,19 * * *" = 매일 오전 6시, 오후 7시에 실행한다.
"0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day.
"0 0 9-17 * * MON-FRI" = 오전 9시부터 오후 5시까지 주중(월~금)에 실행한다.
"0 0 0 25 12 ?" = every Christmas Day at midnight
@Scheduled(cron = "* * * * * *", zone = "Asia/Seoul")
public void run() { System.out.println("Hello CoCo World!");}
먼저 스프링 부트 메인 클래스에 스케줄링을 사용한다고 명시 해주었습니다.
그리고 스케줄링 작업을 해줄 클래스를 아래와 같이 생성해주었습니다.
@RequiredArgsConstructor
@Component
@Slf4j
public class FileCleanUpScheduler {
private final FileService fileService;
private final FileInfoRepository fileInfoRepository;
private final FileRepository fileRepository;
private final FileHandler fileHandler;
/**
* 소프트 삭제된 지 3개월이 지난 파일 정보와 그에 해당하는 파일 테이블, S3 파일들을 스케줄링한다
* 매일 자정에 스케줄링이 동작하게 된다
*/
@Scheduled(cron = "0 0 0 * * *")
@Transactional
public void cleanUpExpiredFileInfosData() {
// 삭제해야할 파일 정보 찾기
// Mysql RDS 인스턴스 타임존 설정을 UTC로 해서 쿼리상으로 KST 시간으로 변환해줬다
log.info("소프트 삭제 후 3개월 지난 파일 정보 및 관련 데이터 정기 삭제 스케줄러 동작");
List<DeletedFileInfo> deletedFileInfos = fileInfoRepository.findDeletedFileInfo();
log.info("삭제 대상 파일 Info size: " + deletedFileInfos.size());
// TODO: 성능 개선을 위해 Spring Batch 혹은 벌크 연산을 이용하는 것이 좋아보인다
for (DeletedFileInfo fileInfo: deletedFileInfos) {
File findFile = fileService.findFile(fileInfo.getFileId());
// 파일, 파일 정보 테이블 delete 로직
fileInfoRepository.hardDeleteFileInfo(fileInfo.getId());
fileRepository.hardDeleteFile(findFile.getId());
// S3 저장소에 파일 삭제 로직
fileHandler.delete(findFile.getFileName());
}
}
/**
* 파일 정보에 속하지 않은 파일 테이블의 데이터를 스케줄링한다
* 매일 자정에 스케줄링이 동작하게 된다
*/
@Scheduled(cron = "0 0 0 * * *")
@Transactional
public void cleanUpUnmatchedFile() {
// 삭제해야할 파일 찾기
log.info("파일 정기 삭제 스케줄러 동작");
List<File> filesToCleanUp = fileRepository.findFilesWithNoFileInfo();
log.info("정기 삭제 대상 파일 size : " + filesToCleanUp.size());
// TODO: 성능 개선을 위해 Spring Batch 혹은 벌크 연산을 이용하는 것이 좋아보인다
for (File file: filesToCleanUp) {
// 파일 테이블 delete 로직
fileRepository.hardDeleteFile(file.getId());
// S3 저장소에 파일 삭제 로직
fileHandler.delete(file.getFileName());
}
}
}
스케줄링 주기는 매일 자정에 동작되도록 설정해주었습니다. 해당 스케줄러는 만료기간이 된 파일 또는 필요 없어진 파일들을 주기마다 삭제시켜주는 작업을 진행합니다.
먼저 cleanUpExpiredFileInfosData()
메서드에 대해서 설명드리도록 하겠습니다.
해당 프로젝트에서는 파일에 관련된 데이터를 파일 테이블과 해당 파일이 사용되는 테이블의 정보를 들고 있는 파일 정보 테이블로 관리를 하고 있습니다.
게시글 또는 회원의 정보를 삭제할 경우 해당 도메인에 저장된 파일들도 같이 삭제(소프트 삭제) 되도록 구현하였습니다. 소프트 삭제의 일시를 조회하여 만료 기간이 다됐을 경우 하드 삭제가 되도록 스케줄링을 돌리는 것이cleanUpExpiredFileInfosData()
메서드입니다.
첫 번째 삭제 대상이 될 파일을 찾아야합니다. 이를 위해 아래의 레포지토리를 사용하여 삭제 대상 파일 리스트를 조회해옵니다.
만료기간이 3개월 이상 지난 파일 정보 목록을 조회한 뒤 해당 파일 정보와 그에 속하는 파일, AWS에 저장된 파일을 삭제하는 작업을 진행하도록 구현하였습니다.
해당 메서드는 파일 테이블에는 존재하지만 파일 정보 테이블에는 존재하지 않는 파일 목록들을 스케줄링을 통해 삭제하는 작업을 진행합니다.
예를 들어, 게시글을 생성하는데 게시글 이미지 파일을 등록합니다. 이때 이미지 파일은 실제 파일 테이블에 저장되고 AWS에 파일이 올라가게 됩니다. 이후 게시글을 생성하지 않고 취소하게 될 경우 파일 정보 테이블에는 해당 파일이 존재하지 않게 됩니다. 이런 경우의 AWS 및 파일 테이블에 올라간 파일들을 정리하기 위한 스케줄러가 cleanUpUnmatchedFile()
입니다.
위의 Repository를 이용하여 파일 정보 테이블의 파일 id에 속하지 않은 파일 목록들을 조회해옵니다.
이후 해당 파일들을 파일 테이블과 AWS에서 하드 삭제해주는 작업을 진행합니다.
참고 자료
https://dbjh.tistory.com/56
https://dev-coco.tistory.com/176