유저 위치를 중심으로 반경 거리를 설정하여 스포츠 시설을 조회하는 api를 개발해야 한다. 이는 DB에 spatial query를 요청함으로써 해결할 수 있다. 기존에 사용하고 있던 DB는 H2였는데, 이 환경에서 spatial query를 적용하기 위해서는 DB에서 관련 함수를 제공하는지 확인해봐야 한다. H2 DB 환경에서는 h2gis와 hibernate-spatial 라이브러리를 활용하면 jpql로 hibernate-spatial에서 제공하는 함수로 spatial query를 요청할 수 있다.

실제로 hibernate-spatial 라이브러리를 살펴보면 여러 가지 DB 환경에서의 호환성을 제공한다. 그 중에 h2gis도 있음을 확인할 수 있다. 그리고 hibernate-spatial에서 공통적으로 제공하는 CommonSpatialFunction이 있다. 해당 enum class에서 아래와 같이 수많은 타입의 enum을 정의하고 있음을 알 수 있다.

하지만 프로젝트에서는 h2gis를 사용하지 않고 PostgreSQL로 DB를 전환하고 postgis에서 제공하는 함수를 사용하기로 했다. 왜냐하면 이후에 거리 관련해서 더 복잡한 쿼리를 요청할 수도 있을 것이라 예상되었기 때문에 강력한 geospatial query를 자랑하는 PostgreSQL로 전환하였다.
사용자 위치를 중심으로 거리를 계산하는 함수를 먼저 찾아야 했는데, 공식 문서에서 찾을 수 있었다.
이를 참고하여 거리를 계산하기 위해 두 개의 포인트(스포츠 시설과 유저의 위치)가 필요하고, ST_DWithin 함수에서 요구하는 파라미터는 geometry type이므로 적절하게 casting을 해줘야 한다. 이 때, SRID를 4326으로 설정해야 하는데, 그 이유는 아래와 같다.
The SRID is used to tell which spatial reference system will be used to interpret each spatial object. A common SRID in use is 4326, which represents spatial data using longitude and latitude coordinates on the Earth's surface as defined in the WGS84 standard, which is also used for the Global Positioning System (GPS).
그래서 다음과 같이 SQL을 만들어서 원하는 기능을 구현할 수 있었다.
@Repository
public interface FacilityRepository extends JpaRepository<Facility, Long> {
//...
@Query(value = "select * from Facility f"
+ " where ST_DWithin(CAST(ST_SetSRID(ST_Point(:#{#dto.latitude}, :#{#dto.longitude}), 4326) as"
+ " geography), CAST(ST_SetSRID(ST_Point(f.latitude, f.longitude), 4326) as"
+ " geography), :#{#dto.distance})"
+ " and f.category = :#{#dto.category.name()}",
nativeQuery = true)
Slice<Facility> findFacilityAroundUser(Pageable pageable, @Param("dto") FindFacilityDto dto);
}