오늘은 V3의 위치 기반 목록 조회 구현을 진행했다!
위치 기반 목록 조회는 2가지 방법으로 구현하게 되는데, 첫 번째는 DB에 저장된 데이터에서 where절을 통해 조회하는 것이고, 두 번째는 Redis의 Geospatial 기능을 통해 필터링하는 것이다.
오늘은 이 두 가지 방법 중, 첫 번째 DB 조회에 대한 부분을 구현하기 시작했다.
우선 어제는 MBRContains(ST_LINESTRINGFROMTEXT(...), ...) 함수를 통해 쿼리문으로 공간 검색을 시도해보았는데, 오늘은 이 내용을 쿼리DSL로 바꿔서 실행해야 했다.
당연히 쿼리DSL에도 위 내용을 간단하게 실행하는 명령어를 제공할 줄 알았지만,, 저 내용을 지원해주지는 않았다.
저 명령어도 결국 범위 안에 지정한 필드가 들어가는지 아닌지를 true/false로 구분하는 것이기 때문에, BooleanExpression을 사용하였다.
private BooleanExpression locationInBoundingBox(BoundingBox boundingBox) {
if (boundingBox == null) {
return null;
}
String lineString = String.format(
"LINESTRING(%f %f, %f %f)",
boundingBox.getMinLat(), boundingBox.getMinLng(),
boundingBox.getMaxLat(), boundingBox.getMaxLng()
);
return Expressions.numberTemplate(
Integer.class,
"MBRContains(ST_LINESTRINGFROMTEXT({0}, 4326), {1})",
lineString,
meeting.point
).eq(1);
}
왜인지 모르겠지만, booleanTemplate으로 사용할 수 없어서 결국은 numberTemplate을 사용해 해당 결과가 1이 맞는지를 확인하게 되었다.
이렇게 위치 필터링 자체는 그리 어렵지 않게 구현할 수 있었다.
이제 문제는 검색 기능의 복합 조건들이었는데.. 일단 검색 기능에서 우리는 최우선으로 사용자의 GPS 기준 현재 위치 정보를 가져오려고 한다.
그리고 지도 API를 사용할 것이기 때문에, 사용자가 지도를 확대/축소하거나 이동함에 따라 지도 화면에 맞는 목록들을 가져오도록 구현하려고 한다.
이 모든 내용들을 검색 API 하나에 담아야 했기 때문에.. 검색 조건을 처리하는 것이 굉장히 까다로웠다.
일단 쿼리 파라미터로 사용자가 직접 선택하는 검색어/카테고리/정렬/일정 범위를 받고, 사용자의 실시간 위치로 위도/경도를 받고, 초기에 보여줄 지도의 범위를 받고, 이후에 지도 이동에 따른 ViewPort를 받는다.
사용자 선택에 따른 조건들은 이전에 진행하던 것과 동일하게 진행할 수 있었다.
이제 위치 범위를 어떻게 잡을지가 가장 문제였는데,
처음에는 사용자 위치에 따른 범위와 지도 ViewPort에 따른 범위를 null인지 아닌지로만 판단하고, where절에 두가지 모두 사용하려고 했다.
하지만, 사용자가 GPS 허용을 하지 않았을 때는 사용자의 주소지를 기준으로 필터링하고 있는데,
GPS 위치에 대한 쿼리 파라미터를 전달하지 않아도 이 주소지 필터링이 함께 작동해서 주소지와 지도의 ViewPort가 맞물리는 영역만 조회가 되고 있었다.
결국에는 지도 필터링의 BoundingBox의 우선순위를 정할 수밖에 없었는데,
1. 지도의 ViewPort
2. 사용자의 GPS 위치
3. 사용자의 거주지
이런 우선순위를 정해서 처리하게 되었다.
사실 여기에도 아쉬운 부분들이 아직 많다.
검색어를 처리할 때도 BoundingBox 안에 있어야만 결과가 나오는 부분이나 가까운순 정렬 시 사용자의 위치 기준으로만 처리된다는 부분이나..
내가 실제 서비스를 통해 사용해봤던 모든 부분들을 적용해보고 싶은 마음이 있다.
하지만 그 모든 경우의 수를 잡기에는 시간과 나의 지식의 한계가 있기 때문에.. 일단 이정도에서 마무리하고, 다음 단계로 넘어가보려고 한다.
이제 내일부터는 Redis의 Geospatial을 통한 위치 필터링을 진행해보려고 한다.
우리 팀이 작성한 코드는 깃허브를 통해 업로드해두었다.
GitHub 보러가기
사실 DB 필터링 정도는 빠르게 끝내고 Redis에 조금 더 시간을 투자해봐야겠다는 생각이었지만.. 생각보다 DB 필터링이 너무 어려웠다.
쿼리로는 잘만 되던 내용들이 쿼리DSL로는 어떻게 표현해야할지 몰라 더 조사해보고, 이런 저런 경우의 수를 생각하며 대비하다보니 시간이 굉장히 많이 소요되었다.
그래서 사실 지금은.. 지금의 성능도 만족할만한데 Redis를 써야할까 싶긴 하지만..
일단 Redis로도 구현을 해보고 결정해보려 한다.
내일도 화이팅,,