서론
새로운 서비스의 개발을 준비하고 있습니다. 그 서비스를 위해서 위치 기반 검색이 가능해야 할 것으로 생각하고 있습니다.
아직 해당 기능에 대해서 구현 해본 적도 없고, 어떻게 구현 해야 할지 감도 잡히지 않아 아직 개발단계에는 이르지 않았지만 이에 대해 미리 서치한 내용을 기록하려고 합니다.
Postgresql을 사용하게 될 것이므로, Postgreslql에서의 구현 방법도 미리 기록합니다.
위치 기반 검색 기능은 사용자의 위치 정보(위도와 경도)를 데이터베이스에 저장하고, 이를 기반으로 특정 거리 내에 있는 다른 사용자나 장소를 찾는 것입니다.
위치 기반 검색 기능의 구현
위치 정보의 저장
사용자의 위치 정보(위도, 경도)를 저장합니다. 위치 정보는 주소 변환(Geocoding)이나 모바일 기기의 GPS를 통해 직접 얻을 수 있습니다. 또 외부 API를 사용해 위도,경도 정보를 다시 주소로 변환할 수도 있습니다.
거리 계산
데이터베이스에 저장된 위치 정보를 사용하여 사용자 간의 거리를 계산합니다. 이 때 대표적으로 사용되는 알고리즘은 하버사인 공식 입니다. 하버사인 공식은 두 지점 사이의 거리를 계산할 때 지구의 구형을 고려하여 보다 정확한 결과를 제공합니다.
거리 기반 검색
사용자의 위치에서 특정 거리 내에 있는 사용자나 장소를 찾기 위해 SQL 쿼리 내에서 거리 계산 로직을 사용합니다. 예를 들어 SQL의 HAVING 절을 사용하여 계산된 거리가 특정 버위 내에 있는 레코드만 선택할 수 있습니다.
인덱싱과 최적화
위치 기반 검색의 효율을 높이기 위해 공간 데이터를 효율적으로 쿼리할 수 있는 공간 인덱스 (Spatial Index)를 사용할 수 있습니다.
공간 인덱스(Spatial Index)
공간 인덱스란?
공간 인덱스(Spatial Index)는 공간 데이터(위치 정보를 포함한 데이터)를 효율적으로 쿼리하기 위한 데이터베이스 인덱스입니다. 공간 데이터는 위도와 경도로 표현되는 지점(point), 선(line), 면(area) 등 다양한 형태가 있을 수 있습니다. 이러한 데이터를 빠르게 검색하고, 공간적 관계(예: 교차, 인접, 포함 등)를 효율적으로 분석하기 위해 공간 인덱스를 사용합니다.
공간 인덱스의 종류
- R-트리(R-tree): 계층적 구조를 가진 트리 기반 인덱스로, 사각형 영역을 노드로 사용하여 공간 데이터를 구분합니다. 각 노드는 자식 노드를 포함하는 최소한의 경계 사각형을 가집니다. R-트리는 공간 검색과 범위 쿼리에 효율적입니다.
- 쿼드트리(Quadtree): 공간을 사각형 영역으로 재귀적으로 분할하는 트리 기반 인덱스입니다. 각 노드는 최대 네 개의 자식(북동, 북서, 남동, 남서)을 가질 수 있으며, 공간을 균등하게 분할합니다.
- GiST(Generalized Search Tree): 다양한 검색 트리를 일반화한 구조로, R-트리, B-트리 등 여러 종류의 인덱스를 하나의 프레임워크 내에서 지원합니다. PostgreSQL에서 사용됩니다.
- SP-GiST(Space-Partitioned Generalized Search Tree): 공간을 분할하는 기법에 기반한 인덱스로, GiST의 확장형입니다. 공간을 비균등하게 분할하여 더 효율적인 검색을 가능하게 합니다.
공간 인덱스의 사용
공간 인덱스는 공간 쿼리를 실행할 때 데이터베이스가 전체 데이터를 순차적으로 검색하는 대신, 인덱스를 활용하여 필요한 데이터를 빠르게 찾아낼 수 있도록 합니다. 예를 들어, 특정 지역 내의 모든 관심 지점을 찾거나, 두 지점 사이의 거리를 계산할 때 공간 인덱스를 활용할 수 있습니다.
공간 인덱스의 장점
- 쿼리 성능 향상: 공간 데이터에 대한 쿼리를 실행할 때 검색 시간을 크게 줄일 수 있습니다.
- 공간 관계 분석 용이: 데이터 간의 공간적 관계를 빠르게 분석할 수 있어, 인접한 데이터 찾기, 영역 내 데이터 검색 등이 용이합니다.
공간 인덱스를 지원하는 데이터베이스 예
- PostgreSQL의 PostGIS 확장: 공간 데이터를 위한 강력한 지원과 함께 R-트리 기반의 GiST 인덱스를 제공합니다.
- MySQL: R-트리 기반의 공간 인덱스를 지원합니다.
- SQLite의 SpatiaLite 확장: 공간 데이터를 위한 기능과 함께 R-트리 인덱스를 지원합니다.
- MongoDB: 2D 및 2DSphere 인덱스를 통해 공간 데이터를 위한 인덱싱을 제공합니다.
Postgresql에서의 공간 인덱스
Postgresql에는 지리적 객체를 저장, 쿼리 및 조작할 수 있는 기능을 추가하는 PostGIS 확장이 존재합니다.
PostGIS 확장 설치
다음의 명령어를 통해 PostGIS 확장을 설치할 수 있습니다.
CREATE EXTENSION IF NOT EXISTS postgis;
공간 데이터 저장을 위한 테이블
위치 데이터를 저장할 테이블을 생성하고 공간 정보를 저장할 수 있는 GEOMETRY
또는 GEOGRAPHY
타입의 컬럼을 포함시킵니다.
CREATE TABLE places (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
location GEOGRAPHY(POINT, 4326) -- 위도와 경도를 사용하는 점 타입
);
GEOMETRY
와 GEOGRAPHY
GEOGRAPHY
와 GEOMETRY
는 PostgreSQL의 PostGIS 확장에서 제공하는 두 가지 공간 데이터 타입입니다.
-
GEOGRAPHY
- 목적: 지구의 곡률을 고려하여 실제 지리적 위치를 더 정확하게 모델링합니다.
- 사용 케이스: 큰 거리를 다루거나, 극지방 같이 지구의 곡률이 중요한 계산에 영향을 미치는 지역에서 주로 사용됩니다.
- 계산 방식: 지구를 타원체로 가정하고, 위도와 경도를 사용하여 거리와 면적을 계산합니다. 이로 인해
GEOMETRY
에 비해 계산이 더 복잡하고 느릴 수 있습니다. - 좌표 체계: 기본적으로 WGS 84 (EPSG:4326) 좌표 체계를 사용합니다.
-
GEOMETRY
- 목적: 평면 상의 점들을 사용하여 공간 데이터를 모델링합니다. 지구의 곡률을 고려하지 않고, 모든 계산을 유클리드(평면) 기하학을 사용하여 수행합니다.
- 사용 케이스: 소규모 지역 또는 곡률이 중요하지 않은 계산에 주로 사용됩니다. 예를 들어, 도시 또는 건물 내부와 같은 상대적으로 작은 지역에서 사용됩니다.
- 계산 방식: 유클리드 기하학을 바탕으로 거리, 면적 등을 계산합니다. 이는
GEOGRAPHY
에 비해 계산이 더 간단하고 빠릅니다. - 좌표 체계: 다양한 좌표 체계를 지원하며, 사용자가 필요에 따라 선택할 수 있습니다.
-
주요 차이점
- 계산 정확도와 복잡성:
GEOGRAPHY
는 지구의 곡률을 고려하기 때문에 더 복잡하고, 대규모 거리 계산에 적합합니다. 반면,GEOMETRY
는 간단한 평면 계산에 적합합니다. - 성능:
GEOMETRY
는 일반적으로GEOGRAPHY
보다 계산이 더 빠르지만, 이는 계산의 복잡성과 정확도에 따라 달라질 수 있습니다. - 적용 범위:
GEOGRAPHY
는 전 세계적인 지리적 데이터 처리에 적합하고,GEOMETRY
는 지역적 또는 평면적 공간 데이터 처리에 더 적합합니다. - 대규모, 정확한 지리적 계산이 필요한 경우
GEOGRAPHY
를, 소규모 또는 빠른 계산이 필요한 경우GEOMETRY
를 사용하는 것이 좋습니다.
- 계산 정확도와 복잡성:
결론
제가 개발할 서비스의 경우, 대부분의 상호 작용이 도시 수준에서 일어나고, 성능과 응답 시간이 중요한 요소라고 생각되기 때문에 GEOMETRY
가 더 유리해 보입니다.
공간 인덱스 생성
공간 인덱스도 생성할 수 있습니다.
CREATE INDEX idx_places_location ON places USING GIST (location);
위치 기반 검색 쿼리의 작성법
사용자의 위치를 기반으로 가까운 장소를 찾아주는 ST_DWithin
같은 공간 함수를 사용하는 쿼리를 작성합니다.
-- 사용자 위치 (위도 37, 경도 127) 주변 10km 이내의 장소 검색
SELECT name FROM places
WHERE ST_DWithin(
location,
ST_MakePoint(127, 37)::geography,
10000 -- 거리(미터 단위)
);
공간함수(Spatial functions)란?
공간 함수는 PostGIS, PostgreSQL의 확장 기능 중 하나입니다. 공간 데이터를 처리하기 위한 함수로, 지리적 위치의 관계, 형태, 거리 등을 계산하고 분석하는 데 사용됩니다.
PostGIS에서 제공하는 주요 공간 함수
-
기하학적 관계 판단 함수:
ST_Contains(A, B)
: A가 B를 포함하는지 여부를 반환합니다.ST_Intersects(A, B)
: A와 B가 교차하는지 여부를 반환합니다.ST_Within(A, B)
: A가 B 내부에 있는지 여부를 반환합니다.ST_Touches(A, B)
: A와 B가 접하는지 여부를 반환합니다.ST_DWithin(A, B, distance)
: A와 B가 지정된 거리distance
이내에 있는지 여부를 반환합니다.
-
기하학적 변환 및 생성 함수:
ST_Buffer(geometry, radius)
: 주어진 지오메트리 주변에 지정된 반경의 버퍼를 생성합니다.ST_Centroid(geometry)
: 지오메트리의 중심점(centroid)을 계산합니다.ST_MakePoint(x, y)
: x, y 좌표로 점을 생성합니다.
-
거리 및 길이 측정 함수:
ST_Distance(A, B)
: A와 B 사이의 최소 거리를 계산합니다.ST_Length(geometry)
: 선형 지오메트리의 길이를 계산합니다.
-
공간 분석 함수:
ST_Area(geometry)
: 면적을 계산합니다.ST_Union(A, B)
: 두 지오메트리의 합집합을 생성합니다.ST_Intersection(A, B)
: 두 지오메트리의 교집합 영역을 계산합니다.
-
공간 색인 및 최적화를 위한 함수:
ST_GeomFromText('POINT(0 0)')
,ST_GeomFromEWKT(...)
: Well-Known Text(EWKT)로부터 지오메트리를 생성합니다.ST_SetSRID(geometry, srid)
: 지오메트리에 공간 참조 시스템(Spatial Reference System Identifier, SRID)을 설정합니다.