
FitPass의 스케줄러는 자동화 된 비즈니스 로직 처리를 담당하는 핵심 기능이다. 주로 만료된 목표들의 상태를 자동으로 업데이트하여 시스템의 데이터 일관성을 유지함.
패키지 구조
src/main/java/org/example/fitpass/domain/fitnessGoal/
└── scheduler/
└── FitnessGoalScheduler.java
의존성 관계
FitnessGoalScheduler → FitnessGoalService → FitnessGoal Entity
↓
FitnessGoalRepository
메인 스케줄러 클래스
@Component
@RequiredArgsConstructor
@Slf4j
public class FitnessGoalScheduler {
private final FitnessGoalService fitnessGoalService;
// 매일 자정에 만료된 목표들 상태 업데이트
@Scheduled(cron = "0 0 0 * * *")
public void updateExpiredGoals() {
log.info("만료된 피트니스 목표들 상태 업데이트 시작");
try {
fitnessGoalService.updateExpiredGoals();
log.info("만료된 피트니스 목표들 상태 업데이트 완료");
} catch (Exception e) {
log.error("만료된 피트니스 목표들 상태 업데이트 중 오류 발생", e);
}
}
}
Cron 표현식 :
@Scheduled(cron = "0 0 0 * * *")
// 초 분 시 일 월 요일
// 0 0 0 * * *
0초 0분 0시(자정) 일(매일) 월(매월) *요일(모든요일)
결과 : 매일 자정 00:00:000에 실행
예외 에러 핸들링
서비스 레이어 처리 로직
@Service
public class FitnessGoalService {
// 활성 목표들의 만료 상태 일괄 업데이트 (스케줄러용)
@Transactional
public void updateExpiredGoals() {
List<FitnessGoal> activeGoals = fitnessGoalRepository.findByGoalStatus(GoalStatus.ACTIVE);
activeGoals.forEach(FitnessGoal::checkAndUpdateExpiredStatus);
}
}
엔티티 레벨 비즈니스 로직
@Entity
public class FitnessGoal {
// 목표 만료 체크 및 상태 업데이트
public void checkAndUpdateExpiredStatus() {
if (goalStatus == GoalStatus.ACTIVE && LocalDate.now().isAfter(endDate)) {
this.goalStatus = GoalStatus.EXPIRED;
log.info("목표 만료 처리: Goal ID {}, 종료일 {}", this.id, this.endDate);
}
}
}
실행 흐름
graph TD
A[매일 자정 00:00] --> B[FitnessGoalScheduler.updateExpiredGoals()]
B --> C[FitnessGoalService.updateExpiredGoals()]
C --> D[ACTIVE 상태 목표들 조회]
D --> E[각 목표별 만료 상태 체크]
E --> F{종료일 < 오늘?}
F -->|Yes| G[상태를 EXPIRED로 변경]
F -->|No| H[상태 유지]
G --> I[로그 기록]
H --> I
I --> J[완료]
FitPass는 실시간 상태 체크 + 스케줄러 처리를 동시에 사용한다.
// 내 목표 목록 조회 (만료 상태 체크 포함)
public List<FitnessGoalListResponseDto> getMyGoals(Long userId) {
List<FitnessGoal> goals = fitnessGoalRepository.findByUserIdOrderByCreatedAtDesc(userId);
// 만료 상태 체크 및 업데이트
goals.forEach(FitnessGoal::checkAndUpdateExpiredStatus);
return goals.stream().map(FitnessGoalListResponseDto::from).collect(Collectors.toList());
}
// 목표 상세 조회 (만료 상태 체크 포함)
public FitnessGoalResponseDto getGoal(Long userId, Long goalId) {
FitnessGoal fitnessGoal = fitnessGoalRepository.findByIdAndUserIdOrElseThrow(goalId, userId);
// 만료 상태 체크 및 업데이트
fitnessGoal.checkAndUpdateExpiredStatus();
return FitnessGoalResponseDto.from(fitnessGoal);
}
내 목표를 조회 시 실시간으로 상태를 체크하는 메서드
checkAndUpdateExpiredStatus()를 사용한다.
즉, 스케줄러와 실시간 상태를 체크를 둘 다하여 즉시성과 효율성을 모두 확보하고, 안정적으로 구현.
스케줄러는 더 확장이 가능하도록 만듦.