본문 바로가기
Back-end

JPA, Postgres earthdistance를 이용하여 사용자 근처 가맹점 조회 API 구현하기

by 노아론 2020. 7. 24.

PostgreSQL Cube-Based Earth Distances와 Spring Data JPA Native Query를 이용하여
Spring Boot의 사용자 근처 가맹점 찾기 API를 구현하는 내용을 담았습니다

재난지원금 사용을 위해 경기도 지역화폐 웹서비스를 개발하면서

나의 위치 기준에서 몇 Km 내에 있는 OO시의 가맹점을 조회해주는 기능이 필요하였다

(재난지원금 정책상 자신이 거주하는 시 내에서만 사용가능하다)

 

사용중인 PostgreSQL에 추가 모듈인 earthdistance가 있는 것을 보았고

이 모듈과 함께 Spring Data JPA의 Native Query를 이용하여 구현하기로 결정하였다.

 

먼저 PostgreSQL의 earthdistance가 무엇이고 어떻게 사용하는지 알아보자

 

earthdistance 모듈은 2가지의 방법으로 거리 측정을 제공한다

cube 모듈을 이용한 Cube-Basedpoint 데이터 타입을 이용한 Point-Based가 있다

 

  • Cube-Based Earth Distances
    • cube 모듈을 이용
    • 사용을 위해 earthdistance 설치가 요구됨
  • Point-Based Earth Distance
    • 내장 point 데이터타입을 이용

주의할 점으로 earthdistance 모듈은 지구를 완벽한 구의 형태로 본다

따라서 문서에서는 해당 모듈이 부정확하다고 생각되면 PostGIS를 보라고 기재해두고 있다

지구는 타원의 형태를 띄고 있다.

따라서 경도 1도마다의 거리가 모두 다르다

이러한 지구의 거리를 계산하는 방법에 마침 좋은 세미나가 있다

우아한 서버개발자의 위치정보 다루기

 

이 글에서는 Cube-Based Earth Distance 방법을 이용해 구현해본다

먼저 psql로 DB서버에 접속하여 extension을 설치한다

필요한 extension은 cubeearthdistance이다

roharon-db=# create extension cube;
CREATE EXTENSION
roharon-db=# create extension earthdistance;
CREATE EXTENSION

 

Cube-Based Earthdistance의 함수는 다음과 같다.

image

 

JPA를 사용하여 작성하기 전,

earth_distance(earth, earth)ll_to_earth(float8, float8) 함수를 psql에서 쿼리를 사용해본다

 

가맹점 데이터가 담긴 store 테이블은 아래와 같이 구성되어있다.

                          Table "public.store"
    Column    |          Type          | Collation | Nullable | Default
--------------+------------------------+-----------+----------+---------
 id           | bigint                 |           | not null |
 address      | character varying(255) |           |          |
 big_category | character varying(255) |           |          |
 category     | character varying(500) |           |          |
 latitude     | real                   |           |          |
 longitude    | real                   |           |          |
 phone_number | character varying(255) |           |          |
 post_code    | character varying(255) |           |          |
 sigoon       | character varying(255) |           |          |
 title        | character varying(500) |           | not null |
 update_date  | character varying(255) |           |          |
Indexes:
    "store_pkey" PRIMARY KEY, btree (id)

 

현 위치는 판교역 부근의 위치(37.394968, 127.111064)로 가정하고

1Km 이내의 가맹점을 조회한다면 아래와 같이 작성할 수 있다.

select * from store s where earth_distance(ll_to_earth(37.394968, 127.111064), ll_to_earth(s.latitude, s.longitude)) < 1000;

위 쿼리의 결과가 정상적으로 나왔다.

 

earthdistance 모듈을 사용하여 쿼리를 사용해봤으니 직접 JPA에 적용해보자

Native Query는 아래처럼 @Query 어노테이션에서 nativeQuery = true로 두고

value 파라미터에 쿼리를 작성하면 된다.

@Query(value = "SELECT * FROM store s WHERE earth_distance(ll_to_earth(CAST(s.latitude as float), CAST(s.longitude as float)), ll_to_earth(:latitude, :longitude)) < :radisus", nativeQuery = true)

 

시/군과 몇 Km 내의 가맹점 데이터를 가져오는지 findBySigoonAndEarthDistance로 나타내었다.

사용자 요청으로부터 시군, 거리, 위도, 경도 파라미터를 전달받고,

postgres의 store 테이블의 값들을 이용해 함께 작성한다.

 

 

이제 API 엔드포인트를 위해 storeController.java를 작성해본다.

 

 

GET /api/v1/store/near?page=0&sigoon=성남시&lat=37.394968&lng=127.111064

요청을 하게 되면 아래와 같은 응답을 받을 수 있게 된다

{
    "data": {
        "content": [
            {
                "id": 232346,
                "title": "**약국",
                "category": null,
                "sigoon": "성남시",
                "address": "경기도 성남시 분당구 판교역로 **",
                "phoneNumber": "031-***-****",
                "postCode": "13493",
                "latitude": 37.401222,
                "longitude": 127.110374,
                "updateDate": "2019-06-30",
                "bigCategory": null
            },
            {
                "id": 230420,
                "title": "(주)***",
                "category": null,
                "sigoon": "성남시",
                "address": "경기도 성남시 분당구 이매로 **",
                "phoneNumber": "031-***-****",
                "postCode": "13564",
                "latitude": 37.394947,
                "longitude": 127.12235,
                "updateDate": "2019-06-30",
                "bigCategory": null
            },
            ....           
}

 

이것으로 Spring Data JPA Native Query Postgres 추가모듈인 earthdistance 를 이용해서 가까운 가맹점 조회 API를 구현을 마쳤다.

댓글