무엇을 구현할 것인가?
사용자의 위치에 따라 가까운 거리의 정보부터 보여주기 위해 좌표 값을 저장해야 함.
왜 구현할 것인가?
- DB 컬럼에 직접 double 데이터를 넣어 좌표계를 만들고 애플리케이션에서 정렬하는 방법이 있지만, RDB에서 이미 공간 정보를 저장하기 위한 데이터 타입이 따로 있기 때문에 확장성 관점에서 굳이 내가 바퀴를 직접 구현할 필요가 없다.
- RDB에서 인덱스를 구성하는 등의 검색을 위한 최적화를 시도해 볼 수 있다.
- 1. 대로 하여도 테이블의 row는 점점 늘어날 것이기 때문에 WAS에서 점점 부하가 많이 걸릴 것이다.
어떻게 구현할 것인가?
가까운 거리를 계산하기 위해선 다음과 같은 조건들이 필요하다.
1. 위치 정보를 저장하기 위해 통일된 좌표계가 필요함.
2. 임의의 두 점 사이의 거리를 계산하는 방법은 피타고라스 정리와 같은 기하학적인 방법으로 구할 수 있어야 함.
그래서 찾아본 결과, RDB에서 공간 정보를 저장하기 위한 특별한 데이터 타입과 함수가 있다는걸 알았고, 이를 JPA로 옮기기 위해서 삽질을 했다.
우선, 일반적인 데이터 타입과는 다르게 공간 정보를 저장하기 위해서는 구현체를 따로 지정해주어야 한다.
일반 구현체를 지정해주면 hibernate가 tinyblob과 같은 타입으로 저장하기 때문이다.
적용을 위해선 다음과 같은 설정이 필요하다.
build.gradle에 라이브러리를 추가해야 한다.
implementation 'org.hibernate:hibernate-spatial:5.6.2.Final'
h2, maria db를 기준으로 각각 다음과 같은 설정을 application.yml에 추가했다.
jpa:
#h2
database-platform: org.hibernate.spatial.dialect.h2geodb.GeoDBDialect
#maria
database-platform: org.hibernate.spatial.dialect.mysql.MySQL56InnoDBSpatialDialect
이후 도메인 클래스에 하단의 패키지를 추가하였다. 내가 사용할 클래스의 이름은 Point이다.
import org.locationtech.jts.geom.Point;
@Column(name = "location")
private Point location;
Point 클래스에 데이터를 저장하기 위해선 GeometryFactory 클래스에서 Coordinate을 이용해 변환하는 방법과 WTKReader 클래스에서 문자열을 이용해 변환하는 방법 등이 있다. 나는 전자를 선택했다. DTO에서 double 형태로 값을 받기 때문이다.
public interface GeometryConverter{
Point convertCoordinateToPoint(double latitude, double longitude);
}
...
@Component
public class GeometryCoordinateConverter implements GeometryConverter {
private final GeometryFactory geometryFactory;
public GeometryCoordinateConverter() {
this.geometryFactory = new GeometryFactory();
}
@Override
public Point convertCoordinateToPoint(double latitude, double longitude){
return this.geometryFactory.createPoint(new Coordinate(latitude, longitude));
}
}
인터페이스를 만든 이유는 Point를 만드는 구현체가 달라질 수 있기 때문이다. 또한 기술적 복잡함을 덜어내기 위해 인터페이스로 빈을 주입받도록 했다.
이제 저장한 데이터를 확인해보도록 하자. h2 데이터베이스에 다음과 같이 저장된다.
POINT(x좌표, y좌표) 형태로 잘 저장된 것을 확인할 수 있다.
참고:
https://momentjin.tistory.com/136
https://dev.mysql.com/doc/refman/8.0/en/spatial-relation-functions-object-shapes.html