Spring에서 제공하는 스케줄러 - 스케줄러 : 시간에 따른 특정 작업(Job)의 순서를 지정하는 방법.
설정 방법
1) XXXSSAPPlication.java 파일에 @EnableScheduling 어노테이션 추가
2) 스케쥴링 동작을 위한 클래스 작성
수행시간이 다 끝난 후 시작함
이전 작업이 끝났든 안 끝났든(수행 시간이 있으니까) 시작한지 10초 뒤면 또 시작함
(작업이 겹칠 수 있음)
위에 두개는 일정한 스케쥴을 가지고 돌아갈 수 없음
명확한 날짜를 작성하는 속성
특수문자
* : 모든 수.
- : 두 수 사이의 값. ex) 10-15 -> 10이상 15이하
, : 특정 값 지정. ex) 3,4,7 -> 3,4,7 지정
/ : 값의 증가. ex) 0/5 -> 0부터 시작하여 5마다
? : 특별한 값이 없음. (월, 요일만 해당)
L : 마지막. (월, 요일만 해당)
@Scheduled(cron="0 * * * * *") // 모든 0초 마다 -> 매 분마다 실행
BoardProjectBootApplication.java
@EnableScheduling // 스프링 스케줄러를 이용하기 위한 활성화 어노테이션
@SpringBootApplication(exclude= {SecurityAutoConfiguration.class})
public class BoardProjectBootApplication {
public static void main(String[] args) {
SpringApplication.run(BoardProjectBootApplication.class, args);
}
}
ImageDeleteScheduling
@Component
public class ImageDeleteScheduling {
// @Scheduled(fixedDelay = 5000) // 이전 작업이 시작된 후 5초 후에 수행
// @Scheduled(fixedRate = 5000) // 이전 작업 끝난 후 5초 뒤에 수행
// @Scheduled(cron = "0,15,30,45 * * * * *") // 시계 초 단위가 0,15,30,45 경우 수행
// @Scheduled(cron = "0 0 * * * *") // 정시 마다 수행
// @Scheduled(cron = "0 0 0 * * *") // 자정
// @Scheduled(cron = "0 0 12 * * *") // 정오
@Scheduled(cron = "0 0 0 1 * *") // 매달 1일
public void scheduling() {
}
}
scheduling 메서드 안에 작성
@PropertySource , @Value 이용해서 가져오기
@PropertySource("classpath:/config.properties")
public class ImageDeleteScheduling {
// 회원 프로필 이미지 파일 저장 경로
@Value("${my.profile.folder-path}")
private String profileFolderPath;
// 게시판 이미지 파일 저장 경로
@Value("${my.board.folder-path}")
private String boardFolderPath;
File boardFolder = new File(boardFolderPath);
File memberFolder = new File(profileFolderPath);
File[] boardArr = boardFolder.listFiles();
File[] memberArr = memberFolder.listFiles();
// 두 배열을 하나로 합침 (for 문 한번만 돌기 위해서)
// imageArr 이라는 빈 배열을 boardArr 과 memberArr 길이 만큼의 크기로 만든다.
File[] imageArr = new File[boardArr.length + memberArr.length];
// 배열 내용 복사 (깊은 복사)
// System.arraycopy(복사할 배열, 몇 번 인덱스부터 복사할지, 새로운 배열, 새로운 배열의 몇 번 인덱스부터 넣을지, 복사를 어디까지 할건지)
System.arraycopy(boardArr, 0, imageArr, 0, boardArr.length);
System.arraycopy(memberArr, 0, imageArr, boardArr.length, memberArr.length);
// 배열 -> List 변환 (다루기 쉽도록)
List<File> serverImageList = Arrays.asList(imageArr);
어노테이션, 필드 추가
@RequiredArgsConstructor
public class ImageDeleteScheduling {
private final BoardService service;
서비스 호출
List<String> dbImageList = service.selectDbImageList();
SQL 먼저 조회
SELECT CAST(IMG_RENAME AS VARCHAR2(300)) "rename"
FROM BOARD_IMG
UNION
SELECT SUBSTR(PROFILE_IMG, INSTR(PROFILE_IMG, '/', -1) + 1) "rename"
FROM "MEMBER"
WHERE PROFILE_IMG IS NOT NULL;
SELECT * FROM BOARD_IMG; --> NVARCHAR2(50)
SELECT * FROM "MEMBER"; --> VARCHAR2(300)
둘이 타입이 안 맞아서 UNION 안되는 걸 캐스팅으로 해결
board-mapper.xml
<!-- DB 이미지 파일명 목록 조회 -->
<select id="selectDbImageList">
SELECT CAST(IMG_RENAME AS VARCHAR2(300)) "rename"
FROM BOARD_IMG
UNION
SELECT SUBSTR(PROFILE_IMG, INSTR(PROFILE_IMG, '/', -1) + 1) "rename"
FROM "MEMBER"
WHERE PROFILE_IMG IS NOT NULL
</select>
if(!serverImageList.isEmpty()) { // 서버에 이미지가 있을 경우
for(File serverImage : serverImageList) {
// File.getName() : 서버 파일 이름
// List.indexOf(객체) :
// List 에 객체가 존재하면 존재하는 index 반환
// 존재하지 않으면 -1 반환하는 메서드
if(dbImageList.indexOf(serverImage.getName()) == -1) {
log.info(serverImage.getName() + "삭제");
serverImage.delete(); // 파일 삭제
}
}
}