지난 6주 동안 위치 기반 음악 공유 서비스 'Play,Place' 프로젝트를 진행했다.
그 중에서 근처에 있는 사용자들의 재생 중인 곡 정보를 보여주는 '플레이더 기능을 담당했다. 재밌는 기획이였지만 구현하는 데 많은 고민이 있었다. 기능을 구현하면서 어떤 고민이 있었는지, 어떤 방식을 선택했는지 개발 과정을 남기려고 한다.
우선 특정 사용자를 기준으로 다른 사용자들과의 거리를 계산해서 가까운 곳에 있는 사용자를 찾아야 했다. 그러나 주기적으로 정보가 업데이트되는 기능 특성 상 모든 사용자들의 위치를 가져와서 거리를 계산하는 방식은 비효율적이라고 생각되었다. 또한 주기적으로 업데이트 해야하는 정보를 RDB에 저장한다면 그 자체로도 서버 성능에 무리가 갈 것이라고 생각되었다.
그래서 보다 효율적인 거리 계산 방법을 찾기 위해 유사한 서비스에서 사용하고 있는 기술이나, 다양한 위치 저장 및 거리 계산 방법들을 찾아보았다.
주변에 있는 사용자들을 탐색하여 보여주는 기능인 토스의 '함께 토스 켜고 포인트 받기'나 카카오페이의 '내 주변 송금'은 Bluetooth Low Energy(저전력 블루투스)를 활용한 기능이다.
블루투스만으로 주변 사용자를 탐색하는 방식이 매력적이였지만 우리 서비스에서는 '100m 반경 내의 사용자를 탐색'한다는 기준이 있었기 때문에 가까운 거리의 사용자만 탐색하는 블루투스 방식은 적절하지 않다고 판단했다.
그 다음으로는 GeoHash라는 기술을 발견했다.
Geohash는 (위도, 경도) 정보를 문자와 숫자로 이루어진 짧은 문자열로 인코딩하는 방식이다.
GeoHash의 원리는 다음과 같다. 지구를 평평한 사각형으로 가정하고, 적당한 크기가 될 때까지 일정한 구획으로 계속해서 나눈다. 그리고 현재 위치가 속한 구획을 문자로 표시하여 라벨을 붙인다. 우리나라는 w 구획에 속해있기 때문에 1레벨의 라벨은 w가 붙게 된다.
첫번째 라벨을 찾았다면, 그 구획 안에서 또 다시 일정하게 구획을 나누어서 현재 위치가 속한 구획을 찾는 방식이다. geohash 페이지를 이용해보면 이해가 더 쉽다.
GeoHash는 인코딩 된 문자열의 뒷 부분을 지우는 방식으로 정밀도를 조절하고 문자열의 크기를 줄일 수 있다. 즉, 문자열이 길어질수록 표현하고자 하는 위치에 대한 정밀도가 높아진다. 이런 점을 이용하여 적당한 레벨로 구획을 나눈다면, 가까운 위치에 있는 데이터를 찾을 때 기준 구획과 인접한 구획들만 탐색하면 되기 때문에 훨씬 효율적으로 탐색 할 수 있다.
아래 사진을 예시로 들어보자.
100m 반경 내의 사용자를 탐색하기 위해 7레벨로 구획을 나누었다. 사진 속 사용자가 위치한 구획은 wy60jyq
이다.
중앙의 박스 안에 기준이 되는 사용자가 있다고 했을 때, 해당 사용자의 반경 100m 내에 있는 사용자는 같은 박스 안에 있을 수도 있지만, 그 주변의 박스에 있는 경우도 있을 수 있다. 이 사용자들을 모두 탐색하기 위해 자신이 위치한 박스 + 주변 8개 박스를 탐색할 수 있다. 6레벨까지의 라벨인wy60jy
를 활용하여 wy60jyq
의 인접한 구획을 찾을 수 있다.
Java는 GeoHash를 사용할 수 있는 라이브러리가 있다. build.gradle
에 dependency를 추가한다.
dependencies {
implementation 'ch.hsr:geohash:1.4.0'
}
GeoHash의 withCharacterPrecision()
메서드를 통해 위도, 경도, 문자열의 길이를 지정하여 GeoHash로 인코딩할 수 있다.
GeoHash geoHash = GeoHash.withCharacterPrecision(latitude, longitude, 7);
GeoHash의 getAdjacent()
메서드는 해당 GeoHash의 인접한 구획 List를 반환한다.
GeoHash[] adjHash = geoHash.getAdjacent();
GeoHash를 사용해서 사용자의 위치를 저장하고 특정 사용자가 위치한 구획의 인접 구획들만 가져와서 탐색한다면 훨씬 효율적으로 기능을 구현할 수 있을 것이라고 생각했다. 하지만 직접 GeoHash로 위도와 경도를 변환하여 저장하고, 또 인접 구획을 찾아서 인접한 사용자들을 찾는 것이 번거롭다고 느껴졌다. 그러던 중 이를 해결해줄 Redis의 Geospectial Index를 알게 되었다.
다음 글에서는 Redis Geospectial에 대해서 정리해보려고 한다.
아주 멋진 글이네요!