
이 문서는 진행한 사이드 프로젝트에서 여행 경로와 위치 데이터를 저장하고 조회하기 위해 PostGIS를 적용한 전체 과정을 정리한 가이드입니다.
PostGIS를 선택한 이유부터 Docker 기반 설치, Spring Boot 연동, 주요 공간 함수 활용까지 전체 흐름을 처음부터 끝까지 설명합니다.
이 글을 통해 다음 내용을 적어보았어요
- PostGIS를 선택한 이유와 그 장점
- PostgreSQL + PostGIS 설치 과정
- Spring Boot 환경에서 Geometry 타입 매핑 및 설정 방법
- 주요 공간 함수(ST_DWithin, ST_MakeLine 등) 사용 예제
- PostGIS 적용 중 마주한 문제와 그 해결 팁
배경

현재 이길어때라는 프로젝트를 진행하고 있으며, 핵심 요구사항은 사용자가 다녀온 여행 코스를 기록하고 저장하는 기능입니다.
이를 구현하기 위해서는 위치 기반 데이터를 효율적으로 저장하고 빠르게 조회할 수 있는 구조가 필요했고, 그 결과 PostgreSQL + PostGIS 조합을 선택했습니다.
처음에는 PostGIS 적용 방식이 생소해 시행착오를 겪었고, 같은 문제를 반복하지 않기 위해 이번 적용 과정을 정리해 공유합니다
Why postGIS
위치 데이터 처리를 위한 확장 기능, PostGIS

PostGIS는 PostgreSQL에 공간 기능을 추가하는 확장 모듈로, 좌표·경로·다각형 등 공간 데이터를 효율적으로 저장하고 조회할 수 있도록 여러 함수를 제공합니다.
대표적인 기능은 다음과 같습니다
- 지도 상 특정 위치 조회
- 반경 기반 검색 및 거리 계산
- 지리적 패턴 분석
- 복잡한 네이티브 쿼리 없이 간단한 함수로 처리
저희 팀의 경우 MyBatis 경험자가 저뿐이어서,
직접 Haversine 공식을 SQL로 구현하는 대신 PostGIS의 거리 계산 함수로 간단히 대체할 수 있었던 점이 큰 장점이었습니다.
예시로 보는 PostGIS의 강력함
먼저, 기존에 반경 기반 검색을 구현할 때 사용하던 Haversine 공식 기반 쿼리는 다음과 같습니다
SELECT *,
(6371 * acos(
cos(radians(latitude)) * cos(radians(point.latitude)) *
cos(radians(point.longitude) - radians(longitude)) +
sin(radians(latitude)) * sin(radians(point.latitude))
)) AS distance
FROM point
HAVING distance < radius
ORDER BY distance;
이 방식의 문제는 다음과 같습니다
- 쿼리만 보면 의도를 바로 파악하기 어렵다
- 계산식이 복잡해 유지보수가 힘들다
- 매번 동일한 패턴의 쿼리를 직접 작성해야 한다
반면, PostGIS를 사용하면 동일한 기능을 다음과 같이 구현할 수 있습니다
SELECT *
FROM point
WHERE ST_DWithin(
geom,
ST_SetSRID(ST_MakePoint(latitude, longitude), 4326),
radius
);
이 점이 PostGIS를 선택하게 된 결정적인 이유였습니다.
프로젝트 환경 정보
- Java: 21
- Spring Boot: 3.2.0
- PostgreSQL: 16.1
- PostGIS: 3.4.1
Docker로 postgreSQL 실행하기
아래 과정은 Docker가 사전에 설치되어 있다는 가정하에 진행합니다.
1. PostgreSQL 컨테이너 실행
docker run --name container-name \
-e POSTGRES_PASSWORD=password \
-p 5432:5432 \
-d postgres
- 컨테이너 이름: container-name
- 비밀번호: password
- 포트 매핑: 5432 → 5432 (PostgreSQL 기본 포트)
2. 실행 중인 컨테이너 접속
docker exec -it container-name bash
3. PostGIS 설치
apt-get update
apt-get install postgis postgresql-16-postgis-3
4. psql 접속
psql -U postgres
5. 데이터베이스 생성
CREATE DATABASE "db-name";
6. PostGIS 확장 설치
CREATE EXTENSION postgis;
이제 PostGIS가 활성화된 PostgreSQL 환경 구성이 완료되었습니다.
Spring Boot와 PostgreSQL 연동하기
1. build.gradle에 의존성 추가
dependencies {
runtimeOnly 'org.postgresql:postgresql'
implementation 'org.postgresql:postgresql'
implementation 'org.hibernate:hibernate-core:6.4.0.Final'
implementation 'org.hibernate:hibernate-spatial:6.4.0.Final'
}
PostGIS 지원을 위해 hibernate-spatial 의존성을 함께 추가합니다.
2. application.yml 설정
spring:
jpa:
database: postgresql
database-platform: org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect
datasource:
url: {DB주소}
username: postgres
password: password
driver-class-name: org.postgresql.Driver
database-platform을 PostGIS Dialect로 설정해야 Geometry 타입을 정상적으로 인식합니다.
3. 서버 실행

Docker로 PostgreSQL 컨테이너를 실행한 후,
Spring Boot 애플리케이션이 정상적으로 연결된다면 연동이 완료된 것입니다.
SpringBoot에서 Geometry 타입 사용하기
Spring Boot에서 위치 정보를 저장하려면 jts.geom 패키지의 Geometry 객체를 사용합니다.
대표적으로 단일 좌표를 저장할 때는 Point 타입을 사용합니다.
예시 엔티티는 다음과 같습니다
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Spot {
@Column(columnDefinition = "geometry(Point,4326)")
private Point location;
// 그 외 속성들...
}
columnDefinition에 geometry(Point,4326)을 지정하면
Hibernate가 Geometry 타입을 인식하고 PostGIS 스키마에 맞춰 컬럼을 생성합니다.
ddl-auto 옵션을 활성화한 뒤 테이블이 정상 생성되는지 확인하면 기본 설정이 완료됩니다.

PostGIS 기능 테스트하기
Spring Data JPA 연동이 완료되었다면, 이제 공간 함수가 정상 동작하는지 확인해보겠습니다.
아래 예시는 DataGrip에서 실행한 테스트 쿼리입니다.
두 지점을 연결하는 직선 생성
SELECT ST_AsText(
ST_MakeLine(
ST_SetSRID(ST_MakePoint(126.9780, 37.5665), 4326), -- 서울의 좌표
ST_SetSRID(ST_MakePoint(129.0757, 35.1796), 4326) -- 부산의 좌표
)
) AS line_seoul_to_busan;
쿼리가 정상적으로 실행되면 서울과 부산을 잇는 직선(LineString)이 반환됩니다.
이를 통해 PostGIS 함수가 올바르게 동작함을 확인할 수 있습니다.

오늘은 제 PostGIS를 선택한 이유부터
PostgreSQL + PostGIS 설치, Spring Boot 연동, Geometry 타입 매핑,
그리고 기본 공간 함수 테스트까지 전체 과정을 정리했습니다.
다음 글에서는 PostGIS의 다양한 공간 함수를 실제 비즈니스 로직에서 어떻게 활용할 수 있는지 보다 구체적인 사례와 함께 소개해볼게요 :)
'Backend > Spring 🌱' 카테고리의 다른 글
| 스프링 컨테이너 이해하기: BeanFactory와 ApplicationContext (0) | 2024.03.12 |
|---|---|
| Spring MVC에서 HandlerMapping과 HandlerAdapter를 나눈 이유 (0) | 2024.02.07 |
| 이벤트 기반으로 파일 업로드 기능 구현하기 (0) | 2024.02.07 |
| 전략 패턴으로 확장성 있게 소셜 로그인 설계하기 (0) | 2024.02.07 |
| 스프링부트 프로젝트 시작하는 법 (0) | 2024.02.02 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆