주루마블 위치 기반 서비스 "술지도"

wellbeing-dough·2024년 1월 24일

문제상황

  • 술 조회 기능 중 지방마다 특색 있는 전통주 특성상, 위치 기반으로 조회하는 기능의 필요성을 느껴, 위도 경도 값을 활용하여 유저가 확대한 좌표 안에 있는 전통주 주조장 조회 기능을 개발했다
  • 우리 서비스에서 가장 성능이 안나오는 부분이다 왜와이? 해당 페이지에 들어가면 대한민국 전체 술을 조회해서 500개의 술 데이터를 조회하는데 500개의 술 이미지를 랜더링하는게 가장 성능이 안나온다
  • 술 정보를 전부 바인딩하지 않고 확대하기 전까지 마크 클러스터링을 하면 좋겠지만 프로젝트 사정상 개발 기간이 거기까지 못할거같아서 서버에서라도 성능 개선을 하려고 한다


4.7초....?

해결방안

  • 우리 서비스는 한국관광공사 공모전이며 여행을 가서 지역의 전통주를 즐기는 서비스이다
  • 그럼 유저 입장에서 1. 술 지도 페이지에 들어간다 2. 내 위치를 조회한다 3. 내 주변에 있는 전통주 주조장을 조회한다
  • 1, 2, 3 흐름이 너무 뚝뚝 끊겨서 사용 경험에 악영향을 미친다
  • 처음에 페이지에 접근하면 대한민국 지도 전체를 보여준다 그러면 내가 여행하는 지역 주변에 가야 술을 조회하면 되지 않을까?

결론 -> 처음에 술 지도 기능을 들어갈땐 술 데이터를 반환하지 않고 어느정도 지도상 확대가 되었을 때 그때부터 쿼리를 타고 술 지도를 보여주면 된다!

기존코드

    public Slice<GetMapInDrinksResponse> getMapInDrinks(Double startX, Double startY, Double endX, Double endY, int page, int size) {
        PageRequest pageRequest = PageRequest.of(page, size);
        Slice<MapInDrinkData> drinkData = drinkRepository.findDrinksByCoordinate(pageRequest, startX, startY, endX, endY);
        return new SliceImpl<>(getGetMapInDrinksResponses(drinkData), drinkData.getPageable(), drinkData.hasNext());
    }
    @Query("SELECT new co.kr.jurumarble.drink.domain.dto.MapInDrinkData(d.id, d.name, d.region, d.latitude, d.longitude, d.image, d.manufacturer) FROM Drink d " +
            "WHERE (d.latitude BETWEEN :startX AND :endX AND d.longitude BETWEEN :startY AND :endY) " +
            "ORDER BY d.name")
    Slice<MapInDrinkData> findDrinksByCoordinate(PageRequest pageRequest, @Param("startX") Double startX, @Param("startY") Double startY, @Param("endX") Double endX, @Param("endY") Double endY);

이렇게 간단하게 클라이언트가 확대하는 {좌측 상단 위도,경도} {우측 하단 위도, 경도}를 기반으로 그 내부에 있는 전통주 데이터를 반환하는 간단한 쿼리이다

이제 여기에서 반경 위도 경도 기반으로 거리를 잴 수 있는 Haversine 공식을 사용했다
지구는 구형태라서 위치마다 경도, 위도의 거리는 다르다 이를 계산하기 위해서 두 지점의 위도, 경도를 가지고 거리를 계산하는 하버사인 공식을 사용했다 이 공식은 두 지점의 위도, 경도, 지구 반지름 등을 이용하여 두 지점 사이의 거리를 구할 수 있다

    public double calculateDistanceInKm(double startX, double startY, double endX, double endY) {
        final int R = 6371;

        double latDistance = Math.toRadians(endX - startX);
        double lonDistance = Math.toRadians(endY - startY);

        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(startX)) * Math.cos(Math.toRadians(endX))
                * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return R * c;
    }

이렇게 하면 유저가 확대한 좌측 상단 우측 하단의 거리를 구할 수 있다

여행지에 왔다고 생각해보자, 아무리 차가 있다 해도 여행지 반경 15km 이상 이동하지 않을거 같다는 팀원들의 설문을 얻었다 그러면 자기 여행지에서 지도 좌측 위로 15키로면 확대 범위 살짝 넉넉잡아 40km 부터 쿼리를 날려보자

    public Slice<GetMapInDrinksResponse> getMapInDrinks(Double startX, Double startY, Double endX, Double endY, int page, int size) {
        double distance = calculateDistanceInKm(startX, startY, endX, endY);
        if (distance < RANGE_OF_DRINK_MAP) {
            PageRequest pageRequest = PageRequest.of(page, size);
            Slice<MapInDrinkData> drinkData = drinkRepository.findDrinksByCoordinate(pageRequest, startX, startY, endX, endY);
            return new SliceImpl<>(getGetMapInDrinksResponses(drinkData), drinkData.getPageable(), drinkData.hasNext());
        } else {
            return new SliceImpl<>(new ArrayList<>());
        }
    }

술 지도 기능을 사용하는데 있어서 확대할때마다 툭툭 끊기지 않고 훨씬 수월해졌다

그리고 기존에 맨 위 사진처럼 4.7초 나오던 페이지 로딩 시간이

1.21초로 줄었다

참고 - https://en.wikipedia.org/wiki/Haversine_formula

0개의 댓글