Data Engineering

키바나 Map 활용 - 서울시 지하철 위치 데이터 (Index template)

728x90
반응형

들어가며..

시험기간엔 역시 공부 빼고 다 재미있다.

그래서 엘라스틱 스택 중 가장 화려한 친구인 "키바나"를 가지고 

가장 화려한 시각화인 "지도"를 활용해보는 내용을 준비했다.

 

참고로 키바나의 map을 사용하면 다음과 같이 재미난 친구들을 만들 수 있다!

왼쪽부터 미니 프로젝트 / 서울시 지하철 역사 / 서울시 구별 세금 납부 시각화

세 개를 연달아두다 보니 잘 안보 이긴 하지만

오늘은 셋 중에 가운데 친구를 한번 만들어보려고 한다!

 


데이터는?

우선 데이터는 공공 데이터 포털, 서울시 열린 데이터 광장에서 쉽게 찾을 수 있는 

서울시 지하철 데이터를 기반으로 추가적인 전처리 작업을 조금 한 뒤에

"지하철 호선", "역명", "위도", "경도", "역사가 위치한 지역 구명"

이렇게 5개의 칼럼을 가진 친구로 만들어 주었다.

데이터 형태

서울시 내에 위치한 역을 기준으로 데이터를 전 처리해주었고

2019년 데이터이다 보니 지금과는 조금 다를 수도 있다..!

 


엘라스틱 & 키바나 실행

사실 블로그를 시작하면서 이 부분을 가장 먼저 그리고 자세히

다뤄야 할 거 같다는 생각은 꾸준히 했지만 영 재미있는 내용은 아니라

계속 미루고 있다.. 그래서 오늘도 그냥 간단히 보여주는 정도만..!

 

우선 클러스터 구성을 어떤 형태로 해야 할지 고민을 해봐야 한다.

일반적으로 엘라스틱에서 권장하는 조건을 충족하는 최소 구성은

3 노드의 형태인데 그냥 구색만 갖출 겸 2개만 띄우는 걸로 난 결정 했다....

 

엘라스틱 클러스터에 대해 좀 더 자세히 알고 싶으신 분들은 아래 글을 추천드립니다!

2021.09.26 - [Elastic] - 2.1.1 엘라스틱 서치의 구성 요소 및 구조 (클러스터)

 

elasticsearch.yml 일부

간단하게 노드 구성 부분을 살펴보면 

node.name으로 이름을 지정해주고 그 아래는 해당 노드가

어떤 역할을 수행하고, 안 할지 정해주는 부분이다.

이 부분은 또 다른 형태로 작성하는 방법도 있긴 하다!!

 

노드 2개 실행으로 status가 green으로 변경

노드를 두 개 띄우게 되면 프라이머리 샤드 1개 당 레플리카 샤드 1개 이상이라는 

클러스터 상태 green 조건을 만족하게 되어 status가 초록불로 바뀐 걸 확인할 수 있다.

그리고 내가 왜 굳이 노드를 3개 다 안 올렸냐면,,

메모리 먹는 괴물이다...

내가 자바 힙 사이즈를 노드 당 4기가로 설정해놔서

3개를 띄우면 최소 12기가를 먹게 된다..

2개를 띄웠는데도 다른 프로그램들을 다 합쳐서 32기가 중 24기가 정도를 차지하고 있으니

굳이 굳이 로컬에서 클러스터의 장점을 누리고 싶어!! 하시는 분들 아니면

그냥 1개에서 2개 정도 노드로 공부하시는 걸 권장드린다..

아 그리고 힙 사이즈는 그냥 기본 1기가로 두시고,,,

 

키바나까지 실행시키고, 브라우저에서 localhost:5601을 입력하면

이렇게 키바나가 뜨게 된다!

키바나 실행

이제 반 이상 왔다..!

 


로그스태시로 데이터 전송

자 이제 데이터를 받아줄 엘라스틱서치랑 시각화를 해줄 키바나까지

모두 정상적으로 실행시켜줬으니 데이터만 전송하면 된다.

 

데이터는 csv파일 형태로 저장되어 있어서

로그스태시 필터 중 csv 필터를 사용할 예정이다.

로그스태시 config 파일을 작성해서 다이내믹 매핑으로 발생할 수 있는

data field type오류를 사전에 방지해주고 데이터를 전송해주었다.

 

로그스태시 config 파일 작성법에 대해서는 최대한 빠른 시일내로 정리해서

글을 하나 올려볼 생각이다.

 

로그스태시 실행

로그스태시 실행은 정석 중에 정석 -f 옵션을 주는 형태로

사용할 config 파일을 지정해주었고

터미널이 더러워지는 걸 두고 볼 수 없어서 

codec에 dots를 적용시켜 데이터 하나 당 점이 하나씩 찍히게 설정해주었다.

 

 

그리고 전송된 데이터를 키바나의 dev tools에서 확인해보았다.

GET seoul_subway_test/_search
GET seoul_subway_test/_mapping

데이터 자체와 데이터의 필드 타입 매핑을 출력해본 결과

정상적으로 전송된 것을 확인할 수 있다.

데이터 검색

 

 


키바나 활용

데이터도 정상적으로 전송됐으니 본격적으로 키바나를 활용해보자

먼저 새롭게 추가된 인덱스(데이터)를 기반으로 인덱스 패턴을 생성하고

바로 map에 적용시켜 보겠다.

 

키바나 Map에서 위, 경도 값을 가지는 인덱스 패턴

"seoul_subway_te*"를 사용해 layer를 추가해주려고 했지만

빨간 경고문구가 뜨면서 정상적으로 실행되지 않는다!

 

사실 이 부분은 일부로 의도해서 만든 상황인데

경고 문구를 간단히 해석해보면

"인덱스 패턴에 지오 타입 필드가 존재하지 않습니다."

정도로 해석할 수 있다.

 

분명 우리는 위, 경도 데이터를 가진 인덱스를 생성해주었는데

왜? 이런 일이 발생하는 걸까

 


엘라스틱서치 필드 타입 이해

이 부분에 대해 답을 알기 위해서는 한 가지를 더 알아야 한다.

바로 엘라스틱에서 사용 가능한 필드(데이터) 타입의 종류이다.

 

간단하게 엘라스틱 공식 document에 검색을 해보자

https://www.elastic.co/guide/index.html

검색을 해보자

엘라스틱 documentation에서 검색을 할 때는

"기술 스택" "버전" "검색 내용"

이런 식으로 검색하면 원하는 결과를 90% 이상 한 번에 찾을 수 있다.

 

버전 같은 경우에는 굳이 안 써줘도 되는데 현재 자신이 사용하는 버전과

최신 버전 사이에 기능 차이가 존재할 수 있기 때문에 넣는 걸 추천한다.

 

검색어에 대해 자동 추천해준 게시글 맨 위 친구를 클릭해보면

쉽게 우리에게 발생한 문제의 원인을 찾을 수 있다.

 

찾았다 geo_point

여러 가지 데이터 타입이 있지만, 그중에서 엘라스틱에는

"geo_point"라고 불리는 위, 경도 값을 나타내는 타입이 따로 존재한다.

앞서 우리가 위, 경도에 해당하는 "lat"과 "lon"을 float 타입으로 매핑시켜주었기 때문에

엘라스틱에서는 해당 데이터를 위, 경도 값으로 인식하지 못하게 된 것이다.

 

그럼 어떻게 해야 할까?

여기서 엘라스틱 서치의 Dynamic mapping 특성을 아시는 분이라면

"그럼 그냥 매핑 타입 지정 안 해주면 되는 거 아니야?"

라고 할 수 있다. 하지만 이 또한 정답은 아닌데

엘라스틱 서치의 동적 매핑에는 함정이 존재하기 때문이다.

 

간단히 얘기하면 엘라스틱에서는 동적 매핑을 통해 들어온 데이터에 대해

해당 데이터를 수용할 수 있는 가장 큰 범위의 데이터 타입으로 매핑을 해주게 된다.

 

이를 위, 경도에 적용시켜 생각해보면  위, 경도 값의 경우

"38.331221",  "22.33333"처럼 소수점이 존재하는 실수 형태이다. 

 

그래서 엘라스틱에서는 이 값들을 위, 경도가 아닌

실수(float, double)로 인식해버리게 되는 문제가 생기는 것이다.

그렇기 때문에 우리가 다이내믹 매핑을 이용하건, 실제로 매핑을 지정해주건

엘라스틱은 위, 경도 데이터를 제대로 인식하지 못하는 문제가 생기게 된다.

 


Index Template 사용

이쯤 되면 정녕 방법이 마땅치 않은 것인지 고민이 될 수밖에 없다.

그래서 이제 그 방법을 다뤄보도록 하겠다.

바로 "인덱스 템플릿"을 사용하는 것이다.

 

인덱스 템플릿은 말 그대로 우리가 생성하고 수집하고 저장할 인덱스에 대해

미리 템플릿, 그러니까 일종의 틀을 미리 만들어 놓는 개념이다.

 

특정 인덱스 이름을 가진 또는 포함한 인덱스가 추가될 경우

자동으로 해당 인덱스의 "틀"을 가져오고 그것에 맞게 데이터 타입 등을

맞춰주는 형태의 기능이라고 할 수 있다.

 

이 기능을 사용하면 대표적으로 로그스태시 config 파일이 간결해진다는 장점이 있고

이를 통해 데이터 파이프라인 구축에 있어 한 단계 높은 자동화가 가능해진다.

그리고 별개로 특별한 타입들에 대해 (지오, 날짜 등) 조금 더 손쉽게 저장하고 관리할 수 있다는 장점도 가지고 있다.

 

그럼 바로 인덱스 템플릿을 생성해보자!

인덱스 템플릿은 키바나의 Dev tools에서 간단하게 생성 가능하다.

위와 같이 PUT 커맨드를 사용해서 인덱스 템플릿을 생성해줄 수 있다.

PUT _index_templates/{인덱스 템플릿 이름}
{
	"index_patterns": [{매칭시킬 인덱스 이름 / *(와일드 카드 자주 사용)}],
    "template: {
    	"settings: {
        	# 해당 인덱스가 가질 샤드의 개수
        	"number_of_shards": 1
        },
        # 매핑 타입 지정
        "mappings": {
        	"properties": {
            	"locations": { # locations라는 필드가 있을 경우
                	"type": "geo_point" # 그 타입을 geo_point로 매핑
                }
            }
        }        
    }
}

그리고 커맨드 + 엔터를 눌러서 실행시키면

정상적으로 생성

제대로 인덱스 템플릿이 생성되는 걸 확인할 수 있다!

 

다음으로 로그스태시 config 파일을 조금 수정해주어야 한다.

단순히 매핑을 할 수 있게 해 주는 게 아니라, locations라는 필드에 맞게

데이터를 넣어줘야 하기 때문에 필요한 작업이라고 생각하면 된다.

하나씩 자세히 살펴보자

우선 기존과 동일하게 csv 필터를 사용하고, comma를 기준으로 데이터를 분리

각 필드를 매핑시켜주는 것 까지 동일하다.

 

그 이후에 몇 가지 작업이 추가되는데

add_filed를 통해 locations라는 필드 아래 lat과 lon 하위 필드를 생성해주고

그 값을 우리가 앞서 설정한 lat, lon 값으로 지정해준다.

그리고 그 값들을 float 타입으로 매핑시켜주면 된다.

 

이렇게 되면 우리가 인덱스 템플릿을 통해 locations라는 geo-point 필드를 만들어 둔 곳에

float 타입을 가지는 locations[lat], locations[lon]이 들어가게 된다.

작성된 config 파일을 기준으로 로그스태시를 실행시키면 되는데

한 가지 중요한 점은 우리가 생성해둔 템플릿에 맞게 인덱스가 들어가야 하기 때문에

새로 전송하는 인덱스의 이름을 잘 지정해줘야 한다는 점이다.

 

앞서 생성한 템플릿에서 

"seoul_subway*" 형태를 매핑시켜주겠다고 지정했으니

우리도 인덱스를 생성할 때 그 이름을 동일한 형태로 해줘야지 우리가 의도한 대로 생성되게 된다.

config 파일의 output 영역에 인덱스 명을

"seoul_subway*"의 형태에 매칭 되는

"seoul_subway_real"로 작성해둔 것을 볼 수 있다.

(여기서 *는 와일드카드를 의미한다.)

 

로그스태시 재실행

이제 새롭게 수정한 config를 기반으로 로그스태시를 다시 실행해보겠다.

동일한 파일을 읽어오는 것이기 때문에 config sincedb_path에 /dev/null 옵션을 주었고,

* 로그스태시는 읽은 파일에 대해 어디까지 읽었는지 기록해두고 동일한 데이터는 다시 읽지 않는다.

그렇기 때문에 sincedb_path에 /dev/null 옵션을 주어  읽은 위치에 대한 기록을 없애 처음부터 데이터를 전송할 수 있게 하는 것

 

해당 옵션은 sudo (관리자 권한)으로 실행시켜야 하기 때문에 실행문의 형태가 조금 달라졌다.

그리고 정상적으로 데이터가 전송된 것을 확인할 수 있다.

데이터 전송 성공

앞선 인덱스와 다르게 locations 필드 아래위, 경도 값이 제대로 들어간 것을 볼 수 있다.

그리고 다시 인덱스 패턴을 생성해주고, 키바나의 맵을 사용 하러 가보자!

 


진짜 키바나 Map 활용

자 드디어 본 게임이다.

인덱스 패턴을 생성한 후 좌측에 메뉴바를 열어보면

키바나 - 애널리틱스에 "map" 메뉴가 있다.

Map 메뉴 찾기

 

 

해당 메뉴를 클릭하면 다음과 같이 저장된 맵들과

새로운 맵을 추가할 수 있는 화면이 나오게 된다.

Map 화면

우리는 우측 상단에 위치한

"Create map"을 통해 새 맵을 생성할 것이다.

 

 

 

Add layer 버튼을 클릭해 기본적으로 존재하는 지도 위에

여러 가지 시각화를 나타낼 수 있는데, 우선 지하철역 위치정보를 나타내기 위해서

elastic - documents를 클릭해서 우리가 생성한 인덱스 패턴을 지정해주자

레이어 추가

 

아까와는 다르게 정상적으로 레이어가 추가되는 것을 볼 수 있다.

그리고 맵에 무언가 초록 점이 나타난 것도 확인 가능하다.

경고 없이 잘 적용된다

 

 

 

이를 조금 더 자세히 살펴보면

지도에 지하철역 위치가 나타난다.

어디선가 많이 보던 지도 위에 점(지하철역)들이 나타난 것을 볼 수 있다.

 

그럼 이제 본격적으로 시각화를 한번 생성해보자

다음과 같은 순서로 진행할 예정이다.

  1. 맵에 지역구별로 grid 그리기
  2. 지하철역 이름 표시 및 아이콘 변경
  3. 지역구별 지하철 역사 수 기반 지역구 색상 구분

 


1. 맵에 지역구별로 grid 그리기

지역구를 나누기 위해서는 새로운 레이어가 하나 필요하다.

이번에는 엘라스틱서치에서 기본적으로 제공하는 레퍼런스를 추가해 손쉽게 나타낼 수 있다.

앞서 지하철 데이터를 추가한 것처럼 add layer - reference - ems boundary를 선택해보자

EMS Boundaries

엘라스틱에서 제공하고 있는 데이터로 각 나라, 시도별로 구역을 나눌 수 있는 데이터이다.

 

 

한국을 검색해보자!

클릭한 뒤 한국을 검색해보면 두 종류가 뜨게 된다.

지역구와 시도 데이터 정도로 각각 생각할 수 있는데 우리는 지역구를 사용할 거니

위쪽에 municipalities를 선택해주자

지도에 무언가 나타난다

선택을 하면 지도가 무언가로 덮어진 것을 확인할 수 있다.

한국의 지역구 데이터이기 때문에 한국지역이 선택된 것이라고 보면 된다.

그리고 이제 우리는 "서울시"만 자세히 지역구별로 나눠볼 예정인데

이는 옆에 보이는 Term joins 기능을 사용해주면 된다.

 

일반적인 RDBMS에서의 join과 동일하게

select from join on where 이런 식으로 작동하는 친구다.

우리는 지역구를 나타내는 게 목표이기 때문에

left 필드에 해당하는 현재 레이어 데이터에 한글로 된 지역구 이름을 선택해준다.

 

그리고 right 필드에는 현재 계속 사용 중인

seoul_subway* 인덱스 패턴을 선택하고

join 할 칼럼(필드)으로 지하철 데이터 내에 존재하는 지역구 이름 데이터인

gu.keyword를 선택해준다.

 

이렇게 조인을 해주게 되면 서울 지역구만 선택되는 것을 볼 수 있다.

서울 지역만 범위가 선택되는걸 볼수 있다!

 

다음으로 지역구별로 다른 색으로 표시되게 하고

지역 구명을 지도상에 표현해보자

Layer style에서 이를 모두 설정할 수 있는데 하나씩 해보도록 하겠다.

 

먼저 아까 생성한 join에서 어떤 메트릭을 사용할지 지정해줘야 한다.

기본은 count 그러니까 데이터 개수로 되어 있는데 이를

지역구 이름을 사용하도록 설정이 필요하다.

사용할 metric 변경 / Top_term aggregation을 선택하자

aggregation에서 Top_term을 선택하면 아래와 같이

어떤 필드에 대해 연산을 할지 선택해야 한다.

지역구 이름 선택

우리는 지역구 이름을 사용할 것이기 때문에 지역구 데이터를 나타내는

gu.keyword 필드를 선택해주자

그리고 특정 지역을 클릭해보면 해당 지역구의 이름이 나타나게 된다.

metric 설정 완료

 

본격적으로 layer style을 만져보자

대충 항목을 살펴보면

  • fill 옵션 - 이미지에 영역을 어떤 색상으로 채울지 설정
  • border 옵션 - 경계선의 색과 두께 설정
  • label 옵션 - 표시되는 레이블의 폰트와 색상 설정

등이 존재한다. 그리고 각각은 Fixed 옵션과 By value 옵션이 있는데

어느 한 가지 옵션으로 통일시킬 것 인지, 혹은 특정 값에 따라 다른 형태의 설정을 가지게 할지

고를 수 있는 옵션이다.

 

그렇기에 우리는 우선 fill color 옵션을 지역구별로 다른 색을 나타내 주기 위해

by value 옵션에서 "지역구별"로 설정하게 지정해주겠다.

지역구별로 다른 색상을 표현해보자

 

좌측 이미지를 보면 지역구 별로 색이 달라진 것을 확인할 수 있다.

다만 지역구의 개수보다 사용하기로 지정된 색상의 개수가 적어

동일한 색상으로 표현되는 부분이 많이 보이는데

이는 우측 이미지처럼 더 다른 색상을 지정하거나

커스텀하여 사용하는 방식으로 변경해줄 수 있다.

 

구별로 색상 표현

경계선을 좀 더 뚜렷하게 변경하고 사용할 색상의 수를 증가시키면

위와 같이 알록달록한 형태의 서울시 지도가 보이는 시각화가 생성된다.

 

지역구의 이름을 표현하는 것은 굉장히 간단하다.

border 옵션 아래에 label 옵션을 설정해주면 된다.

label 옵션으로 지역구 이름 표시

앞선 설정들과 마찬가지로 label 옵션을

Fixed가 아닌 By value로 변경하고, "지역구" 데이터를 사용하도록

설정해주면 위 이미지와 같이 서울시 내 각 지역구별로 이름이 나타나게 된다.

 

2. 지하철 아이콘 변경 및 역 이름 표시

다음으로 지하철역을 꾸며보자 지하철 역 아이콘을 조금 더 지하철답게 바꾸고

각 노선별로 다른 색상, 역별 이름을 표시해볼 예정이다.

 

방식은 앞서 사용한 방법들과 유사하게 고정된 값으로 옵션을 주는 것이 아니라

필드 내 값 별로 설정을 달리하는 형태로 진행하면 된다.

지하철역 이름 표시하기

지하철 역명은 label 형태로 표시되기 때문에

label 설정을 by value, 그리고 지하철 역명을 그 값으로 사용하게 설정해주면 된다.

조금 지저분한 모습을 보여주는데 이는 label의 font size 조정을 통해 깔끔하게 생성할 수 있다.

지하철역 아이콘과 색상 변경

지하철역 아이콘을 변경하는 방식도 간단하다.

기본 설정인 marks 대신 우측에 icon을 클릭하고 원하는 형태의

icon을 검색해서 선택해주면 된다. 지하철 모양은 rail이라고 검색하면

3 가지 정도 나오고 그중에 원하는 것을 선택해주면 된다.

 

다음으로 아이콘 색상을 호선별로 변경해보는 것은 우측 이미지와 같이

fill color 옵션을 line.keyword 값 별로 지정해주도록 설정해주면 된다.

 

이미지가 작아서 확대해보면

지하철 호선별로 다른 색상을 띄는 것을 보여준다

각 호선별로 다른 색을 정상적으로 나타내 주고 있다.

 

 

3. 지역구 색상을 지역구내 지하철 개수 기반으로 표현하기

자 이제 드디어 마지막이다...!

마지막으로 어느 지역구 내에 지하철 역이 많이 존재하는지

직관적으로 알 수 있게 설정해보자

구별 지하철 역사 수를 계산할 수 있는 metric을 추가해주자

앞서도 사용했던 join에서 metric을 추가해야 한다.

구별 지하철 역사 수를 계산할 수 있게 지하철 역 이름을 기반의 

unique count를 계산해주는 aggregation을 metric으로 추가해주고

fill color 옵션을 변경

fill color 옵션을 앞서 생성한 metric인 "구별 지하철 역사 수"를 사용해서

숫자별로 색상이 달라지게 as number 옵션을 지정해주면 된다.

지하철이 많은 지역은 색이 진하게 표현된다

그럼 위 이미지와 같이 지하철이 많은 곳일수록 진한 색상으로,

적은 곳일 수록 연한 색상으로 표현할 수 있게 된다.

 

 


마무리...

오늘의 최종 결과물,, 2019년 기준 서울시 지하철 위치 시각화

후,,,, 정말 긴 포스팅이었다...

이미지도 많이 들어가고 텍스트도 많이 들어가서

생각보다 포스팅을 올리는데 시간이 많이 들어갔다.

 

중간중간 자세하게 다루지 못한 내용도 많은데 앞으로 시간 될 때마다

하나씩 올려보겠다...!

글이 너무 정신없어서 보시는 분들이 이해가 되실지 모르겠다...

 

잘못된 부분이나 궁금하신 부분은 댓글 남겨주시면 반영하도록 하겠습니다!

모두들 파이팅,,!

728x90
반응형