오랜만에 기술 관련된 내용으로 글을 쓴다.
많이 경험하지 못할 이슈가 나왔다.
최근 개발 마무리 중인 서비스는 지도 기능이 있는데, 거리 기반으로 쿼리를 해서 지도에 데이터를 표시해야 하는 경우가 많다.
대학원 시절 PostgreSQL가 9버전일 때 PostGIS라는 것이 있다고 교수님께 주워 들은 것이 있어 이를 사용해서 구현했다. 당연히 그때 당시보다 훨씬 좋아졌다는 얘기를 들어서 사용했다.
다행히 Django에서 PostGIS 를 잘 지원하고 있었고, 그대로 활용했다.
condition = Q(latlng__distance_lte=(Point(lng, lat), D(m=1000))
Geo Field 에 대해서 distance_lte를 지원하기 때문에, 이렇게 1km 내 엔트리를 조회할 수 있다.
사실 이렇게 동작하니 별 문제 없고, 성능이 조금 느리긴 했는데, 나중에 인덱스 만들면 되겠지~ 싶었다.
역시나 성능을 개선하달라는 요청이 있었다.
알아보니 PostGIS의 ST_Distance 보다 ST_DWithin의 성능이 좋다는 아티클을 보게 되었다.
그래서 Django 문서를 보니 역시 지원하고 있었다.
다만 문서에는 D 객체를 사용해서 거리를 전달하고 있었는데, 실제로는 m 단위의 numeric 데이터를 전달해야했다.
condition = Q(latlng__dwithin=(Point(lng, lat), 1000)
그런데 잘 동작하지 않았다. 에러는 없었지만, 필터가 되지 않은 것으로 보였다.
PostGIS에서의 설정이 이슈인지 보기 위해 PostGIS 관련 아티클도 보는데, 별도로 설정해야하는 것이 없는 것으로 보아 이 문제는 아니었던 것 같았다.
역시 돌고 돌아 공식 문서다. Django 문서 내 GeoDjango 문서를 정독해본적이 없어서, 관련 문서를 보던 중 GeoDjango Model API 문서를 정독해봤다. Spatial Field Options 항목에 꽤 중요한 내용들이 많았다.
그 중 Geography Type을 봤는데, 처음에는 리스트로 정리된 Cast 관련 값만 봐서 필요 없는 값인 줄 알았으나 WGS84를 사용하려면 활성화해야 한다고 적혀있었다. 세상에나.. dwithin 을 사용하려면 WGS84가 필요했다.
이 두 연결고리를 코드 예시로 적어두었으면 좋았을텐데..
결국 모델 필드를 변경하고 나니 잘 동작했다.
latlng = gis_models.PointField(geography=True, verbose_name="GIS")
1.5s ~ 3.5s 가 걸리던 API 가 200ms 로 줄었다.
뿌듯하다.
정리를 다 하고 드는 생각이, ChatGPT는 알았을까? Cursor는 알았을까?