본문 바로가기
Spring

[스케줄러]

by moca7 2024. 11. 5.

 

 

 

ㅁ 사용자가 요청을 보내지 않아도 내가 원하는 시간에 내가 원하는 코드가 실행되게끔 등록할 수 있다.

- 생각보다 스케쥴러 기법도 많이 쓴다.

- 스프링에서도 제공하지만 오라클에서도 제공해서 쓸 수 있다. 우리는 스프링의 스케줄러를 써본다.

 

 

 

※ Scheduler

- 특정 날짜, 특정 시간, 일정 주기마다 어떤 작업을 묵시적으로 실행시켜주는 주체.

- 별도의 액션이 없어도 알아서 특정시간이 되면 자동 실행된다.

 

 

※ Spring Scheduler

 

(1) 매번 묵시적으로 실행시키고자 하는 작업들을 정의할 클래스를 만들기

- 그리고 그 클래스를 빈으로 등록한다. 빈등록은 3가지 방법이 있었다.

 

(2) 해당 클래스 내에 각 작업별 메소드를 작성하기

- 반환형은 반드시 void여야 하고, 매개변수는 없어야 한다. 메소드명은 상관없다.

- 메소드 위에 @Scheduled 어노테이션을 부여한다.

 

(3) servlet-context.xml에서 스케줄링을 사용하기 위해 task를 추가하기

 

 

 

 

※ Cron 표현식

- 작업을 실행시키고자 하는 날짜 및 시간 또는 일정주기를 지정할 때 사용하는 문법.

- 스프링에서만 쓰는 것은 아니다.

 

 

(1) 형식

- 초 분 시 일 월 요일 [연도]         //  연도만 생략 가능

 

 

(2) 각 자리별 표기가능한 값이 정해져있다.

- 초 : 0~59

- 분 : 0~59

- 시 : 0~23

- 일 : 1~31

- 월 : 0~11 (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC - 숫자 대신 영문도 가능)

- 요일 : 0~6 (SUN, MON, TUE, WED, THR, FRI, SAT)

- 연도 : 4자리 연도 생략 가능

 

 

(3) 각 형식에 작성 가능한 키워드도 있다

- ? : 설정값(특정값)을 지정하지 않을 때 사용 (일, 요일 자리에만 쓸 수 있다)

- * : 모든조건 (초, 분, 시, 일, 월)

- L : 마지막 날짜 (일, 요일 자리에만 쓸 수 있다)

- W : 가장 가까운 평일 (일) 

일 자리에 10W를 하면 10과 가까운 평일을 의미함.

10일이 평일이면 10일에 실행되고

10일이 토요일이면 9일에 실행된다.

10일이 일요일이면 11일에 실행된다.

- # : 몇주째인지 (요일 자리에만 쓸 수 있다) 

3#2면 수요일 2째주 (3이 수요일을 뜻한다)

- / : 주기(시작시간/단위)

분 자리에 0/10이면 0분부터 10분단위(간격)마다. 0분 10분 20분 ...

- - : 범위

시 자리에 1-3하면 1시 2시 3시

- , : 여러개 지정

 

 

(4) 예시

- 크론 표현식을 만들어주는 사이트도 있는데 스프링이랑은 조금 달라서 실행이 안될 수도 있다.

- 0 0 * * * * : 매일 모든 시간마다

- 0 0 5 * * * : 매일 5시마다(24시간 표기라 오후 5시가 아닌 새벽 5시를 뜻한다.)

- 0 0 3-5 * * * : 매일 3, 4, 5시마다

- 0 0 4,5 * * * : 매일 4,5시마다

- 0 1/30 3-5 * * * : 매일 3:01, 3:31, 4:01, 4:31, 5:01, 5:31마다

- 0 0 12 * * MON-FRI : 평일(월, 화 ,수, 목, 금) 12시마다

 

 

 

 

 

ㅁ com.br.spring.scheduler 패키지 생성

 

 

ㅁ com.br.spring.scheduler에 BoardScheduler (일반) 클래스 생성

 

 
package com.br.spring.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.br.spring.service.BoardService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j // 로그쓰게 @Slf4j
@RequiredArgsConstructor // final 필드에 대한 매개변수 생성자가 만들어지고, 매개변수 생성자 주입에 의해서 생성된 객체가 boardService에 대입된다.
@Component
public class BoardScheduler {
 
  private final BoardService boardService;
 
 

  @Scheduled(cron="0 10 11 * * *") // 매일 오전 11시 10분마다 로그 출력 
  public void esecute1() { // 반환형은 반드시 void, 매개변수 반드시 X,  메소드명은 마음대로.
   
    log.debug("매일 오전 11시 10분마다 실행됨");
  }
 
 
  @Scheduled(cron="0 0/1 * * * *") // 1분마다 실행
  public void execute2() {
    log.debug("1분마다 매번 실행됨");
  }
 
 
  @Scheduled(cron="0 0 0/1 * * *") // 1시간마다 실행
  public void execute3() {
    log.debug("1시간 마다 매번 실행됨");
  }
 
 
 
  // 통계 정보를 위해 매일 밤 12시에 현재 게시글의 총 갯수를 로그로 기록을 남기는 스케줄러가 필요하다고 가정한다.
  @Scheduled(cron="0 0 0 * * *") // 매일 밤 12시마다 실행
  public void execute4() {
    log.debug("현재 게시글의 총 갯수: {}", boardService.selectBoardListCount());
  }
  // 지금은 개발서버고 나중에 실서버에 배포하면 실서버는 24시간 계속 돌아간다.
 
 
}
 

 

 

 

 

ㅁ servlet-context.xml

 

 
<?xml version="1.0" encoding="UTF-8"?>

  <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
 
  <!-- Enables the Spring MVC @Controller programming model -->
  <annotation-driven />

  <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
  <resources mapping="/resources/**" location="/resources/" />
  <resources mapping="/upload/**" location="file:///upload/" />

  <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
  <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
  </beans:bean>
 
  <context:component-scan base-package="com.br.spring" />
 
 
  <!-- webSocket 관련 등록 구문 -->
  <beans:bean class="com.br.spring.handler.ChatEchoHandler" id="chatEchoHandler" />
  <websocket:handlers>
    <websocket:mapping handler="chatEchoHandler" path="/chat" />
    <websocket:handshake-interceptors>
      <beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor" />
    </websocket:handshake-interceptors>
    <websocket:sockjs />
  </websocket:handlers>
 
 
  <!-- interceptor 관련 등록 구문 -->
  <interceptors>
    <interceptor>
      <mapping path="/member/myinfo.do" />
      <mapping path="/board/regist.do" />
           
      <beans:bean class="com.br.spring.interceptor.LoginCheckInterceptor" id="LoginCheckInterceptor"/>
    </interceptor>
   
    <!--
    <interceptor>
   
    </interceptor>
    -->
  </interceptors>
 
 
  <!-- Scheduler -->
  <task:annotation-driven />
 
</beans:beans>
 

 

 

 

 

ㅁ 서버 start

 

 

 

- 오른쪽 아래 시간을 클릭해서 "날짜 및 시간 조정" - "자동"에서 "수동"으로 현재 시간을 11시 58분으로 설정하고 서버를 스타트했다. 12시가 되자마자 저렇게 뜬다.

 

 

 

 

 

 

=====================================================================================

 

 

 

 

 

ㅁ status가 n인 댓글을 스케줄러로 자동으로 delete 해보기

 

 

- REPLY 테이블에가서 저 두 댓글을 N으로 바꿨다. (삭제된 댓글이라고 가정)

- 일요일 새벽 1시마다 REPLY 테이블에서 상태가 N인 댓글을 삭제한다.

- 현재는 댓글을 DELETE하는 서비스와 쿼리가 없다. 만든다.

 

 

 

 

ㅁ board-mapper.xml

 

 

 

 

 

ㅁ BoardDao

 

 

 

 

 

ㅁ BoardService

 

 

 

 

 

ㅁ BoardServiceImpl

 

 

 

 

 

ㅁ BoardScheduler

 

 
package com.br.spring.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.br.spring.service.BoardService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor // final 필드에 대한 매개변수 생성자가 만들어지고, 매개변수 생성자 주입에 의해서 생성된 객체가 boardService에 대입된다.
@Component
public class BoardScheduler {
 
  private final BoardService boardService;
 
 

  @Scheduled(cron="0 10 11 * * *") // 매일 오전 11시 10분마다 로그 출력 (로그쓰게 @Slf4j)
  public void esecute1() { // 반환형은 void, 메소드명은 마음대로
   
    log.debug("매일 오전 11시 10분마다 실행됨");
  }
 
 
  @Scheduled(cron="0 0/1 * * * *") // 1분마다 실행
  public void execute2() {
    log.debug("1분마다 매번 실행됨");
  }
 
 
  @Scheduled(cron="0 0 0/1 * * *") // 1분마다 실행
  public void execute3() {
    log.debug("1시간 마다 매번 실행됨");
  }
 
 
 
  // 통계 정보를 위해 매일 밤 12시에 현재 게시글의 총 갯수를 로그로 기록을 남기는 스케줄러가 필요하다고 가정한다.
  @Scheduled(cron="0 0 0 * * *") // 매일 밤 12시마다 실행
  public void execute4() {
    log.debug("현재 게시글의 총 갯수: {}", boardService.selectBoardListCount());
  }
  // 지금은 개발서버고 나중에 실서버에 배포하면 실서버는 24시간 계속 돌아간다.
 
 
  // 일요일 새벽 1시마다 현재 status가 N인 댓글을 완벽히 delete하는 스케줄러가 필요하다고 가정한다.
  @Scheduled(cron="0 0 1 * * SUN")
  public void execute5() {
    int result = boardService.deleteReplyCompletely();
    log.debug("현재 완전 삭제된 댓글 갯수: {}", result);
  }
 
}
 

 

 

 

 

 

ㅁ 서버 start

 

 

- 일요일 새벽 1시가 아닌 지금 되는지 확인하려고 크론 표현식으로 11시 38분 30초에 되도록 했다. 

 

 

 

 

- 실제 db에서도 delete 되었다.

 

 

 

ㅁ 굳이 관리자가 일일이 연차를 지급할 필요 없이, 스케줄러로 매년 1월 1일이 되는 순간 조회해와서 연차를 주게끔 할 수 있다.

- 근태관리도 특정 시간이되었는데 출근/퇴근을 안찍으면 자동으로 출근/퇴근되게끔 할 수 있다.

 

 

 

'Spring' 카테고리의 다른 글

[스프링 부트] 정적자원 디렉토리 등록하기 (WebMvcConfigurer)  (0) 2024.11.05
[스프링 부트] migration  (2) 2024.11.05
[인터셉터]  (0) 2024.11.05
[웹소켓] 2.  (2) 2024.11.04
[웹소켓] 1.  (0) 2024.11.04