스케줄러 시스템 기술적 의사결정 - fitpassTeam/fitpass GitHub Wiki

스케줄러 시스템 기술적 의사결정

문제 : 시간 기반 비즈니스 로직의 수동 관리 한계

스케줄러 도입 배경

  • 수동 상태 관리의 한계: 예약 완료, 멤버십 만료, 목표 기간 종료 등을 관리자가 직접 처리해야 하는 비효율성
  • 사용자 경험 저하: 만료 알림 부재로 갑작스러운 서비스 중단 경험
  • 데이터 일관성 문제: 시간이 지났음에도 상태가 업데이트되지 않는 레거시 데이터 누적
  • 운영 비용 증가: 반복적인 수동 작업으로 인한 인력 낭비

의사결정 배경 및 요구사항

비즈니스 요구사항

  • 자동화: 시간 기반 상태 전이를 시스템이 자동 처리
  • 사용자 경험 향상: 단계적 알림으로 갑작스러운 서비스 중단 방지
  • 운영 효율성: 수동 관리 업무 최소화

기술적 요구사항

  • 실시간성: 중요한 상태 변경은 즉시 반영
  • 안정성: 배치 작업 실패가 메인 서비스에 영향을 주지 않아야 함
  • 확장성: 새로운 도메인의 자동화 요구사항을 쉽게 추가할 수 있어야 함

Spring Scheduler + 실시간

// 배치 처리 + 실시간 처리 조합
@Scheduled(cron = "0 0 0 * * *")// 백그라운드 일괄 처리
+
goal.checkAndUpdateExpiredStatus();// 조회 시 실시간 체크

장점: 성능 + 정확성 균형

단점: 구현 복잡도 약간 증가

선택한 이유 :

"배치 처리로 효율성을 확보하고, 실시간 체크로 정확성을 보장하는 이중 안전장치”

시스템 플로우

image

핵심 구현 설계 결정사항

  1. 도메인별 스케줄러 분리
// 도메인별 책임 분리로 유지보수성 향상
ReservationScheduler    → 예약 관련 자동화
MembershipScheduler     → 멤버십 관련 자동화  
FitnessGoalScheduler   → 목표 관련 자동화

의사결정 이유: 단일 책임 원칙, 코드 가독성, 확장 용이성

  1. 점진적 정보 수집 전략
// 시스템 부하 분산을 위한 시간대별 분리
00:00 (자정)    → 목표 만료 처리 (하루 단위 정리)
01:00 (새벽)    → 장기 대기 예약 취소 (정리 작업)
06:00 (새벽)    → 멤버십 자동 활성화 (사용 전 준비)
09:00 (오전)    → 만료 알림 발송 (사용자 활동 시간)
매시간 00분     → 예약 상태 변경 + 2시간 전 알림

의사결정 이유:

  • 서버 리소스 집중 방지
  • 사용자 알림 최적 시간대 고려
  • 비즈니스 로직에 맞는 처리 순서
  1. 하이브리드 상태 관리 패턴
// 실시간 + 배치 처리 조합
@Entity
public class FitnessGoal {
    // 실시간 체크 (조회 시마다)
    public void checkAndUpdateExpiredStatus() {
        if (goalStatus == GoalStatus.ACTIVE && LocalDate.now().isAfter(endDate)) {
            this.goalStatus = GoalStatus.EXPIRED;
        }
    }
}

@Scheduled(cron = "0 0 0 * * *") // 배치 처리 (백그라운드)
public void updateExpiredGoals() {
    List<FitnessGoal> activeGoals = repository.findByGoalStatus(ACTIVE);
    activeGoals.forEach(FitnessGoal::checkAndUpdateExpiredStatus);
}

의사결정 이유: 즉시성, 효율성, 안정성

  1. 사용자 경험 최적화 알림 전략
// 단계적 알림으로 사용자 이탈 방지
멤버십 만료: 3일 전 → 1일 전 → 당일
예약 알림: 하루 전 → 2시간 전
목표 처리: 자정 일괄 (사용자 수면 시간)

의사결정 이유: 사용자 준비 시간 확보, 이탈률 감소

구현 성과 및 효과

자동화된 비즈니스 프로세스

image

// Before: 수동 관리
- 관리자가 직접 상태 변경
- 사용자 문의 증가
- 데이터 불일치 가능성

// After: 자동화
- 무인 상태 관리
- 사용자 만족도 향상
- 데이터 일관성 보장

실제 자동화 현황

도메인 작업 내용 실행 주기 기대 효과
예약 만료된 예약 → 자동 완료 처리 매시간 100% 자동 상태 관리
예약 24시간 대기 예약 → 자동 취소 매일 새벽 1시 장기 대기 해소
예약 예약 2시간 전 알림 발송 매시간 No-show 30% 감소
멤버십 3단계 만료 알림 전송 매일 오전 9시 갱신률 25% 향상
멤버십 기간 만료 후 자동 비활성화 매일 오전 6시 사용자 편의성 증대
목표 만료된 목표 자동 정리 매일 자정 데이터 일관성 확보

안정성 보장 설계

1. 실패 격리

// 각 스케줄러는 독립적으로 동작
ReservationScheduler 실패 → MembershipScheduler 정상 동작

예약 스케줄러가 무너져도 다른 스케줄러는 살아있기에 독립적으로 동작한다.

2. 멱등성 보장

// 같은 작업을 여러 번 실행해도 결과 동일
if (goalStatus == GoalStatus.ACTIVE && LocalDate.now().isAfter(endDate)) {
    this.goalStatus = GoalStatus.EXPIRED; // 이미 EXPIRED면 변경 없음
}

3. 상세 로깅 및 모니터링

log.info("만료된 예약 {}개를 COMPLETED로 변경합니다.", expiredReservations.size());
log.info("사용자 ID: {}, 멤버십: {} 자동 활성화 완료", userId, membershipName);

https://velog.io/@todok0317/FitPass-스케줄러-시스템-기술적-의사결정