Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import jombi.freemates.model.constant.Author;
import jombi.freemates.model.constant.CategoryType;
import jombi.freemates.model.dto.PlaceDto;
Expand Down Expand Up @@ -112,7 +113,7 @@ public ResponseEntity<Page<PlaceDto>> getPlacesByCategory(
"""
)
@GetMapping("/geocode")
public ResponseEntity<PlaceDto> getPlaceByGeocode(
public ResponseEntity<List<PlaceDto>> getPlaceByGeocode(
@RequestParam String x,
@RequestParam String y
) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/jombi/freemates/repository/PlaceRepository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package jombi.freemates.repository;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
import jombi.freemates.model.constant.CategoryType;
Expand Down Expand Up @@ -45,4 +46,18 @@ public interface PlaceRepository extends JpaRepository<Place, UUID> {

Optional<Place> findByXAndY(String x, String y);

@Query(
value = "SELECT * " +
"FROM place p " +
"WHERE CAST(p.x AS double precision) BETWEEN :xMin AND :xMax " +
" AND CAST(p.y AS double precision) BETWEEN :yMin AND :yMax",
nativeQuery = true
)
List<Place> findByCoordinateRange(
@Param("xMin") double xMin,
@Param("xMax") double xMax,
@Param("yMin") double yMin,
@Param("yMax") double yMax
);
Comment on lines +49 to +61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

성능 최적화를 위한 개선사항을 고려해보세요.

네이티브 쿼리가 올바르게 구현되었지만, 다음 사항들을 개선하면 더 좋을 것 같습니다:

  1. 인덱스 활용 제한: WHERE 절에서 CAST 사용으로 인해 x, y 컬럼의 인덱스 활용이 제한될 수 있습니다.
  2. 데이터 타입 불일치: 좌표가 문자열로 저장되어 있어 매번 형변환이 필요한 상황입니다.

다음 중 하나의 방법을 고려해보세요:

방법 1: 복합 인덱스 추가

CREATE INDEX idx_place_coordinates_cast ON place 
(CAST(x AS double precision), CAST(y AS double precision));

방법 2 (권장): 데이터 모델 개선
Place 엔티티에 double 타입의 좌표 필드를 추가하여 형변환 오버헤드를 제거:

@Query("SELECT p FROM Place p " +
       "WHERE p.xCoord BETWEEN :xMin AND :xMax " +
       "  AND p.yCoord BETWEEN :yMin AND :yMax")
List<Place> findByCoordinateRange(
    @Param("xMin") double xMin,
    @Param("xMax") double xMax,
    @Param("yMin") double yMin,
    @Param("yMax") double yMax
);
🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/repository/PlaceRepository.java around lines 49
to 61, the current native query casts string coordinates to double, which
prevents index usage and hurts performance. To fix this, update the Place entity
to include double-typed coordinate fields (e.g., xCoord and yCoord) and modify
the query to use these fields directly without casting. Replace the native query
with a JPQL query filtering on the double fields to eliminate casting overhead
and enable efficient index utilization.


}
45 changes: 31 additions & 14 deletions src/main/java/jombi/freemates/service/PlaceService.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package jombi.freemates.service;


import static java.util.stream.Collectors.toList;

import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import jombi.freemates.model.constant.CategoryType;
import jombi.freemates.model.dto.KakaoPlaceCrawlDetail;
import jombi.freemates.model.dto.PlaceDto;
Expand Down Expand Up @@ -118,19 +119,35 @@ public Page<PlaceDto> getPlacesByCategory(CategoryType category, Pageable pageab
* 좌표에 따른 장소 조회
*/
@Transactional(readOnly = true)
public PlaceDto getPlacesByGeocode(
String x,
String y
) {
if (x == null || x.trim().isEmpty() || y == null || y.trim().isEmpty()) {
throw new CustomException(ErrorCode.INVALID_REQUEST);
}
Optional<Place> placeOpt = placeRepository.findByXAndY(x, y);
if (placeOpt.isEmpty()) {
log.warn("좌표 ({}, {})에 해당하는 장소가 없습니다.", x, y);
throw new CustomException(ErrorCode.PLACE_NOT_FOUND); // 또는 예외 처리
public List<PlaceDto> getPlacesByGeocode(String xStr, String yStr) {
double xInput;
double yInput;
try {
xInput = Double.parseDouble(xStr);
yInput = Double.parseDouble(yStr);
} catch (NumberFormatException e) {
throw new CustomException(ErrorCode.INVALID_REQUEST);
}
return convertToPlaceDto(placeOpt.get());

// ±0.0001(= tolerance) 범위를 오차로 설정
double tolerance = 0.0001; // 0.0001은 약 11m 정도의 거리
double xMin = xInput - tolerance;
double xMax = xInput + tolerance;
double yMin = yInput - tolerance;
double yMax = yInput + tolerance;

// 네이티브 쿼리로 DB에서 범위 내의 장소만 한 번에 조회
List<Place> matched = placeRepository.findByCoordinateRange(xMin, xMax, yMin, yMax);
if (matched.isEmpty()) {
throw new CustomException(ErrorCode.PLACE_NOT_FOUND);
// 혹은 빈 리스트를 반환하고 싶다면 아래처럼:
// return Collections.emptyList();
}

// Place → PlaceDto로 변환
return matched.stream()
.map(this::convertToPlaceDto)
.collect(Collectors.toList());
}

/**
Expand Down