Mysql 공간 데이터 정리
NestJS를 사용하는 프로젝트를 진행하다가 사용자의 경로 데이터를 저장해야 하는 상황이 생겨 공간 데이터를 사용하게 되었습니다. 공간 데이터 활용을 위해 공부했던 내용을 정리하고자 합니다.
공간 데이터베이스란?
공간 데이터베이스란 말 그대로 공간 정보를 저장할 수 있는 데이터베이스를 뜻합니다. 좀 더 명확한 개념을 이야기해보자면 공간에 존재하는 점, 선, 폴리곤등을 포함하는 객체의 데이터를 저장하고, 검색하는데 최적화된 데이터베이스라고 정의할 수 있습니다.
공간 데이터베이스는 단순히 공간 데이터를 저장해줄 뿐 아니라 공간 데이터를 활용한 공간 함수을 함께 제공합니다.
Mysql의 공간 데이터 타입
Mysql에서는 공간 데이터를 저장할 수 있도록 타입을 제공하고 있습니다.
타입 | 정의 | 예시 |
---|---|---|
Point | 좌표 공간의 한 지점 | POINT(10 10) |
LineString | 다수의 Point를 연결해주는 선분 | LINESTRING(10 10, 20 20, 30 30) |
Polygon | 다수의 선분들이 연결되어 닫혀있는 상태 | POLYGON((10 10, 10 20, 20 10, 10 10)) |
Multi-Point | 다수의 Point 집합 | MULTIPOINT(10 10, 20 20) |
Multi-LineString | 다수의 LineString 집합 | MULTILINESTRING((10 10, 20 20), (15 15, 25 25)) |
Multi-Polygon | 다수의 Polygon 집합 | MULTIPOLYGON(((10 10, 10 20, 20 10, 10 10)), ((40 40, 30 30, 40 40))) |
GeomCollection | 모든 공간 데이터들의 집합 | GEOMETRYCOLLECTION(POINT(10 10), LINESTRING(20 20, 30 30)) |
Mysql의 공간 함수
Mysql에서는 다양한 공간 함수를 제공하고 있습니다. 그 중에서 자주 사용되는 함수들은 아래의 표와 같습니다.
공간 연산 함수 | 기능 |
---|---|
ST_Intersection (g1 Geometry, g2 Geometry) : Geometry | g1과 g2의 교집합인 공간 객체를 반환한다 |
ST_Buffer (g1 Geometry, d Double ) : Geometry | g1에서 d 거리만큼 확장된 공간 객체를 반환한다 |
ST_StartPoint (l1 LineString) : Point | l1의 첫 번째 Point를 반환한다 |
ST_EndPoint (l1 LineString) : Point | l1의 마지막 Point를 반환한다 |
ST_PointN (l1 LineString) : Point | l1의 n번째 Point를 반환한다 |
공간 관계 함수 | 기능 |
---|---|
ST_Equals (g1 Geometry, g2 Geometry): Boolean | g1과 g2가 동일하면 True, 다르면 False를 반환한다 |
ST_Intersects (g1 Geometry, g2 Geometry): Boolean | g1과 g2의 교집합이 존재하면 True, 존재하지 않으면 False를 반환한다 |
ST_Contains (g1 Geometry, g2 Geometry): Boolean | g2가 g1 영역 안에 포함되는 경우 True, 그렇지 않으면 False를 반환한다 |
ST_Distance (g1 Geometry, g2 Geometry): Double | g1과 g2간의 거리를 반환한다 |
다양한 함수들이 많으니 공식 문서를 참고해서 원하는 기능의 함수를 잘 찾아서 사용하면 좋을 것 같습니다. Mysql 공간 함수
Example
지금부터는 NestJS에서 TypeORM을 활용하여 공간 데이터를 사용하고 저장하는 방법에 대해 알아보곘습니다.
(1) Entity
TypeORM
과wkx
라이브러리를 사용하여 정의한 Route 엔티티입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { Point, LineString } from 'wkx';
@Entity()
export class Route {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'point' })
startPoint: Point;
@Column({ type: 'linestring' })
route: LineString;
}
여기서 사용한 wkx
는 Well Known Text
의 약자로, 이해하기 쉬운 문자열로 데이터를 표현하는 방식이며 이를 이용해서 GIS(공간 정보 시스템)의 공간 데이터의 Row 단위를 나타낼 수 있습니다. wkx
를 사용하면 mysql에서 지원하는 공간 데이터 타입들을 표현할 수 있기에 startPoint와 route 컬럼의 타입을 정의해주었습니다.
npm을 통해 설치해주어야 사용이 가능합니다. npm-wkx
(2) DTO
프론트에서 전달받는 위치 정보는 latitude
와 longitude
로 이루어진 배열 데이터라고 가정하겠습니다.
이는 eachPoint
타입을 정의해서 사용하였고 경로 데이터는 2개 이상의 Point가 필수적으로 필요하기 때문에 @ArrayMinSize(2)
으로 설정해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
import { IsArray, ArrayMinSize } from 'class-validator';
type eachPoint = {
latitude: number;
longitude: number;
};
export class CreateRouteDto {
@IsArray()
@ArrayMinSize(2)
readonly routeData: eachPoint[];
}
(3) Service
Point 타입과 LineString 타입 데이터를 저장할 때에는 위의 표에 있는 예시와 같이 입력해줘야 하기 때문에 그에 맞게 데이터 형식을 바꿔주는 작업이 필요합니다.
데이터 저장은 TypeORM의 queryBuilder
를 사용하여 세부적인 쿼리문을 작성할 수 있으며, ST_GeomFromText
함수를 사용해서 문자열 데이터를 공간 데이터 타입으로 변환하여 저장할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async create(createRouteDto: CreateRouteDto) {
const { routeData } = createRouteDto;
const startPoint = `${routeData[0].latitude} ${routeData[0].longitude}`;
const route = routeData.map((v) => {
return `${v.latitude} ${v.longitude}`;
});
const linestring = route.join(',');
try {
await this.routeRepository
.createQueryBuilder()
.insert()
.into(Route)
.values({
startPoint: () => `ST_GeomFromText('POINT(${startPoint})')`,
route: () => `ST_GeomFromText('LINESTRING(${linestring})')`,
})
.execute();
} catch (err) {
throw err;
}
}
(4) 결과 확인
1
2
3
4
5
6
7
8
9
10
// request-body
{
"routeData": [{
"latitude": 0,
"longitude": 0
}, {
"latitude": 1,
"longitude": 1
}]
}
DB에서 select문을 통해 결과를 확인하고 싶을 때에는 st_astext
함수를 사용하면 됩니다.
공간 함수도 잘 동작하는 것을 확인할 수 있습니다.
참고
https://orkhan.gitbook.io/typeorm/docs/entities#spatial-columns https://dev.mysql.com/doc/refman/5.7/en/spatial-types.html https://youngwoon.tistory.com/3 https://sparkdia.tistory.com/24
Comments