번개 모임 프로젝트를 진행하던 중, 사용자 주위의 모임을 보여주는 기능을 개발해야 했다.
모임을 등록할 때, 모임의 장소를 선택해야 하기 때문에 해당 장소의 좌표값을 저장하고 사용자의 위치 좌표값을 기준으로 특정 반경 내에 있는 모임들을 보여주는 것을 목표로 했다.
먼저, 좌표값을 DB에 어떻게 저장해야 할 지 고민했다. 각각 경도와 위도를 저장할 수도 있고 MySQL에서는 POINT 데이터 타입으로 저장하는 방법이 있었다.
POINT는 위도와 경도를 하나의 점으로하는 공간 데이터를 저장하는 타입이다.
Point를 사용하면 점과 점 사이의 거리를 계산하는 공간 함수를 사용할 수 있다고 하여서 해당 타입을 사용하기로 결정했다.
Spring Data JPA에서 지리적 데이터를 사용하기 위해서 Hibernate Spatial을 사용하기로 했다. Hibernate Spatial은 Hibernate ORM 프레임워크의 확장으로, 지리적 데이터를 다루는데 유용한 기능을 제공한다고 한다.
먼저, build.gradle에 아래 의존성을 추가했다.
implementation group: 'org.hibernate.orm', name: 'hibernate-spatial', version: '6.4.4.Final'
그다음 모임에 Point 타입의 변수를 저장하기 위해 Bungae Entity 클래스에 Point 타입의 속성을 추가했다.
@Column(columnDefinition = "POINT")
@Type(type = "point")
private Point coordinates;
그다음 현재 위치 기준 특정 반경 내의 모임들을 불러오기 위해 Controller에 아래 메서드를 추가했다.
@GetMapping(value = "/find/nearby", produces = "application/json; charset=UTF-8")
@ResponseBody
public List<BungaeDTO> findBungaeNearby(@RequestParam double lat, @RequestParam double lon, @RequestParam double radius) {
Point location = new GeometryFactory().createPoint(new Coordinate(lon, lat));
return bungaeService.findBungaeNearby(location, radius);
}
간단한 테스트를 위해 url로 위도와 경도, 원하는 반경을 받아와서 위도와 경도를 Point로 바꾸고 이 Point와 반경을 service 레이어로 전달했다.
(이제 와서 돌이켜보면 위도와 경도를 Point로 변경하는 부분은 비즈니스 로직에 해당하니까 service 레이어에서 진행하는 게 맞는 것 같다.)
service에서는 repository에서 찾은 데이터들을 List형태로 받아오고 이를 DTO로 바꾸고 다시 List로 바꾸는 작업을 진행했다.
@Override
@Transactional
public List<BungaeDTO> findBungaeNearby(Point userLocation, double radius) {
List<Bungae> bungaeList = bungaeRepository.findBungaeNearby(userLocation, radius);
return bungaeList.stream().map(this::convertToDTO).collect(Collectors.toList());
}
repository에서는 점과 점사이의 거리를 계산하는 MySQL 함수인 ST_Distance_Sphere를 사용해서 좌표(사용자 위치) 기준 범위 내의 모임들을 찾아서 반환해 주었다.
@Query(value = "SELECT * FROM Bungae WHERE ST_Distance_Sphere(bungae_location, :location) <= :radius", nativeQuery = true)
List<Bungae> findBungaeNearby(@Param("location") Point location, @Param("radius") double radius);
이렇게 해서 특정 위치를 기준으로 주변의 모임을 불러오는 기능을 만들었다.
하지만, 장소를 등록할 때 카카오 지도 검색 기능을 가져와서 사용했는데, 여기서 좌표를 받아오는 걸 못 찾아서 다른 방법으로 해당 기능을 구현하게 되었다. So sad...
결국 Location이라는 Embeddable 클래스를 만들어서 카카오 지도에서 제공하는 장소 이름과 상세주소를 저장하고 프런트 쪽에서 해당 장소를 기준으로 지도 화면에 보이는 모임만 보여주는 걸로 변경됐다.
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Location {
@Column(name = "keyword")
private String keyword;
@Column(name = "address")
private String address;
}
@Embedded
private Location bungaeLocation;
'프로젝트 회고' 카테고리의 다른 글
[번개 모임 프로젝트 회고] 프로젝트 구조 설정, Git을 통한 버전 관리 (0) | 2024.07.04 |
---|---|
[번개 모임 프로젝트 회고] 소개 및 배경 (0) | 2024.06.27 |