Post

Mysql 공간 데이터 정리

NestJS를 사용하는 프로젝트를 진행하다가 사용자의 경로 데이터를 저장해야 하는 상황이 생겨 공간 데이터를 사용하게 되었습니다. 공간 데이터 활용을 위해 공부했던 내용을 정리하고자 합니다.

본 포스트는 아래의 환경을 기준으로 작성되었습니다.
typeorm 0.3.0
Mysql 5.7

공간 데이터베이스란?

공간 데이터베이스란 말 그대로 공간 정보를 저장할 수 있는 데이터베이스를 뜻합니다. 좀 더 명확한 개념을 이야기해보자면 공간에 존재하는 점, 선, 폴리곤등을 포함하는 객체의 데이터를 저장하고, 검색하는데 최적화된 데이터베이스라고 정의할 수 있습니다.

공간 데이터베이스는 단순히 공간 데이터를 저장해줄 뿐 아니라 공간 데이터를 활용한 공간 함수을 함께 제공합니다.

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) : Geometryg1과 g2의 교집합인 공간 객체를 반환한다
ST_Buffer (g1 Geometry, d Double ) : Geometryg1에서 d 거리만큼 확장된 공간 객체를 반환한다
ST_StartPoint (l1 LineString) : Pointl1의 첫 번째 Point를 반환한다
ST_EndPoint (l1 LineString) : Pointl1의 마지막 Point를 반환한다
ST_PointN (l1 LineString) : Pointl1의 n번째 Point를 반환한다
공간 관계 함수기능
ST_Equals (g1 Geometry, g2 Geometry): Booleang1과 g2가 동일하면 True, 다르면 False를 반환한다
ST_Intersects (g1 Geometry, g2 Geometry): Booleang1과 g2의 교집합이 존재하면 True, 존재하지 않으면 False를 반환한다
ST_Contains (g1 Geometry, g2 Geometry): Booleang2가 g1 영역 안에 포함되는 경우 True, 그렇지 않으면 False를 반환한다
ST_Distance (g1 Geometry, g2 Geometry): Doubleg1과 g2간의 거리를 반환한다

다양한 함수들이 많으니 공식 문서를 참고해서 원하는 기능의 함수를 잘 찾아서 사용하면 좋을 것 같습니다. Mysql 공간 함수

Example

지금부터는 NestJS에서 TypeORM을 활용하여 공간 데이터를 사용하고 저장하는 방법에 대해 알아보곘습니다.

(1) Entity

TypeORMwkx 라이브러리를 사용하여 정의한 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;
}

여기서 사용한 wkxWell Known Text의 약자로, 이해하기 쉬운 문자열로 데이터를 표현하는 방식이며 이를 이용해서 GIS(공간 정보 시스템)의 공간 데이터의 Row 단위를 나타낼 수 있습니다. wkx를 사용하면 mysql에서 지원하는 공간 데이터 타입들을 표현할 수 있기에 startPoint와 route 컬럼의 타입을 정의해주었습니다.

npm을 통해 설치해주어야 사용이 가능합니다. npm-wkx

(2) DTO

프론트에서 전달받는 위치 정보는 latitudelongitude로 이루어진 배열 데이터라고 가정하겠습니다.
이는 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
    }]
}

st_astext 결과

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

This post is licensed under CC BY 4.0 by the author.

Comments