Spring Framework 스케줄링(19) - Day 66

jaehoon ahn·2025년 12월 22일

Spring boot

목록 보기
17/20
post-thumbnail

이미지 삭제 스케줄링

DB

SELECT SUBSTR(PROFILE_IMG, INSTR(PROFILE_IMG, '/', -1) + 1) "rename"
FROM "MEMBER"
-- -1이면 맨 뒤부터 탐색 시작
UNION
SELECT CAST(IMG_RENAME AS VARCHAR2(300)) "rename"
FROM "BOARD_IMG";
-- SQL Error [12704] [72000]: ORA-12704: 문자 집합이 일치하지 않습니다
-- 둘의 데이터 타입이 다르기 때문에 발생하는 에러
-- CAST는 타입 바꾸는 함수

@Scheduled

/*
* @Scheduled
*
* * Spring에서 제공하는 스케줄러 : 시간에 따른 특정 작업(Job)의 순서를 지정하는 방법.
*
* 설정 방법
* 1) XXXAPPlication.java 파일에 @EnableScheduling 어노테이션 추가
* 2) 스케쥴링 동작을 위한 클래스 작성
*
*
* @Scheduled 속성
*  - fixedDelay : 이전 작업이 끝난 시점으로 부터 고정된 시간(ms)을 설정.
*    @Scheduled(fixedDelay = 10000) // 이전 작업이 끝난 후 10초 뒤에 실행
*  
*  - fixedRate : 이전 작업이 수행되기 시작한 시점으로 부터 고정된 시간(ms)을 설정.
*    @Scheduled(fixedRate  = 10000) // 이전 작업이 시작된 후 10초 뒤에 실행
* 
*  
*  
* * cron 속성 : UNIX계열 잡 스케쥴러 표현식으로 작성 - cron="초 분 시 일 월 요일 [년도]" - 요일 : 1(SUN) ~ 7(SAT)
* ex) 2019년 9월 16일 월요일 10시 30분 20초 cron="20 30 10 16 9 2 " // 연도 생략 가능
*
*
*  @Scheduled(cron = "* * * * * 30")
*  @Scheduled(cron = "0 0 12 * * *")
*
*
* - 특수문자
* * : 모든 수.
* - : 두 수 사이의 값. ex) 10-15 -> 10이상 15이하
* , : 특정 값 지정. ex) 3,4,7 -> 3,4,7 지정
* / : 값의 증가. ex) 0/5 -> 0부터 시작하여 5마다
* ? : 특별한 값이 없음. (월, 요일만 해당)
* L : 마지막. (월, 요일만 해당)
* @Scheduled(cron="0 * * * * *") // 모든 0초 마다 -> 매 분마다 실행
*
*/
  • 이전 작업이 얼마나 긴 작업이, 짧은 작업인지 알 수 없기 떄문에 fixedDelay, fixedRate는 이전 작업이 끝난 시작이기 때문에 언제 시작할지, 달라질 수 있기 때문에 문제가 발생함
    • 완벽한 스케줄러로써 동작하기에는 어려움이 있음
  • cron
    
    *  @Scheduled(cron = "* * * * * 30")
    *  @Scheduled(cron = "0 0 12 * * *")
    • ex)
      • 2019년 9월 16일 월요일 10시 30분 20초
        • cron="20 30 10 16 9 2(월요일) " // 연도 생략 가능

BoardProjectApplication.java

package edu.kh.project;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling // 스프링 스케줄러를 이용하기 위한 활성화 어노테이션
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) 
// Spring Security에서 기본제공하는 로그인 페이지를 이용 안하겠다
public class BoardProjectApplication {

	public static void main(String[] args) {
		SpringApplication.run(BoardProjectApplication.class, args);
	}

}
  • @EnableScheduling 추가

ImageDeleteController

@Component // IOC 관련 : Component Scan을 통해서 Bean 등록
@Slf4j
@PropertySource("classpath:/" + "config.properties")
@RequiredArgsConstructor

public class ImageDeleteScheduling {

	private final BoardService service;

	@Value("${my.profile.folder-path}")
	private String profileFolderPath; // C:/uploadFiles/profile

	@Value("${my.board.folder-path}")
	private String boardFolderPath; // C:/uploadFiles/board/
	
	// 매분 0초, 30초마다 수행하겠다는 것 
	// 시계 초 단위가 0, 30 인 경우 수행
	// cron="초 분 시 일 월 요일 [년도]" 
	// @Scheduled(cron = "0,30 * * * * *")
	// @Scheduled(cron = "0 0 * * * *") // 매 시간마다 수행
	// @Scheduled(cron = "0 0 0 * * *") // 자정
	// @Scheduled(cron = "0 0 12 * * *") // 정오
	// @Scheduled(cron = "0 0 0 1 * *") // 매달 1일
	@Scheduled(cron = "0,30 * * * * *")
	public void scheduling() {
		log.info("스케줄러 동작!");
		// DB와 서버 폴더의 파일 목록 비교 후
		// DB에 없는 서버 이미지 파일 삭제 동작

		// 1. 서버 폴더의 파일 목록을 조회
		File boardFolder = new File(boardFolderPath);
		File memberFolder = new File(profileFolderPath);

		// 참조하는 폴더에 존재하는 파일 목록을 얻어오기
		File[] boardArr = boardFolder.listFiles();
		// 여기서 말하는 list는 배열을 의미하는 것
		File[] memberArr = memberFolder.listFiles();

		// 두 배열을 하나로 합침
		// imageArr 라는 빈 배열을 boardArr과 memberArr의 길이만큼의 크기로 만들기
		File[] imageArr = new File[boardArr.length + memberArr.length];

		// 배열 내용 복사(깊은 복사)
		// System.arraycopy(복사할배열, 몇번인덱스부터복사할지,
		// 새로운배열, 새로운배열의몇번부터넣을지인덱스번호,복사를어디까지할건지)

		System.arraycopy(memberArr, 0, imageArr, 0, memberArr.length);
		System.arraycopy(boardArr, 0, imageArr, memberArr.length, boardArr.length);

		// 배열 -> List 변환(다루기 쉽도록)
		// asList -> List로 변환해주는 메서드
		List<File> serverImageList = Arrays.asList(imageArr);

		// 2. DB에 있는 이미지 파일 이름 부분만 모두 조회할 것
		List<String> dbImageList = service.selectDbImageList();

		// 3. 서버, DB 이미지 파일명을 비교하여
		if (!serverImageList.isEmpty()) {
			// 서버에 이미지가 있을 경우
			for (File serverImage : serverImageList) {
				// File.getName() : 서버 파일 이름
				// List.indexOf(객체) :
				// List에 전달한 객체가 존재하면 존재하는 index 번호 반환
				// 존재하지 않는다면 -1을 반환
				if (dbImageList.indexOf(serverImage.getName()) == -1) {
					// DB에는 없는데 서버에는 있는 것
					serverImage.delete(); // 파일 삭제 메서드
					log.info(serverImage.getName() + " 삭제");
				}
			}
		}
	}

0개의 댓글