2020년 2월 29일 토요일

손쉽게 만드는 Strapi 기반 REST API 지원 웹사이트 개발

이 글은 누구나 손쉽게 만들 수 있는 Strapi 기반 REST API 지원 웹사이트 개발 내용을 요약한다. Strapi는 편리한 컨텐츠 관리 시스템으로 클릭만으로 REST API end point가 지원되는 웹사이트를 몇분만에 만들수 있도록 지원해주는 nodejs 기반 프레임웍이다.

Strapi 소개

Strapi는 GraphQL과 같은 다양한 플러그인을 통해 손쉽게 기능을 확장할 수 있다.

레퍼런스

쉽게 개발하는 맵박스 기반 실시간 3차원 지도 웹 서비스 개발

이 글은 매우 간단하게 3차원 동적 지도 웹 서비스 개발을 지원하는 맵박스(mapbox) 사용방법을 간략히 설명한다. 맵박스는 맞춤형 디자인 맵을 위한 오픈 소스 매핑 플랫폼이다. 맵박스를 이용해 동적 지도, 3차원 지도, 네이게이션, 증강현실, 게임 등 다양한 것을 쉽게 만들 수 있다. 맵박스는 어느 정도 이하 서비스 요청 사용자수는 무료이다(참고-과금). 정말 맵 서비스 개발에 시간이 없다면 맵박스는 정말 좋은 대안이다.

맵 박스 소개

맵박스 개발 방법 소개
mapbox 사용 방법은 다음과 같이 간단하다.

이제 폴더를 만들고 index.html 파일을 만들어 아래와 같이 코딩해 본다. mapbox 사용방법은 매우 단순하고 직관적이다. accessToken 에 mapbox API키값을 넣고, <div id='map'/> 에 해당하는 mapboxgl.Map 객체를 생성해 map 에 할당하면 된다. 
<html>
<head>
<title>First mapbox</title>
<script src='https://api.mapbox.com/mapbox-gl-js/v1.8.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v1.8.0/mapbox-gl.css' rel='stylesheet' />
</head>
<body>
<div id='map' style='width: 400px; height: 300px;'></div>
<script>
  mapboxgl.accessToken = 'pk.eyJ1IjoibWFjOTk5IiwiYSI6ImNrNzdyOXc5cjBhdWozbG9kajlwNDk3NGwifQ.2Z3kIeNZbwtVaKVl5NldaA';
  var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v11'
  });
</script>
</body>
</html>

그럼 mapbox 기본 지도를 확인할 수 있다. 맵박스는 다음같은 다양한 예제를 제공한다.

맵박스 기반 3D 빌딩 예제 개발해 보기
맵박스에서 제공하는 예제로 3D 빌딩 맵 표시 예제를 개발해 본다. 폴더를 만들고 index.html 파일을 생성한다. 그 안에 아래 코드를 붙여 넣기한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display buildings in 3D</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibWFjOTk5IiwiYSI6ImNrNzdyOXc5cjBhdWozbG9kajlwNDk3NGwifQ.2Z3kIeNZbwtVaKVl5NldaA';
    var map = new mapboxgl.Map({
        style: 'mapbox://styles/mapbox/light-v10',
        center: [-74.0066, 40.7135], zoom: 15.5, pitch: 45,
        bearing: -17.6, container: 'map', antialias: true
    });

    // map 이 load 되면, map layers에 OpenStreetMap 지도 데이터로 부터 건물 높이를 포함한
    // mapbox-streets vector 소스를 레이어로 추가한다.
    map.on('load', function() {
        // Insert the layer beneath any symbol layer.
        var layers = map.getStyle().layers;

        var labelLayerId;   // 'symbol' type 레이어 ID를 획득한다
        for (var i = 0; i < layers.length; i++) {
            if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
                labelLayerId = layers[i].id;
                break;
            }
        }

        // 3차원 건물 레이어를 추가한다.
        map.addLayer(
            {
                'id': '3d-buildings', 'source': 'composite', 'source-layer': 'building',
                'filter': ['==', 'extrude', 'true'], 'type': 'fill-extrusion',
                'minzoom': 15,
                'paint': {
                    'fill-extrusion-color': '#aaa',

                    // zoom 할 때 건물이 부드럽게 표시되도록 보간법(interpolate)을 사용
                    'fill-extrusion-height': [
                        'interpolate', ['linear'], ['zoom'],
                        15, 0, 15.05,
                        ['get', 'height']
                    ],
                    'fill-extrusion-base': [
                        'interpolate', ['linear'], ['zoom'],
                        15, 0, 15.05,
                        ['get', 'min_height']
                    ],
                    'fill-extrusion-opacity': 0.6
                }
            },
            labelLayerId
        );
    });
</script>
</body>
</html>

파이썬 HTTP 서버를 실행한 후(파이선 설치), 크롬에서 localhost:9000 을 접속하면 결과를 확인할 수 있다.
서버 실행 
실행 결과

실시간 데이터 기반 지도 개발
일반적으로 실시간 데이터를 가져오는 방법은 다양하나, 기본적으로 publication-subscriber 구조로 개발된다. 센서 데이터의 경우 데이터 토픽을 발생하는 서버와 이를 구독하는 서버가 존재한다. 보통 websocket, MQTT와 같은 프로토콜, PubNub 와 같은 메시징 서버 등을 사용해 실시간 데이터를 얻어 지도에 반영할 수 있다.

다음은 websocket, MQTT, PubNub 를 사용한 맵박스 예제들이다. 
이중 IBM의 Geofencing with IoT and Mapbox 예제는 IoT 연결 미들웨어로 많이 사용되는 오픈소스 node-red(노드레드. 사용법)를 통해 IoT 토픽 메시지를 받아 mapbox에 실시간 센서 데이터를 가시화해주는 방법을 소개하고 있다. 
Geofencing with IoT and Mapbox 예제 아키텍처(IBM)
레퍼런스
다음은 mapbox를 이용한 다양한 예제이다.

2020년 2월 27일 목요일

웹 개발 시 다중 도메인 리소스 재활용 문제 및 솔류션

이 글은 웹 개발 시 다중 도메인의 리소스를 재활용, 연동하는 경우 발생되는 문제와 솔류션을 다룬다.

웹 개발을 진행하다보면, 다른 웹사이트 서버의 리소스나 웹페이지 정보가 필요할 때가 있다. 이 경우, 단순히 해당 웹사이트 리소스 및 웹페이지를 iframe으로 가져와 조합하면 다음 같은 자연스러운 화면과 동기화된 데이터 모델에 기반한 사용자 서비스 맥락을 만들기 어렵다.

이런 접근은 보안, 데이터 동기화, 화면 디자인, 실행 맥락(context) 부조화 등 문제를 발생시킬 수 있다. 이 글은 이 문제에 대해 다루어 본다.

리소스 재사용과 보안 문제
원칙적으로 웹 서버는 다른 B서버 도메인(다른 주소, 포트 및 프로토콜을 가진 서버)의 파일, 웹 페이지, DB 등을 개발 중인 A서버의 Javascript 와 같은 어플리케이션이 접근할 때는 웹브라우저에서 보안 문제를 발생한다. 즉, 다른 서버 도메인의 리소스를 접근할 때는 CROS(Cross-Origin Resource Sharing) 보안 문제가 발생한다(동일 서버는 이런 문제 발생하지 않는다). CROS는 웹브라우저에서 다른 도메인에서 리소스 사용할 때 보안 정책을 정의한 것이다. 이 경우, 다음 같이 처리한다.
  • 각 도메인에서 CROS 정책을 접근하는 서버 주소를 등록함으로써 보안문제 처리
  • 각 도메인 접근 주소를 동일 proxy 서버로 등록함으로써 웹브라우저를 속임
CROS에 대한 좀더 상셍한 내용은 다음 링크를 참고한다.
CROS 보안 문제

웹화면 재사용과 동기화 문제
다음 그림과 같이 개발을 줄이기 위해 iframe으로 비슷한 기능을 제공하는 웹사이트 화면을 재사용한다면(예. Profile, Post) 다음과 같은 동기화 문제가 발생한다. 
  1. 사용자 로그인 계정이나 맥락(context)과는 별개로 움직이는 웹화면
  2. 데이터 동기화 문제: 서로 다른 데이터 모델을 사용하므로 데이터 동기화 문제 발생
  3. 각 iframe으로 가져온 화면의 부조화. 서로 다른 사용자 인터페이스 스타일
단순한 웹화면 메쉬업 서비스라면 다음과 같이 iframe으로 짜집기 하면 되지만, 앞의 1, 2 문제가 있다면, 이렇게 짜집기된 웹 서비스는 각 화면별로 따로 동작하기 때문에 사용자 관점에서 큰 의미가 없게 된다.
단순 iframe 짜집기 웹페이지 예시

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Display buildings in 3D</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.css" rel="stylesheet" />
<style>
 body { margin: 0; padding: 0; }
 #map { position: absolute; top: 0; height: 30%; width: 100%; }
</style>
</head>
<body>
<div class=frame id="terrain">
  <iframe src="https://sandcastle.cesium.com/gallery/Terrain.html" style="position: absolute; display:block; top: 30%; width:100vw; height: 30vh"></iframe>
</div>
<div class=frame id="map"></div>
<div class=frame id="dashboard">
  <iframe src="https://merakidemo.internetoflego.com/#flow/fa89d9fb.342448" style="position: absolute; display:block; top: 60%; width:100vw; height: 40vh"></iframe>
</div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibWFjOTk5IiwiYSI6ImNrNzdyOXc5cjBhdWozbG9kajlwNDk3NGwifQ.2Z3kIeNZbwtVaKVl5NldaA';

var map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/light-v10',
center: [-74.0066, 40.7135], zoom: 15.5, pitch: 45,
bearing: -17.6, container: 'map', antialias: true
});
 
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on('load', function() {
  // Insert the layer beneath any symbol layer.
  var layers = map.getStyle().layers;
   
  var labelLayerId;
  for (var i = 0; i < layers.length; i++) {
    if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
      labelLayerId = layers[i].id;
      break;
    }
  }
   
  map.addLayer(
    {
      'id': '3d-buildings', 'source': 'composite',
      'source-layer': 'building', 'filter': ['==', 'extrude', 'true'],
      'type': 'fill-extrusion', 'minzoom': 15,
      'paint': {
        'fill-extrusion-color': '#a00',
         
        // use an 'interpolate' expression to add a smooth transition effect to the
        // buildings as the user zooms in
        'fill-extrusion-height': [
          'interpolate', ['linear'], ['zoom'], 15, 0, 15.05, ['get', 'height']
        ],
        'fill-extrusion-base': [
          'interpolate', ['linear'], ['zoom'], 15, 0, 15.05, ['get', 'min_height']
        ],
        'fill-extrusion-opacity': 0.6
      }
    },
    labelLayerId
  );
});
</script>
</body>
</html>
iframe 사용해 서로 다른 도메인 웹페이지 화면 통합 예시(hilite)

사용자 맥락을 고려해야 할 경우, RESTful API, Javascript API 등을 해당 서비스를 제공하는 서버에서 제공해, 사용하는 서버 측에서 코딩하여, 동일한 HTML DOM(Document Object Model)로 렌더링하도록 해야 한다. 

웹화면 재사용과 보안, N-Screen 문제
앞서 iframe 등으로 어떻게 화면 통합 문제를 해결했다고 하더라도, 부적절한 스크립트를 실행해 보안문제가 발생하거나, 다양한 스마트 장치(스마트폰, 스마트패드 등)에서 일관성없는 짜집기 화면이 표시될 수 있다. 이를 수정하는 것은 어렵고 추가적으로 복잡하고 많은 iframe, javascript 및 css 코드를 발생시킨다.

스마트폰에서 N-screen 문제
(앞의 동일 웹사이트를 'python -m http.server --bind <서버IP주소> 9000' 로 실행 후 iPhone 크롬에서 접속한 결과)

참고로, 메쉬업(mashup) 서비스인 Qlik는 iframe에서 다음과 같은 주소 형식으로 사용자 맥락을 유지하는 방법을 사용하고 있다.

웹화면 데이터 재활용과 Web Scraping 문제
Web Scraping(웹 스크래핑)은 특정 웹사이트에 표시된 데이터만 가져올 때 사용한다. crawler와 같은 nodejs 패키지나 파이썬의 몇몇 scraping 라이브러리는 이런 작업을 매우 손쉽게 할 수 있다.

다만, 이 경우에도 데이터를 스크래핑하는 웹서버의 HTML 렌더링 코드가 변경되거나 미쳐 예측하지 못한 보안 정책 등의 이유로 올바르게 동작되지 않을 수 있다.

웹 리소스 재사용 관련 읽어볼 만한 글
앞서 웹 리소스 재사용에 대한 몇몇 전략과 문제들을 확인해 보았다.

사실 일반적인 재활용 통합 전략은 외부 도메인 서버에서 제공하는 API, 공식적인 리소스 접근 및 서비스 호출 프로토콜, 서비스 실행 모듈만 개발 서버에 설치해 사용하는 방법 등일 것이다.

다른 도메인의 웹 리소스 재활용 시 부득이한 상황이라면 앞의 부작용을 고려해 웹 재사용 전략을 사용해야 한다. 다음 글은 이와 관련된 좀 더 자세한 내용이다.

레퍼런스

빅데이터 처리를 위한 윈도우 버전 hadoop, spark 설치 및 간단한 사용법

빅데이터 처리를 할 수 있으면 SNS, 사물인터넷, 공간정보 등에서 발생되는 수많은 데이터에서 인사이트 있는 정보를 쉽게 얻을 수 있다. 이 글은 윈도우 환경에서 빅데이터 분산 처리를 위한 hadoop, spark 설치 및 사용 방법을 간단히 다룬다.

보통 빅데이터 처리할 때 다음과 같은 아키텍처를 가지고 있는 데, hadoop과 spark는 각각 분산 저장 및 관리, hadoop 기반 분석 및 통계 처리 역할을 한다.

빅데이터 활용 초기에는 하둡이 많이 사용되었으나, 빈번한 디스크 입출력으로 인한 속도저하와 빅데이터 처리 프로그램 개발 및 배포의 불편함(java 기반) 등으로 인해, 스파크가 점차 인기를 끌고 있다. 스파크는 In-memory 처리 방식으로 분산 처리 성능을 크게 향상시켰다. RDD(resilient distributed datasets)을 이용해 메모리 내에서 분산 처리 연산 및 분산된 데이터 유실에 대한 문제를 해결해 가용성을 높였다. 이 방법은 Hadoop의 HDFS(Hadoop Distributed File System) 읽기쓰기 시 디스크 작업 속도보다 10~100배 빠르다.

다음은 일반적인 빅데이터처리 아키텍처를 보여준다. 카프카로 IoT, SNS 등 massive data를 스트리밍해, No SQL로 저장한다. 만약, 빅데이터 분산 저장 및 분석 등이 필요한 경우 스파크를 통해 처리한다. 이를 다양한 가시화 도구를 통해 결과를 보여준다.
빅데이터 처리 시스템 구조 개념도

다음 그림은 다양한 소스를 통해 얻어지는 빅데이터를 카프카나 하둡으로 얻어 스파크를 통해 No SQL DB에 분산 저장하거나 처리한 후 저장한다. 로그 형식으로 발생되는 데이터는 좀 더 단순한 플럼(Flume참고)으로 스트리밍 저장할 수 도 있다. 이를 스카파크 SQL 등을 통해 질의한다. 정재된 데이터는 유사도, 군집, 데이터 마이닝을 위해 머하웃(Mohout참고)을 사용할 수도 있다. 머하웃은 하둡 맵리듀스(map reduce) 기반 분산 계산이 가능하다. 이 과정은 BI(business intelligent)에서 사용하는 ETL(Extract, Transform and Load)와 매우 유사하다.
빅데이터 처피 파이프 라인 예시(Big Data, Streaming Data - ETL Analytics Pipeline)

이렇게 구축된 플랫폼위에 트위터에서 사용되는 스톰(Storm)과 같은 실시간 분석 분산 시스템, 하둡파일을 SQL 질의할 수 있는 Hive, 대용량 데이터셋을 좀 더 쉽게 다룰수 있는 스크립트를 지원하는 피그(pig), 솔라(Solr)와 같은 웹 기반 검색 엔진을 사용할 수 있다.
빅데이터 처리 시스템
(Reference: https://intellipaat.com/blog/tutorial/hadoop-tutorial/introduction-hadoop/)

윈도우 환경은 우분투에서 설치하는 것보다 방법이 좀 까다롭다. 이 글은 윈도우 환경에서 하둡, 스파크 설치 테크트리를 정리해 놓는다. 참고로, 이 글은 분산 메시지 스트리밍 플랫폼으로 많이 사용되는 카파(kafka)는 다루지 않는다. 카파 설치 및 사용법은 아래 링크를 참고바란다.
기타, 이 예제에 대한 상세 내용은 레퍼런스를 참고 바란다.

소프트웨어 다운로드 및 설치
다음 링크에서 Java, Hadoop, Spark 를 다운로드 한다. 
만약, 오라클 Java Licence 문제를 피하고 싶다면 OpenJDK를 대신 다운받아 설치한다. Java, Skala는 설치하고, 다른 프로그램은 압축을 푼 후 hadoop, spark 폴더를 만들어 복사해 넣는다(주의 - 폴더에 공백이나 한글이 포함되면 안됨).

환경 변수 설정
각 설치 프로그램에 대한 실행 경로나 환경 변수를 설정해야 한다. 다음과 같이 시스템 환경 변수를 설정한다.
  • JAVA_HOME: Java 설치 폴더
  • HADOOP_HOME: hadoop 설치 폴더
  • SPARK_HOME: spark 설치폴더
시스템 환경변수 설정 화면
설정된 HADOO_HOME 환경변수

그리고 hadoop, spark, scala 각 프로그램의 bin, sbin폴더를 시스템  Path에 추가해 놓는다. 본인의 경우 다음과 같이 설정하였다.

제대로 환경변수가 설정되었다면, cmd 창을 실행해 각 프로그램 버전을 다음과 같이 확인할 수 있다.
설치 프로그램 버전 확인

설치 후 다음과 같이 hadoop 폴더에 datanode와 namenode 폴더를 만든다.

HADOOP 설정
Hadoop 설치 폴더의 etc/hadoop 폴더에 설정파일을 수정해야 한다. 다음과 같이 수정한다.
다음과 같이 core-site.xml를 수정한다.
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>

다음과 같이 mapred-site.xml 을 수정한다.
<configuration>
  <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
</configuration>

다음과 같이 hdfs-site.xml 을 수정한다. 그리고, C:/hadoop 부분을 경로에 맞게 수정한다.
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>file:///C:/Hadoop/hadoop-<version>/namenode</value>
  </property>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>file:///C:/Hadoop/hadoop-<version>/datanode</value>
  </property>
</configuration>

다음과 같이 yarn-site.xml 을 수정한다.
<configuration>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
  <property>
    <name>yarn.nodemanager.auxservices.mapreduce.shuffle.class</name>  
    <value>org.apache.hadoop.mapred.ShuffleHandler</value>
  </property>
</configuration>

Winutils hadoop  윈도우버전 패치
winutils는 hadoop을 윈도우에서 동작하도록 해준다. winutils 링크에서 폴더를 통채로 다운로드 하고, 압축을 푼 후, hadoop-3.1.2/bin의 파일들을 설치된 hadoop의 bin에 폴더에 덮어 쓴다. 

이제 다음과 같이 hdfs namenode 을 포맷한다. 
hdfs namenode -format


그리고, hadoop 설치폴더의 /share/hadoop/yarn/timelineservice 폴더 내 hadoop-yarn-server-timelineservice-<version> 파일을 hadoop 설치폴더 /share/hadoop/yarn 폴더로 복사해 넣는다.

HDFS 서버 시작하기 
이제 다음과 같이 HDFS 서버를 시작한다.
start-dfs.cmd
start-yarn.cmd

그럼 다음과 같이 서버가 실행된다.
HDFS 서버 실행 모습

현재 HDFS 실행 상태를 확인하기 위해 다음과 같이 jps 명령을 입력한다.
jps 실행 후 모습

데쉬보드를 확인하기 위해 크롬에서 localhost:8088, localhost:9870 을 다음처럼 입력한다.
Hadoop 서버 데쉬보드

HADOOP과 Spark 사용 테스트
하둡과 스파크 간단한 테스트를 위해 다음과 같이 hadoop 파일 시스템 내 test 폴더를 만든다.
hdfs dfs -mkdir /test
hdfs dfs -ls /
실행 결과

다음과 같이 spark 설치 폴더의 README 파일을 하둡 파일 시스템에 put 하고 이를 스파크로 테스트해본다.
hdfs dfs -put README.md /test
hdfs dfs -ls -R /
set SPARK_LOCAL_HOSTNAME=localhost
spark-shell

그럼 다음과 같이 spark shell 이 실행된다. 참고로, 실행 전에 SPARK_LOCAL_HOSTNAME=localhost 를 꼭 설정해야 한다(참고 - SparkContext 초기화 에러). 아울러, 스파크는 스칼라 이외에 파이썬, 자바 등 다양한 언어를 제공한다. 파이썬의 경우 pyspark를 실행해 입력할 수 있다.

다음과 같이 코딩해 본다.
scala> List(1, 2, 3).zip(List("a", "b", "c"))
res2: List[(Int, String)] = List((1,a), (2,b), (3,c))

다음과 같이 spark 에서 hadoop 파일을 열고, 읽어본다.
var textFile = sc.textFile("hdfs://localhost:9000/test/README.md")
textFile.first()

다음과 같이 count, filter, map, reduce 함수를 사용해 코딩해 본다.
textFile.count()
textFile.filter(line => line.contains("Spark")).count()
textFile.map(line => line.split(" ").size).reduce((a, b) => if(a > b) a else b)

아래와 같이 결과가 출력되면 성공한 것이다.

스파크는 http://127.0.0.1:4040 에 웹UI를 제공한다.
스파크 웹 UI

스파크 어플리케이션을 스칼라, 파이선, 자바 등으로 개발해 jar 파일을 만들면, spark-submit을 이용해 실행할 수 있다. 예를 들어, NoSQL에 저장된 IoT센서 빅데이터를  스파크, Hadoop으로 병렬처리해 일별, 월별, 년별 분석하는 등의 프로그램을 미리 만들어 놓고 필요할 때 실행할 수 있다. 이에 대한 예제는 다음 링크를 참고한다.

빅데이터 분석 가시화와 NoSQL 데이터베이스
명령행에서 스칼라 코딩하기 어렵거나 분석된 데이터를 손쉽게 가시화하고 싶다면, 데이터 분석 시각화 도구인 제플린(zeppelin Binary package with all interpreters download) 노트북을 다운로드한다. 제플린에서는 UI에서 스파크 함수를 사용해 프로그램 실행할 수 있다.

다운로드 받은 제플린 압축을 풀고, 다음 같이 제플린 서버를 실행한다.
cd bin
zeppelin.cmd

앞서 언급한 환경이 제대로 설정되어 있다면, 다음과 같이 서버가 잘 실행될 것이다.
제플린 서버 실행 모습

브라우저에서 http://localhost:8080/#/ 에 접속한다.
제플린 서버 접속 모습

제플린에서 Create new note 를 클릭한다. Zeppelin Tutorial에서 다음 예제와 데이터를 얻어 실행해 본다. 참고로, 제플린과 스파크 간에 호환성 문제가 있어 에러 발생할 수 있다. 이 경우, 아파치 스파크 2.3.2와 제플린 0.8 버전을 사용한다.
val bankText = sc.textFile("D:/Program/zeppelin-0.8.2-bin-all/sample/bank-full.csv")

case class Bank(age:Integer, job:String, marital : String, education : String, balance : Integer)

// split each line, filter out header (starts with "age"), and map it into Bank case class
val bank = bankText.map(s=>s.split(";")).filter(s=>s(0)!="\"age\"").map(
    s=>Bank(s(0).toInt, 
            s(1).replaceAll("\"", ""),
            s(2).replaceAll("\"", ""),
            s(3).replaceAll("\"", ""),
            s(5).replaceAll("\"", "").toInt
        )
)

// convert to DataFrame and create temporal table
bank.toDF().registerTempTable("bank")


빅데이터 분석된 정보는 NoSQL 데이터베이스인 몽고 DB(MongoDB), 카산드라(Cassandra), HBase 등에 저장할 수 있다. NoSQL은 각 DB마다 장단점이 있고, 응용 목적에 따라 성능이 다르니 이에 맞게 사용해야 한다(참고 - NoSQL DB 비교 #1, #2).

스칼라를 이용해 빅데이터를 시간, 날짜, 월, 년도별로 분석해 MongoDB로 저장하고 싶거나, 센서 데이터를 필터링한 후 필요한 데이터만 저장하고 싶다면, 다음 예시를 참고한다.

마무리
빅데이터 처리를 위해 하둡은 데이터를 분산 저장, 처리 및 병합하는 Map reduce 방식을 사용한다. 스파크는 이를 이용해 대용량 데이터 분석 및 통계 처리를 할 수 있다. 환경 설정이 쉽지는 않지만, 앞의 내용을 잘 따라 설치하면 큰 문제 없이 빅데이터 처리를 지원하는 하둡 개발 환경을 만들 수 있을 것이다.

레퍼런스
스파크 실행 및 관련 환경 예시는 다음을 참고한다.
스칼라 설명 및 스파크 어플리케이션 개발은 다음을 참고한다.
제플린 설치 방법은 아래 링크를 참고한다.
IntelliJ을 설치한 후 바로 Scala 플러그인을 설치해 IDE환경에서 편하게 코딩할 수 있다.
IntelliJ 에서 Scala 플러그인 설치 모습

Cesium 기반 건물 시설물 모니터링 웹 서비스

이 글은 Cesium 기반 건물 시설물 웹 서비스 개발을 위해 필요한 내용을 정리한다. 가능한 검증된 오픈소스를 사용해 확장성과 유지관리성이 좋은 Cesium 기술 스택을 사용한다.


아래 링크를 입력하면 관련 코드를 확인할 수 있다.
이 소스는 Ceisum의 Cesium3DTileset 객체를 생성한 후 primitives 형상으로 뷰어의 scene그래프에 추가하여 3D BIM 형상 모델을 생성한다.
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;

var tileset = scene.primitives.add(
    new Cesium.Cesium3DTileset({
        url: Cesium.IonResource.fromAssetId(8564)
    })
);

tileset.readyPromise.then(function(tileset) {
    viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(0.5, -0.2, tileset.boundingSphere.radius * 4.0));
}).otherwise(function(error) {
    console.log(error);
});

뷰어 캔버스에 마우스 이벤트를 설정해 픽을 할 경우, 선택된 모델 형상이 무엇인지 확인 가능하다. 이를 이용해 형상의 속성정보 등을 확인할 수 있다.
var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function(movement) {
    if (!picking) {
        return;
    }

    var feature = scene.pick(movement.endPosition);

    unselectFeature(selectedFeature);

    if (feature instanceof Cesium.Cesium3DTileFeature) {
        selectFeature(feature);
    }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

참고로, LoD(level of detail) 옵션을 사용해 형상의 LOD 표현을 다음과 같이 제어할 수 있다.
var tileset = scene.primitives.add(new Cesium.Cesium3DTileset({
     url : 'http://localhost:8002/tilesets/Seattle/tileset.json',
     skipLevelOfDetail : true,
     baseScreenSpaceError : 1024,
     skipScreenSpaceErrorFactor : 16,
     skipLevels : 1,
     immediatelyLoadDesiredLevelOfDetail : false,
     loadSiblings : false,
     cullWithChildrenBounds : true
}));

이 예제는 오픈소스이므로 다운로드한 후 HTTP 서버를 구동해 쉽게 실행할 수 있다.

레퍼런스

2020년 2월 19일 수요일

GeoServer, PostgreSQL 기반 CesiumJS 지도 웹 서버 개발 방법

이 글은 GeoServer 기반 CesiumJS 지도 웹 서버 어플리케이션 개발 방법을 간략히 설명한다. Cesium을 이용하면 WebGL 기반 공간정보 서비스가 가능하며, 3차원 그래픽 모델 등을 표현하기 쉽다. Geoserver를 이용해 사용자가 직접 만든 지도 데이터를 레이어로 렌더링하여, Cesium 플랫폼 위에 보여줄 수 있다.

Cesium 개념 및 사용 방법을 전혀 모른다면 다음 링크를 미리 참고한다.
머리말
이 예제는 Google map, OSM(OpenStreetMap) 등에서 제공하지 않는 형태의 지도 데이터를 사용해 인터렉티브한 맵 어플리케이션을 개발하고 싶을 때 유용하다. 이 예제는 오픈소스로 유명한 GeoServer, PosgreSQL, PostGIS를 사용한다. 이 예제에 대한 좀 더 상세한 내용은 이 글의 마지막 레퍼런스들을 참고하길 바란다. 참고로, 이 예제는 윈도우즈10 환경에서 개발하지만, 환경 설치나 예제 실행은 우분투, 리눅스에서도 똑 같이 동작한다. 다음은 예제에서 사용하는 기술 스택이다.
각 컴포넌트 역할은 다음과 같다.
  • CesiumJS: Cesium 기반 웹 서비스 개발을 지원하는 Javascript 모듈
  • Cesium: WebGL 기반 동적 그래픽 지도 렌더링 및 이벤트 처리를 지원하는 플랫폼
  • Geoserver: 다양한 형식의 지도 레이어 렌더링을 지원하는 서버
  • PostGIS: PostgreSQL 데이터베이스의 정보 검색 SQL 질의 애드인(addin). 
  • PostgreSQL: 객체 관계형 데이터베이스. 다양한 형식의 지도 데이터를 체계적으로 저장, 관리하기 위해 사용
Geoserver, PostgreSQL, PostGIS 설치
자제 지도 데이터를 사용하기 위해서 다양한 형식의 지도 데이터를 렌더링할 수 있는 Geoserver를 사용한다. Geoserver를 설치하기 위해서는 Java SDK가 필요하다. 공간정보 데이터베이스가 필요하다면 PostgreSQL DBMS와 공간정보 질의 기능을 지원하는 PostGIS 애드인을 미리 설치해야 한다. 다음 링크에서 각 설치 프로그램을 다운로드할 수 있다.
우선 Java SDK를 다운로드받아 설치한다. 이후, GeoServer를 다음과 같이 설치한다.
Geoserver 설치
Geoserver start 화면 

다음과 같이 PostgreSQL 설치 후 바로 실행되는 Stack Builder를 통해 PostGIS 애드인을 설치한다. 그 후, 설치된 pgAmin4를 실행해 PostgreSQL 데이터베이스 서버를 시작해 본다.
PostgreSQL과 PostGIS 설치
Stack Builder를 통한 PostGIS 설치 완료 

설치된 프로그램 중 PostgreSQL DBMS 관리를 지원하는 pgAdmin4를 실행한다. 다음과 같은 화면이 보이면 성공한 것이다.
pgAdmin4를 통한 PostgreSQL 서버 실행 모습

CesiumJS Starter Kit 준비 및 실행
Javascript로 동작되는 cesiumjs는 9000 포트, GeoServer는 8080 포트를 사용한다. cesiumjs 어플리케이션 개발을 지원하기 위해, cesium에서는 예제 템플릿과 설명을 다음과 같이 제공한다.
다음과 같이 Starter Kit 소스코드를 다운로드한다.
git clone https://github.com/Cingulara/cesium-starterkit.git

git clone으로 코드를 다운로드 받으면 폴더 안에 index.html 이 있고, 그 안에 css, javascript import 등이 코딩되어 있다. 폴더 내 cesium-scripts.js는 cesiumjs API, 웹 맵 서버와 상호동작 등의 기능을 포함하고 있다. js 파일 18행에 cesium 사용을 위해 다음과 같이 cesium API key 값을 입력한다.
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MDA1OGJhMi04NTY0LTQ0NDgtOWZkNi02ZWM1YzE1Y2VkODUiLCJpZCI6MjI3NzksInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODIwMzI3MTJ9.le7e8JdjCMK94zUohux5Wu-CWF0ydrrh7vSvxU3ZR4U';

이 예제를 웹 서버로 실행하려면 HTTP 서버를 먼저 설치해야 한다.  웹 서버 중에 아파치(설치), NGINX(설치 및 사용법)가 유명하나 이 경우에는 간단하게 python에 내장된 SimpleHTTPServer(python 2.x 버전), http.server(python 3.x 버전)를 이용한다.

우선 Geoserver를 실행한다. 그 후, 다음처럼 HTTP 서버를 실행한다(python 3.x 버전의 경우. 상세 내용은 Local server, Mozilla 참고).
python -m http.server 9000
Geosever와 HTTP server 실행 모습

HTTP 서버 실행 후 크롬에서 localhost:9000 접속하면 다음 같은 결과를 볼 수 있다.
실행 결과

다른 서버 도메인의 자바 스크립트 어플리케이션에서 GeoServer 를 사용하려면 GeoServer의 CORS(Cross-Origin Resource Sharing)을 활성화해야 한다(도메인이 서로 다르면 Javascript에서 Geoserver 데이터를 다운로드할 수 없다). 우선 GeoServer 가 설치된 경로에서 web.xml 파일을 찾아 CORS filter 및 filter-mapping xml 태그 주석을 아래와 같이 해제한다. 그리고, Geoserver를 재시작한다.
geoserver web.xml 수정

CORS에 대한 좀 더 상세한 내용은 다음 링크를 참고한다.
Geoserver와 CesiumJS 연동 코드 분석
Geoserver와 같은 지도 이미지 제공 서버를 위해 세슘은 Cesium.WebMapServiceImageryProvider 함수를 제공한다. cesium-scripts.js의 44행에는 상호 연동을 위한 코드가 다음과 같이 구현되어 있다. 이 함수는 Geoserver webservice API url 및 관련 parameters를 입력받는다. 코드를 분석해 보면, 2개의 레이어를 추가하고 있는 데, 플로리다 이미지(florida:NE1_50M_SR_W) 및 도로망 맵(florida:FL_planet_osm_roads)이다. 이 맵 데이터 모두 geoserver에서 webservice 방식인 wms(web mapping server)로 제공한다.
    var worldMap = new Cesium.WebMapServiceImageryProvider({
        url : 'http://localhost:8080/geoserver/wms',
        parameters: {
            format:'image/png',
            transparent:'true',
            tiled: true,
            enablePickFeatures: true
        },
        layers : 'florida:NE1_50M_SR_W',  // comma separated listing
        maximumLevel : 12
    }); 
...
    var detailedMaps = new Cesium.WebMapServiceImageryProvider({
        url : 'http://localhost:8080/geoserver/wms',
        parameters: {
            format:'image/png',
            transparent:'true',
            tiled: true,
            enablePickFeatures: true
        },
        layers : 'florida:FL_planet_osm_roads',  // comma separated listing
        maximumLevel : 20
    }); 

florida:NE1_50M_SR_W, florida:FL_planet_osm_roads 모두 geoserver에 등록되어 있지 않으므로, 해당 지도 데이터를 준비해 geoserver 레이어로 추가해야 한다.

참고로, js 소스 안에 레이어들을 켜고 끌 수 있는 changeLayer함수가 정의되어 있다. 원하면, 레이어를 이런 방식으로 조작할 수 있다.
function changeLayers(value) {
    if (value == "satellite"){
        viewer.imageryLayers._layers[0].show = true;
        viewer.imageryLayers._layers[1].show = false;
        viewer.imageryLayers._layers[2].show = false;
    }
    else if (value == "roads"){
        viewer.imageryLayers._layers[0].show = true;
        viewer.imageryLayers._layers[1].show = false;
        viewer.imageryLayers._layers[2].show = true;
    }
    else if (value == "hybrid"){
        viewer.imageryLayers._layers[0].show = false;
        viewer.imageryLayers._layers[1].show = true;
        viewer.imageryLayers._layers[2].show = true;
    }
}

GeoServer 지도 데이터 레이어 추가
앞서 설명했듯이 본 예제의 js 소스코드에 두개 지도 데이터 형식을 Geoserver에 레이어로 추가해야 한다. 오픈소스 QGIS, Arcgis, CAD를 이용해 지도 데이터를 만들 수 있겠지만, 지금은 맵 서비스 개발 방법 이해가 중요하므로, 기존에 만들어진 지도 데이터를 다운로드해 사용할 것이다. 이 예제에서 지도 데이터를 가공해 맵 서비스를 개발하는 과정은 다음과 같다.

우선, florida:NE1_50M_SR_W 레이어 용 GeoTiff 형식 데이터를 준비한다. 이를 위해, naturalearthdata.com 을 방문하여 Shaded Relief and Water 데이터를 다음과 같이 다운로드 한다.
다운로드 압축파일을 풀고, geoserver의 data 폴더에 worldmap 폴더를 아래와 같이 만들어 넣는다.

Geoserver를 시작하고, 다음과 같이 새로운 작업공간을 만든다.

Geoserver에서 새로운 데이터 저장소를 선택하여, 다음과 같이 GeoTiff 메뉴를 선택해 앞에서 다운로드한 NE1_50M_SR_W.tif 파일을 업로드한다.



GeoTiff 파일이 추가되면, 다음과 같이 레이어 미리보기에서 해당 지도 데이터를 확인할 수 있다.


이제 florida:FL_planet_osm_roads 지도 데이터 레이어를 만들 차례다. psql CLI(command line interface)를 실행해 다음과 같은 순서로 PostgreSQL에 데이터베이스를 만들고, postgis를 사용하기 위해 확장 구조를 DB에 등록한다.
create database floridamaps;
\c floridamaps;
create extension postgis;
create extension postgis_topology;
create extension hstore;

psql 명령 입력 모습
PostgreSQL DB 생성 결과

geofabrik.de 사이트에서 OSM 지도 데이터를 다운로드 받는다. 압축울 풀고, osm2pgsql 프로그램(설치 방법)을 이용해 다음과 같이 PostgreSQL DB(database)로 변환한다.
osm2pgsql -C 2048 -s -H localhost -P 5432 -d floridamaps ./florida-latest.osm -C 2048 --slim --number-processes 2 --password --username postgres

osm2pgsql 의 --username 은 PostgreSQL DB username 과 동일하다. PostgreSQL 설치 시 디폴트는 postgres 이다. --password 옵션을 추가하면 사용자 암호 입력을 받는다. 설치 시 postgres 유저에 대한 암호를 입력하면, 다음과 같이 변환 과정이 실행된다.
osm2pgsql 변환 과정

변환 작업이 끝나면, 다음과 같이 pgAdmin의 SQL창에서 변환된 지도 데이터를 확인해 본다. 쿼리 결과가 올바르면 OSM 지도 데이터가 정상 변환 된 것이다.
SELECT COUNT(*) FROM public.planet_osm_point
UNION
SELECT COUNT(*) FROM public.planet_osm_line
UNION
SELECT COUNT(*) FROM public.planet_osm_polygon

쿼리 결과

이제 다음과 같이 새로운 저장소로 PostGIS을 추가한다. 앞서 생성한 PostgreSQL database, user, password를 입력한다.

다음과 같이 Geoserver에 생성된 저장소의 planet_osm_roads를  florida:FL_planet_osm_roads 레이어로 추가한다. 이 때, 레이어 최소 경계 영역은 '데이터로부터 계산하기', '원본 영역으로부터 계산하기'버튼을 클릭해 자동 입력해 넣는다.
레이어 추가 화면

레이어가 정상 추가되었다면 다음과 같이 레이어 미리보기를 할 수 있다. OpenLayers 버튼을 클릭해 지도가 제대로 표시되는 지 확인하자.
레이어 미리보기
미리보기 결과 

GeoServer에 다음과 같이 레이어 맵이 추가되어 있다면, Cesium에서 이 레이어를 렌더링해 오버레이 할 수 있다. 레이어 명과 앞서 설명한 cesium-scripts.js 소스코드 안에 지정된 레이어 명은 서로 같아야 함에 주의한다.

다시 HTTP server를 실행해 Geoserver에서 제공되는 레이어가 표시된 CesiumJS 지도 앱 어플리케이션 실행 결과를 확인해 본다. 
Cesium Roads 레이어 켜졌을 때 지도 앱 모습 
 
지도 앱 어플리케이션 실행 결과
Geoserver에서 다운로드되는 지도 데이터 로그와 PostgreSQL Transactions 처리 모습

CesiumJS 기반 지도 앱 서버가 잘 실행되는 것을 확인할 수 있다. 만약, 제대로 동작하지 않는 다면 크롬에서 개발자 도구로 디버깅 로그를 확인해 보길 바란다. 각 단계를 따라하였다면 잘 실행될 것이다.

마무리
최근 스마트시티, 디지털 트윈, 스마트 건설과 같은 서비스 요구가 많아지고 있다. 이런 서비스에 공간정보는 중요한 서비스 기반이다. 세슘 플랫폼을 이용하면 다양한 데이터 소스와 연결하여 공간정보 기반으로 시각화하고 의사결정 데이터 서비스를 쉽게 개발할 수 있다.

본 글에서 설명한 Cesium Start kit 예제를 목적에 맞게 확장하기는 어렵지 않다. Geoserver를 사용하면 사용자가 Cesium에서 기본 제공하지 않은 지도 형식을 쉽게 확장할 수 있다. cesuim은 webgl을 기반으로 하는 다양한 데이터 소스 연결, 2D/3D 그래픽 생성, 이벤트 처리, 서버 연결 방법 등을 제공하고 있기 때문에 인터렉티브한 맵 어플리케이션 서버 개발이 그리 어렵지 않을 것이다.

레퍼런스