포스트

PostGIS 설치 및 사용

PostGIS 설치 및 사용

위경도를 그냥 숫자 두 개로 저장하다가

지도 기반 기능을 처음 만들 때, 저는 위도/경도를 그냥 latitude, longitude 두 개의 double precision 컬럼에 넣었습니다. “반경 1km 안의 매장”을 찾으려니, 하버사인(Haversine) 공식을 SQL에 직접 박아 넣어야 했고, 쿼리는 길어지고 인덱스도 안 타서 느렸습니다. 😩

이때 알게 된 게 PostGIS입니다. PostgreSQL을 공간 데이터베이스로 확장해주는 익스텐션인데, 위치 기반 연산을 함수 한두 개로 끝낼 수 있습니다.

설치

PostGIS는 PostgreSQL의 익스텐션이라, 패키지만 깔려 있으면 SQL 한 줄로 활성화됩니다.

1
2
3
4
5
-- 현재 데이터베이스에 PostGIS 활성화
CREATE EXTENSION IF NOT EXISTS postgis;

-- 버전 확인
SELECT PostGIS_Full_Version();

로컬에서 빠르게 테스트할 거면 도커 이미지가 제일 편합니다. PostgreSQL + PostGIS가 같이 들어 있어요.

1
2
3
4
docker run --name postgis \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  -d postgis/postgis:16-3.4

geometry vs geography

PostGIS에는 위치를 저장하는 타입이 크게 두 가지 있습니다.

  • geometry: 평면(데카르트) 좌표계. 계산이 빠르지만, 위경도를 그대로 넣으면 “거리”가 미터가 아니라 도(degree) 단위로 나옵니다.
  • geography: 지구를 타원체로 보고 계산. 위경도(SRID 4326)를 넣으면 거리 결과가 미터로 나와서 직관적입니다. 대신 살짝 느립니다.

좌표계는 SRID로 구분합니다. 우리가 흔히 쓰는 GPS 위경도(WGS84)가 SRID 4326 입니다.

테이블 만들고 데이터 넣기

1
2
3
4
5
6
7
8
9
CREATE TABLE stores (
    id   bigserial PRIMARY KEY,
    name text NOT NULL,
    geom geometry(Point, 4326)   -- 점, WGS84
);

-- ST_MakePoint(경도, 위도) 순서에 주의! (x=lng, y=lat)
INSERT INTO stores (name, geom)
VALUES ('강남점', ST_SetSRID(ST_MakePoint(127.0276, 37.4979), 4326));

가장 많이 헷갈리는 부분이 ST_MakePoint는 (경도, 위도) 순서라는 점입니다. 사람은 보통 “위도, 경도”로 말하니까 반대로 넣기 쉬워요.

반경 검색

이제 “특정 지점에서 1km 안의 매장”을 찾아봅시다. geography로 캐스팅하면 ST_DWithin의 거리 단위가 미터가 됩니다.

1
2
3
4
5
6
7
8
9
10
SELECT id, name,
       ST_Distance(geom::geography,
                   ST_SetSRID(ST_MakePoint(127.0286, 37.4980), 4326)::geography) AS dist_m
FROM stores
WHERE ST_DWithin(
        geom::geography,
        ST_SetSRID(ST_MakePoint(127.0286, 37.4980), 4326)::geography,
        1000               -- 1km
      )
ORDER BY dist_m;

하버사인 공식을 손으로 짜던 것에 비하면 훨씬 읽기 좋죠.

공간 인덱스 (GiST)

공간 검색을 빠르게 하려면 일반 B-tree가 아니라 GiST 인덱스를 써야 합니다.

1
CREATE INDEX idx_stores_geom ON stores USING GIST (geom);

ST_DWithin 같은 함수는 이 GiST 인덱스를 활용해서 후보를 먼저 좁힌 뒤 정밀 계산을 합니다. 인덱스가 없으면 전체 행을 다 계산하니, 데이터가 많아지면 반드시 걸어주세요.

정리

  • 위치 데이터를 다룰 거면 위경도 숫자 컬럼 대신 PostGIS를 쓰는 게 코드도 성능도 좋습니다.
  • 거리 결과를 미터로 받고 싶으면 geography(SRID 4326).
  • ST_MakePoint(경도, 위도) 순서 주의.
  • 공간 검색에는 GiST 인덱스 필수.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.