// Links to the libraries needed

image

출처: https://xkcd.com/399/

문제 상황

  • 현재 프로젝트에서는 일주일 이내의 단기 일정에 대한 추천을 AI 서버에서 받아오고 있습니다.
  • AI 서버에서는 출발지를 기준으로 설정된 클러스터 내에서 사용자 선호도에 적합한 여행지들을 뽑아서 보내주고 있습니다.
    • 만약 AI 서버에서 3일치의 결과를 뽑는다면 클러스터 내에서 가장 만족스러운 결과를 1일 당 5개의 장소씩 총 15개를 뽑아서 주고 있습니다.
    • AI 서버에서는 지정한 클러스터 내에서 만족하는 결과가 원하는 개수만큼 안나올 수도 있기 때문에 클러스터를 넓게 지정했지만 결과가 너무 넓게 나오고 있습니다.
  • 하지만 이는 사용자 경로가 고려되지 않고 있기 때문에 경로를 최적화 해줘야 사용자 경험이 올라갈 수 있다고 생각하여 이를 최적화해보려 합니다!

문제 상황 확인

시각화

  • 현재의 결과 화면

image

  • 맵에 마커 표시

image

  • 2번과 3번의 거리가 가장 먼 것을 확인할 수 있음

image

  • 해당 결과를 보면 거리가 전혀 고려되지 않고 있다는 것을 확인할 수 있습니다.
  • 클러스터가 너무 넓어서 위와 같은 결과가 나오지만 이는 AI 추천 서버에서 조정할 문제이고 저는 우선 최단 경로를 짜주려고 합니다!

문제 해결 아이디어

1. 노드 간 거리 구하기

Google Distance Matrix API를 사용하여 위치 간 거리 계산하기

  • 우선 Google Distance Matrix API를 사용하여 두 노드 간의 거리를 계산해보고 데이터의 신뢰성을 확인해보았습니다!

  • 코드 구성

private int[][] computeDistanceMatrix(List<PlaceDTO> places) {
    int n = places.size();
    int[][] dist = new int[n][n];
    
    // 각 경유지 간의 거리 계산
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i != j) {
                dist[i][j] = (int) getDistanceBetweenPlaces(places.get(i), places.get(j));  // 거리 계산
                count += 1;
            } else {
                dist[i][j] = 0; // 동일한 노드 간의 거리는 0
            }
        }
    }
    
    return dist;
}

public float getDistanceBetweenPlaces(PlaceDTO place1, PlaceDTO place2) {
    String origins = place1.getLatitude() + "," + place1.getLongitude();
    String destinations = place2.getLatitude() + "," + place2.getLongitude();

    String url = "https://maps.googleapis.com/maps/api/distancematrix/json?"
            + "units=metric&mode=transit"
            + "&origins=" + origins
            + "&destinations=" + destinations
            + "&region=KR"
            + "&key=" + googleApiKey;

    RestTemplate restTemplate = new RestTemplate();
    Map<String, Object> response = restTemplate.getForObject(url, Map.class);

    // 응답에서 거리 값을 추출, Null 체크 포함
    List<Map<String, Object>> rows = (List<Map<String, Object>>) response.get("rows");
    if (rows != null && !rows.isEmpty()) {
        Map<String, Object> row = rows.get(0);
        List<Map<String, Object>> elements = (List<Map<String, Object>>) row.get("elements");
        if (elements != null && !elements.isEmpty()) {
            Map<String, Object> element = elements.get(0);
            Map<String, Object> distance = (Map<String, Object>) element.get("distance");
            if (distance != null && distance.get("value") != null) {
                return ((Number) distance.get("value")).floatValue(); // 거리 값 (미터 단위)
            } else {
                log.error("Distance is null for the request between " + place1.getName() + " and " + place2.getName());
                log.info(place1.toString());
                log.info(place2.toString());
            }
        }
    }

    return -1; // 거리를 계산할 수 없을 때
}
{destination_addresses=[108 Makgye-dong, Gwacheon-si, Gyeonggi-do, South Korea], origin_addresses=[495-86 Sangam-dong, Mapo-gu, Seoul, South Korea], rows=[{elements=[{distance={text=24.3 km, value=24264}, duration={text=1 hour 16 mins, value=4561}, status=OK}]}], status=OK}
Distance between 난지한강공원 and 서울랜드: 24264.0 meters

image

  • 난지 한강 공원과 서울 랜드 사이의 거리는 24.3km, 대중교통 이용 시 소요 시간은 1시간 16분으로 나왔습니다.
  • 내가 자주 사용하는 네이버 지도에서 더블 체크해보았습니다.
  • 대중교통 이용 시 소요시간과 유사하게 나왔습니다.

image

  • 이로써 데이터의 신뢰성은 확보했습니다!

각 노드 간 거리 구하기에서의 문제 발생

  • 이제 문제 상황에 대해 알아보겠습니다.

문제 1. 너무 가까운 거리는 거리를 계산할 수 없음

  • 현재 API에서 값을 반환해주지 않을 때 아래와 같이 로그를 출력하게 했습니다.
log.error("Distance is null for the request between " + place1.getName() + " and " + place2.getName());
log.info(place1.toString());
log.info(place2.toString());
  • 실행하면 꼭 한 두개씩은 아래와 같은 오류가 발생했습니다.

image

  • 현재 거리를 transit으로 구하고 있어서 너무 가까운 거리나 대중교통으로 갈 수 없는 곳은 API 반환이 안되고 있는 문제였습니다.

image

  • 거리를 구할 수 없을 때는 거리가 매우 가깝다고 가정하여 이를 -1로 두고 진행하였습니다.
return -1; // 거리를 계산할 수 없을 때

image

문제 2. 너무 많은 API를 호출한다.

image

  • 15개의 장소가 주어지고 출발지까지 16개의 장소가 있을 때 API를 420번 요청하고 있습니다.

    -> n * (n - 1)

문제 3. 너무 많은 요청때문에 비용이 너무 많이 발생함

  • 위의 문제점과 유사한 문제인데 API를 너무 많이 요청하고 있어서 비용이 너무 많이 발생한다는 문제가 있습니다.

image

  • 현재는 무료 평가판을 사용하고 있지만 일주일 뒤 체험판이 종료되면 요금 폭탄 예상됩니다…

정리

  • 현재 해결해야 할 문제들을 본다면 다음과 같습니다.

1. 너무 가까운 거리는 계산할 수 없다.

2. API호출이 너무 많고 이 때문에 비용이 너무 많이 발생한다.

  • 다음 포스트에서는 위의 문제를 해결할 방법들을 찾아보도록 하겠습니다!!

© 2024. All rights reserved.

김민제의 블로그