- 로직
- UserService에서 Redis의 현재의 search_count 정렬하기
- ParkingMainController 만들어서 GET으로 인기 주차장 출력(일단 순위 별로 전체 출력)
📁 UserService.java
...
public Map<String, String> getAllSearchCounts(){
Set<String> keys = stringRedisTemplate.keys(SEARCH_COUNT_KEY_PREFIX + "*");
if(keys != null && !keys.isEmpty()){
List<String> keyList = new ArrayList<>(keys);
List<String> values = stringRedisTemplate.opsForValue().multiGet(keyList);
Map<String, String> searchCounts = new HashMap<>();
for(int i = 0; i < keyList.size(); i++){
String key = keyList.get(i);
String parkingId = key.substring(SEARCH_COUNT_KEY_PREFIX.length());
String count = values.get(i);
searchCounts.put(parkingId, count);
}
return searchCounts;
}
return Collections.emptyMap();
}
...
...
public int getSearchCount(Long parkingId) {
String key = SEARCH_COUNT_KEY_PREFIX + parkingId;
String count = stringRedisTemplate.opsForValue().get(key);
return count != null ? Integer.parseInt(count) : 0;
}
설계
적용
📁 ParkingMainController.java
@Slf4j
@Controller
@RequestMapping(value = "/api/v1/parking")
public class ParkingMainController {
private final UserService userService;
public ParkingMainController(UserService userService) {
this.userService = userService;
}
@GetMapping("/main")
public ResponseEntity<List<ParkingInfoDTO>> getParkingInfoForUI(Model model) {
try {
// Redis에서 주차장 정보 가져오기
List<ParkingInfoDTO> parkingInfoList = userService.getParkingInfo();
// 검색 횟수를 높은 순으로 정렬
List<ParkingInfoDTO> sortedParkingInfoList = parkingInfoList.stream()
.sorted(Comparator.comparingInt(dto -> -userService.getSearchCount(dto.getParkingId())))
.collect(Collectors.toList());
// 메인 페이지에 전달할 데이터 설정
model.addAttribute("parkingInfoList", sortedParkingInfoList);
return ResponseEntity.status(HttpStatus.OK).body(sortedParkingInfoList);
} catch (Exception e) {
log.error("주차장 정보 목록을 가져오는 중 오류 발생", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
}
📤 Request
GET localhost:8080/api/v1/parking/main
📥 Response
1️⃣ 결과
테스트 기준
기존의 위치 정보 조회(영등포) 이용 : 카운트를 가장 많이 진행한 DB를 사용했습니다.
✚ 스타벅스 뚝섬유원지역의 위도, 경도를 이용해 주변 주차장 조회를 1회 진행하여 카운트를 다르게 설정해주었습니다.
Redis 현황
웹페이지 결과(포스트맨과 동일하나 더 많은 값을 보여줄 수 있음)
- 현재 count 현황
- 336, 380, 43, 293 : 19
- 370, 27, 70, 549, 66, 247 : 11
- 그 외 : 10(이전 카운트 테스트로 인해 증가됨..)
2️⃣ 보완할 사항 정리
부여받은 RDS의 MySQL 버전은 5.6.15인데, 현재 주변 주차장 조회 로직에 사용한 st_distance_sphere
라는 GIS Function은 8.0버전에서부터만 사용할 수 있기 때문에 문제가 발생했습니다.
-> 결국, 기존의 다른 방식이었던 RADIANS를 이용한 계산 로직으로 변경했습니다!
이전
public List<ParkingEntity> findNearbyParking(double userLon, double userLat, int maxDistance) {
QParkingEntity parkingEntity = QParkingEntity.parkingEntity;
return queryFactory
.select(parkingEntity)
.from(parkingEntity)
.where(Expressions.numberTemplate(Double.class,
"FUNCTION('ST_Distance_Sphere', FUNCTION('POINT', {0}, {1}), FUNCTION('POINT', {2}, {3}))",
parkingEntity.lon, parkingEntity.lat, userLon, userLat)
.loe(maxDistance))
.orderBy(Expressions.numberTemplate(Double.class,
"FUNCTION('ST_Distance_Sphere', FUNCTION('POINT', {0}, {1}), FUNCTION('POINT', {2}, {3}))",
parkingEntity.lon, parkingEntity.lat, userLon, userLat).asc())
.fetch();
}
이후
public List<ParkingEntity> findNearbyParking(double userLon, double userLat, int maxDistance) {
QParkingEntity parkingEntity = QParkingEntity.parkingEntity;
return queryFactory
.select(parkingEntity)
.from(parkingEntity)
.where(
Expressions.numberTemplate(Double.class,
"ROUND(6371 * 2 * ASIN(SQRT(POW(SIN((RADIANS({0}) - RADIANS({1})) / 2), 2) + " +
"COS(RADIANS({1})) * COS(RADIANS({0})) * POW(SIN((RADIANS({2}) - RADIANS({3})) / 2), 2))), 2)",
parkingEntity.lat, userLat, parkingEntity.lon, userLon)
.loe(maxDistance)
)
.orderBy(Expressions.numberTemplate(Double.class,
"ROUND(6371 * 2 * ASIN(SQRT(POW(SIN((RADIANS({0}) - RADIANS({1})) / 2), 2) + " +
"COS(RADIANS({1})) * COS(RADIANS({0})) * POW(SIN((RADIANS({2}) - RADIANS({3})) / 2), 2))), 2)",
parkingEntity.lat, userLat, parkingEntity.lon, userLon).asc())
.fetch();
}
공부 자료
📮 MySQL 좌표 데이터 가져오기 (+ Spatial Index 활용하기)
📮 [데이터베이스] 8. 인덱스 (2)
MySQL 관련 참고 자료
💬 12.1 Built-In Function and Operator Reference(MySQL 8.0 버전에 추가된 function에 st_distance_sphere 있음)
💬 12.16.13 Spatial Convenience Functions