이번 포스트에서는 MySQL과 Kotlin을 사용해 폴리곤 데이터를 기반으로 지역 검색을 구현한 과정을 공유하려 합니다. 핵심 목표는 다중 폴리곤(multipolygon) 데이터를 활용한 지역 기반의 지리적 검색을 처리하는 것으로, 공간 데이터 타입과 지리적 함수들을 사용하는 것이 주요 과제였습니다.
이 구현의 핵심은 AREA 테이블로, 상권 구역을 나타내는 COORDINATES 컬럼에 다중 폴리곤 데이터를 저장했습니다. 이를 통해 점-폴리곤 포함 여부(Point-in-Polygon)나 경계 상자를 이용한 검색(Bounding Box Search) 같은 공간 쿼리를 수행할 수 있습니다. 테이블 스키마는 다음과 같습니다:
CREATE TABLE `AREA` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '지역 아이디',
`CITY_CODE` int(10) DEFAULT NULL COMMENT '시도코드',
`CITY` varchar(100) DEFAULT NULL COMMENT '지역 이름',
`DISTRICT` varchar(100) DEFAULT NULL COMMENT '구 이름',
`TRADE_NAME` varchar(100) DEFAULT NULL COMMENT '상권이름',
`COORDINATES` multipolygon DEFAULT NULL COMMENT '좌표',
`CREATED_AT` datetime NOT NULL DEFAULT current_timestamp() COMMENT '생성일',
`UPDATED_AT` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '수정일',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='지역 데이터 테이블';
여기서 COORDINATES 필드는 MySQL의 multipolygon 데이터 타입을 사용하여 복잡한 지역 경계를 저장합니다. 이를 활용해 MySQL의 내장 공간 함수인 ST_Contains와 ST_Intersects를 통해 폴리곤 기반 검색을 수행할 수 있었습니다.
@Query("""
SELECT ...
FROM AreaEntity t
LEFT JOIN FloorEntity f ON t.id = f.areaId
WHERE ST_Contains(t.coordinates, ST_GeomFromText(:point)) = true
AND t.city = :city AND t.district LIKE :district
""")
fun findByCoordinatesContaining(city: String, district: String, @Param("point") point: String): List<Map<String, Any>>
@Query("""
SELECT ...
FROM AreaEntity t
LEFT JOIN FloorEntity f ON t.id = f.areaId
WHERE ST_Intersects(t.coordinates, ST_GeomFromText(:boundingBox)) = true
""")
fun findByCoordinatesOverlappingBoundingBox(@Param("boundingBox") boundingBox: String): List<Map<String, Any>>
MySQL의 지리적 기능과 Kotlin의 함수형 프로그래밍 기능을 활용하여 점-폴리곤 포함 여부(Point-in-Polygon)와 경계 상자 검색(Bounding Box Search) 모두를 처리할 수 있는 견고한 시스템을 구현했습니다. 이는 지역 기반 검색이 필요한 다양한 지리적 응용 프로그램에서 매우 유용하게 사용될 수 있습니다.
향후에는 폴리곤 데이터에 대한 인덱싱을 추가로 최적화하고, 더 복잡한 쿼리를 위한 다른 공간 함수들도 탐색해볼 계획입니다.