Project/AutoSchedule

9일차 - 실시간 스케줄 브로드캐스트 구현

sowon02 2025. 11. 11. 17:59

어제는 WebSocket/STOMP 기반의 실시간 협업 인프라를 구축했다.
오늘은 그 연장선으로, AI 스케줄 최적화 결과를 실시간으로 브로드캐스트하는 기능을 구현했다.

기존 구조에서는 진행률만 실시간으로 전송되고, 최종 스케줄 결과는 프론트가 별도로 REST API를 호출해야 했다.

이 방식의 문제는 다음과 같았다.

  • AI 최적화 완료 시점을 프론트가 알 수 없음
  • API 응답 타이밍 차이로 화면이 늦게 갱신됨
  • 다른 도메인(Task, Calendar, Notification)과의 실시간 흐름이 불일치

결국 “진행률은 실시간인데 결과는 수동 갱신”이라는 어색한 구조가 됐다. 그래서 오늘은 최종 스케줄까지 자동으로 push하는 브로드캐스터 로직을 추가했다.


1. 기존 구조

기존 ScheduleOptimizationService에서는
/topic/schedules/{teamId}로 ScheduleProgressMessage(진행률) 만 발행했다.

messagingTemplate.convertAndSend("/topic/schedules/" + teamId, progressMessage);

이 채널을 통해 프론트는 실시간 진행률을 확인할 수 있었지만,
최종 스케줄(ScheduleResponse)은 직접 /api/schedules/{teamId}를 다시 호출해야 했다.


2. 개선 방향

다른 도메인(Task, Calendar, Notification 등)은 모두 CollaborationEventPublisher를 통해 WebSocket으로 실시간 브로드캐스트되고 있다. 그래서 스케줄도 동일한 흐름에 통합했다.
즉, 최적화 완료 시점에 CollaborationEventPublisher가 최종 스케줄 데이터를 /topic/schedule.{teamId}로 방송하도록 했다.

이렇게 하면

  • 프론트가 별도 API 호출 없이 즉시 결과를 수신
  • 팀 대시보드/캘린더 화면이 자동으로 갱신
  • 실시간 메시징 구조 전반이 일관성 유지

4. 구현 내용

1) CollaborationEventPublisher 확장

@Component
@RequiredArgsConstructor
public class CollaborationEventPublisher {
    private final SimpMessagingTemplate messagingTemplate;

    // 기존: tasks, calendar, notifications, schedules 등
    // 추가: 최종 스케줄 브로드캐스트
    public void publishScheduleResult(Long teamId, ScheduleResponse response) {
        messagingTemplate.convertAndSend("/topic/schedule." + teamId, response);
    }
}

2) ScheduleOptimizationService 연동

@Service
@RequiredArgsConstructor
public class ScheduleOptimizationService {

    private final CollaborationEventPublisher eventPublisher;

    public void optimize(Long teamId) {
        // 1. 진행률 메시지 전송
        sendProgress(teamId, 50);

        // 2. AI 최적화 로직 수행
        ScheduleResponse finalResult = scheduleEngine.run(teamId);

        // 3. 최적화 완료 시점에 결과 브로드캐스트
        eventPublisher.publishScheduleResult(teamId, finalResult);
    }
}

3) 프론트 구독 로직 추가

// /topic/schedule.{teamId} 채널 구독
client.subscribe(`/topic/schedule.${teamId}`, (msg) => {
  const data = JSON.parse(msg.body);
  updateCalendarUI(data);
});

이제 팀원 모두가 같은 팀 ID로 구독만 하고 있으면,
AI가 스케줄을 완성하는 즉시 화면이 자동으로 갱신된다. 


끝 ㅠ