프로젝트/SUFY

[도착 알리미 SUFY] 6. 일정 시각마다 특정 작업 수행 로직 구현하기

kim-dev 2024. 2. 16. 22:02
반응형

 

이제 알림 정보를 DB에 저장하는 것까지 완료했다.
이제 실시간 지하철 위치 정보를 주기적으로 확인하면서, alarm 테이블에 저장된 칼럼들을 돌며 해당 목적지 역에 접근하는 열차가 있을 때마다 특정 작업(알림 전송)을 실행해주면 된다.

 

즉 우리와 같은 경우에는 15초 마다 한 번씩 REST API의 URI로 Http 요청을 보내서, 실시간 지하철 정보를 받아온 후 다음 로직을 작성하면 될 것...


스프링에서 주기적으로 특정 작업을 하기 위해서는, 해당 함수에 @Scheduled 어노테이션을 붙여주면 된다.

 

우선 메인 클래스에 @EnableScheduling 어노테이션을 붙여 주자.

< SubwayNotifyApplication.java >

@SpringBootApplication
@EnableScheduling
public class SubwayNotifyApplication {

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

}


이제 스프링으로 스케쥴링 하는 준비는 끝났다.
다음으로 @Scheduled 어노테이션을 사용하여 30초마다 Http 요청을 보내는 로직을 작성해보면 아래와 같다.

< AlarmService.java >

@Transactional
@Scheduled(fixedDelay = 15000) // 15초마다 아래 로직을 수행
public void sendAlarm() {
    // settedAlarm이 1 이상인 역들(알람이 하나 이상 설정된 역들)을 모두 찾는다.
    ArrayList<Station> stations = stationRepository.findAllBySettedAlarmGreaterThanEqual(1);

    stations.forEach(station -> {
        RestTemplate rt = new RestTemplate();

        String uri = "http://swopenAPI.seoul.go.kr/api/subway/" + HiddenData.subwayKey + "/json/realtimeStationArrival/0/10/" + station.getStationName();
        String response = rt.getForEntity(uri, String.class).getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode subways = objectMapper.readTree(response).get("realtimeArrivalList");
            for (JsonNode subway : subways) {
                // 해당 열차가 전역을 출발했을 경우
                if (subway.get("arvlCd").asInt() == 0 || subway.get("arvlCd").asInt() == 3) {
                    int subwayLine = subway.get("subwayId").asInt(); // 지하철 노선
                    int subwayNo = subway.get("btrainNo").asInt(); // 해당 열차 번호
                    String stationID = station.getStationId(); // 목적지 역 id
                    String goingRoute = subway.get("updnLine").asText(); // 상행 or 하행

                    // 해당 역, 열차를 가지고 설정된 알림이 있는지 검사해서 알림 전송
                    ArrayList<Alarm> alarms = alarmRepository.findAllBySubwayLineAndSubwayNoAndStationIDAndGoingRoute(subwayLine, subwayNo, stationID, goingRoute);
                    alarms.forEach(alarm -> {
                        // 여기서 알림 전송 후 해당 DB 칼럼을 삭제하면 될 것!
                    });
                }
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    });
}

 

내가 보기에도 되게 복잡하고 비효율적으로 보이는 코드이지만...
간결하게 만들고 싶은데 저 이상으로는 역부족 ㅜㅜ


여튼 이게 무슨 코드냐면

  1. DB의 station 테이블에서 settedAlarm이 1 이상인 역들을 찾는다.
    모든 역에 대해 전부 GET 요청을 보내서 확인하는 건 비효율적이니... 알림이 1개 이상 설정된 역만 대상으로 실시간 위치 정보를 확인하는 것!
  2. REST API로 GET요청을 보내 해당 역에 진입하는 열차(전역 출발 or 당역 진입)가 있는지 확인한다.
  3. 만약 진입하는 열차가 있다면, 해당 열차로 설정된 알림이 있는지 확인한다.
    이 때, 단순히 열차 번호로만 Alarm 객체를 find하는 게 아니라 가지고 있는 모든 정보를 이용해서 find 해야 한다.
    (사실 목적지 역 id와 열차 번호만 가지고 find해도 될 것 같긴 한데 혹시 내가 생각하지 못한 로직 오류가 있을 수도 있으니...)
  4. 알림 목록을 찾으면, 해당 목록의 모든 알림들에 대해 알림을 전송한 후 Alarm 정보를 DB에서 삭제한다.
    이건 아직 구현하지 못해서, 다음 포스트에서 구현하도록 하자!

대략 이런 로직이다.
아직 테스트는 제대로 못 해봤지만... 대강 테스트 했을 땐 얼추 예상한 로직대로 잘 동작하는 것 같다.

진짜 되게 짧은 포스트였는데... 이 짧은 코드 하나 짜느라고 수 시간을 허비했다...ㅋㅋ
오늘은 3일 연속 아침 출근 이슈 있었어서 여기까지 쓰고 내일 계속 써야지~