▶ Scheduler
: 매일, 매분, 매초, 매주, 매달, .. 등 반복적으로 실행시켜야 하는 작업(프로세스)가 있는 경우 스프링 스케쥴러를 사용하면 간편하게 셋팅 가능하다.
▶ Spring Scheduler 작업순서
1. xml파일 만들고 task, context 스키마를 등록(하단 namespace탭을 이용)
2. 스케쥴링 관련 annotation 활성화 <task:annotation-driven/>
3. 스케쥴러로 사용할 클래스들을 bean객체로 등록
4. web.xml에 프로그램 구동시 현재 xml이 읽혀지도록 등록 -> *-context.xml로 등록
5. 스케쥴링을 원하는 메서드에서 schedule어노테이션 추가(#2가 되어있어야 함) -> ScheduleController.java에 추가함
// scheduler-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- #2. -->
<task:annotation-driven scheduler="khScheduler" />
<!-- <task:scheduler id="khScheduler"/>의 id값을 넣어줌 -->
<!-- #3. -->
<context:component-scan base-package="com.kh.spring.common.scheduling" />
<task:scheduler id="khScheduler" pool-size="10"/>
<!-- pool-size : 쓰레드풀 개수 지정(기본값1) -->
<task:scheduled-tasks scheduler="khScheduler">
<task:scheduled ref="scheduleController" method="testCron" cron="1 * * * * *"/>
<!--
ref : bean 클래스의 변수명(Beans Graph)
method : bean클래스의 스케쥴링할 메서드명
cron :
* * * * * * (*)
초 분 시 일 월 요일 년도(생략가능)(7개)
* : 모든 값(매시 매분 매주 매초)
? : 어떤 값이든 상관없다.
- : 범위지정(1-2초) 1에서 2초
, : 여러 값을 지정(1, 5) 1초랑 5초
/ : 증분 값 (0/2) 0초부터 매 2초마다 (증가치 설정가능)
L : 지정할 수 있는 범위의 마지막 값(매달 말일)
W : 가장 가까운 "평일"을 설정할 때
# : N번째 특정 요일을 설정할 때
cron="1 * * * * *" : 요일 매월 매일 매시 매분 1초에 실행
ex)매시간 30분에 특정 작업을 실행시키고 싶다?
0 30 * * * * (매월 매일 매시
ex)매일 오전 1시에 실행시키고 싶다?
0 0 1 * * * *
-->
</task:scheduled-tasks>
</beans>
// FileDeleteScheduler.java
/*
Board테이블과 BoardImg테이블 안에 있는 이미지 목록들을 모두 조회한 후
resources/images 디렉토리 안에 있는 이미지, 파일들과 대조하여
하나도 일치하지 않는 이미지 파일들을 삭제하기
-> DB에 존재하지 않는 파일이 images안에 존재하는 경우
매달 1일 정시에 딱 한 번만 실행되도록 크론표현식으로 작성하시오.
(테스트를 위해 5초간격으로 설정한 후 변경해줄 것)
*/
@Slf4j
@Component
public class FileDeleteScheduler {
@Autowired
private ServletContext application; //정적파일들의 경로를 얻어오기 위해 추가
@Autowired
private BoardService boardService;
@Scheduled(cron="0 0 1 * * *")
public void deleteFile() {
//1) board_img안에 있는 모든 파일 목록 조회
List<String> list = boardService.selectFileList();
log.info("list ? {}", list);
List<BoardType> boardTypeList = boardService.selectBoardTypeList();
for(BoardType bt : boardTypeList) {
// 2) resources/images/board/T/ 에 있는 모든 이미지 파일목록 조회
File path = new File(application.getRealPath("/resources/images/board/"+bt.getBoardCd()+"/"));
File[] files = path.listFiles();
// 현재 디렉토리 안에 존재하는 모든 파일을 배열형태로 가져오기
List<File> fileList = Arrays.asList(files); //배열을 컬렉션 형태로 반환
int count = 0;
if(!list.isEmpty()) {
for(File serverFile : fileList) {
String fileName = serverFile.getName(); // 파일명 반환
fileName = "/resources/images/board/"+bt.getBoardCd()+"/"+fileName;
// List.indexOf(value) : List안에 value와 같은 값이 있으면 해당 값의 인덱스 위치를 반환해주는 함수
if(list.indexOf(fileName) == -1) {
// select한 DB목록에 존재하지 않지만 웹서버상에 저장된 파일인 경우.
// -> 사용하고 있지 않는 파일로 간주
log.info(fileName+"을 삭제합니다");
count++;
serverFile.delete();
}
}
}
log.info("총 {}개의 파일이 삭제됨", count);
log.info("파일삭제 스케쥴러 종료");
}
}
}
// board-mapper.xml
<!-- 사용중인 이미지 리스트 -->
<select id="selectFileList" resultType="string">
SELECT
'/resources/images/board/T/' || CHANGE_NAME
FROM BOARD_IMG
UNION ALL
SELECT
'/resources/images/board/' || BOARD_CD || '/' || CHANGE_NAME
FROM BOARD
WHERE CHANGE_NAME IS NOT NULL
</select>
<!-- selectBoardTypeList -->
<select id="selectBoardTypeList" resultType="boardType">
SELECT *
FROM BOARD_TYPE
ORDER BY BOARD_CD
</select>