웹에서 JavaScript만으로 데이터 압축하여 파일로 저장하기

몇 일전에 웹에서 자바스크립트만으로 압축 파일의 압축을 해제하는 내용을 정리했습니다. 해당 글은 아래와 같습니다.

Javascript 기반의 압축 라이브러리, jszip

이제는 다시 웹에서 사용자가 만든 어떤 데이터를 하나의 압축 파일로 만들 필요가 있어, 앞서 살펴본 압축 라이브러리를 이용해 바이너리 데이터와 텍스트 데이터를 각각 file.bin과 file.txt라는 파일명으로 하여 하나의 a.zip 파일로 압축한 후 사용자의 PC에 다운로드 하는 코드를 정리합니다.

먼저 압축하고자 하는 바이너리 데이터를 아래의 코드처럼 준비합니다.

let buffer = new ArrayBuffer(8);
let dataview = new DataView(buffer);

dataview.setInt32(0, 9438);
dataview.setFloat32(4, 3224.3224);

위의 데이터는 file.bin이라는 파일명으로 압축파일에 존재하도록 아래처럼 코드를 추가합니다.

let zip = new JSZip();
zip.file("file.bin", buffer);

앞서 언급했듯, 바이너리 뿐만 아니라 텍스트 파일도 압축 파일에 추가해 봅니다. 아래처럼요.

zip.file("file.txt", 'Hello한글Hi!');

이제 이렇게 압축된 내용을 a.zip 파일로 저장하는 코드는 다음과 같습니다.

let zipFileName = 'a.zip';
zip.generateAsync({ type: "blob" }).then(
    function (blob) {
        if (isIE()) {
            saveToFile_IE(zipFileName, blob);
        } else {
            saveToFile_Chrome(zipFileName, blob);
        }
    }
);

못보던 isIE와 saveToFile_IE, saveToFile_Chrome 함수가 보입니다. 이 놈들은 아래의 글을 참고하시면 파악할 수 있답니다.

웹에서 Javascript 만으로 텍스트 파일 생성

JavaScript로 Excel 파일 읽기

서버측의 기술없이 클라이언트 기술만으로 로컬에 저장된 엑셀 파일을 다룰 수 있는 라이브러리인 sheetjs에 대한 다양한 기능 중 엑셀 파일을 읽는 기능에 대한 JavaScript 코드를 정리해 봅니다.

이 라이브러리를 별도로 다운로드 받지 않고도 CDN을 통해 이용할 수 있습니다. 아래는 제가 이용한 CDN 접근입니다.


읽고자 하는 엑셀 파일은 다음과 같습니다.

위의 엑셀 파일을 선택하고, 선택된 엑셀 파일을 JSON 형식으로 변환해 콘솔에 출력하는 DOM 요소는 다음과 같습니다.


위의 input DOM 요소를 통해 파일을 선택할 때 발생하는 이벤트 코드는 다음과 같습니다.

function readExcel() {
    let input = event.target;
    let reader = new FileReader();

    reader.onload = function () {
        let data = reader.result;
        let workBook = XLSX.read(data, { type: 'binary' });

        workBook.SheetNames.forEach(function (sheetName) {
            console.log('SheetName: ' + sheetName);

            let rows = XLSX.utils.sheet_to_json(workBook.Sheets[sheetName]);
            console.log(JSON.stringify(rows));
        })
    };

    reader.readAsBinaryString(input.files[0]);
}

실행하고, 엑셀 파일을 선택해 보면 다음과 같은 내용이 콘솔에 표시되는 것을 확인할 수 있습니다.

SheetName: #인구수
[
   {
      "지역명":"무안군 무안읍",
      "남자":8599,
      "여자":7900,
      "가구수":6968,
      "노인수":732,
      "아저씨":1359,
      "어린이":4627
   },
   {
      "지역명":"무안군 일로읍",
      "남자":3659,
      "여자":3243,
      "가구수":3526,
      "노인수":8711,
      "아저씨":8758,
      "어린이":4099
   },

   ....

   {
      "지역명":"무안군 운남면",
      "남자":7357,
      "여자":7667,
      "가구수":3624,
      "노인수":8110,
      "아저씨":8042,
      "어린이":9618
   }
]

sheetjs 라이브러리는 엑셀을 읽는 기능 뿐만 아니라 생성도 가능하며 정교한 처리가 가능합니다.

NexGen, 공간 데이터의 분포경향 분석을 위한 밀도맵 기능

공간 분석 중, 밀도맵은 위치 데이터가 공간 상에 분포하는 경향을 매우 효과적으로 시각화할 수 있는 의사결정 지원 도구입니다. 위치 데이터의 공간 상의 위치 분포 뿐만 아니라 각 위치 데이터가 갖는 가중치값을 밀도 분석에 반영할 수 있어 위치 데이터와 통계 데이터를 조합할 수 있습니다.

밀도맵 기능의 소개를 위해 서울시의 인구 통계 데이터를 담고 있는 SHP 파일을 NexGen에 바로 불러와 밀도맵을 작성했습니다. 아래의 표는 사용한 인구 SHP 파일의 속성값에 대한 구조입니다. 통계청의 집계구 데이터를 받아 SHP 파일로 가공하였습니다.

이 데이터는 아래의 URL을 통해 다운로드 받으실 수 있습니다. 통계청에서 다운로드 받은 폴리곤 타입의 SHP 파일인 집계구 데이터와 TXT 파일인 2017년도 인구 데이터를 이용해 포인트 타입의 단일 SHP 파일로 직접 가공한 것입니다. 좌표계는 EPSG:5181입니다.



아래의 시연 동영상은 위의 인구 데이터를 활용한 NexGen의 밀도맵 기능에 대한 소개입니다.

웹 기반의 GIS 솔루션인 NexGen의 밀도맵 기능은 히트맵(HeatMap) 방식이 아닌 정규분포 방식의 알고리즘을 활용하여 고품질의 밀도맵을 작성할 수 있습니다.

대한민국 EPSG 코드

EPSG 코드는 전세계 좌표계 정의에 대한 고유한 명칭입니다. EPSG 코드에 대한 상세 정의는 prj4와 wkt라는 문자열로 되어 있으며, proj4와 EPSG의 wkt는 좌표계의 다양한 제원값을 정해진 문자열로 구성되어 있습니다. EPSG.io 라는 사이트를 통해 각 EPSG 코드에 대한 proj4와 wkt 문자열을 파악할 수 있으며, 이에 대한 글은 아래의 글을 참고하시기 바랍니다.

EPSG.io를 통한 proj4 문자열 얻기

아울러 아래는 대한민국에서 자주 사용하는 EPSG 코드에 대한 proj4 문자열을 정리한 것입니다. 향후에도 지속적으로 내용을 추가/보완하도록 할 것입니다.

EPSG:4166, ESPG:4326

+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs 

WGS84 타원체의 경위도 좌표계입니다. 흔히 GPS 등의 기본 좌표계입니다.

EPSG:2097

+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=500000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43

Bessel 1841 타원체의 한국 중부원점 TM 직각 좌표계입니다.

EPSG:5174

+proj=tmerc +lat_0=38 +lon_0=127.0028902777778 +k=1 +x_0=200000 +y_0=500000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43

일본의 기준점으로 끌어다 사용했다는 (개인적으로 사실이라고 해도 믿고 싶지 않지만?) 좌표계로, 일본에 지진이 발생하여 해당 기준점이 틀어진 만큼 보정된 좌표계입니다. 타원체는 Bessel 1841이며 TM 직각좌표계입니다. 폐기해야할 좌표계입니다. 개인적으로는 Bessel 타원체를 사용하는 모든 좌표계는 폐기하고 범세계적으로 GRS80 타원체로 통일해 사용해야 한다고 생각합니다.

ESPG:5178

+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43

Bessel 1841 타원체의 UTM-K 직각 좌표계입니다.

EPSG:5179 – 네이버 지도에서 사용함

+proj=tmerc +lat_0=38 +lon_0=127.5 +k=0.9996 +x_0=1000000 +y_0=2000000 +ellps=GRS80 +units=m +no_defs

GRS80 타원체의 UTM-K 직각 좌표계입니다. 네이버(v3)와 도로명주소 DB에서 사용하는 좌표계입니다.

ESPG:5181 – 카카오맵에서 사용함

+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=500000 +ellps=GRS80 +units=m +no_defs

GRS80 타원체의 한국 중부원점이며 Y 축으로 500000미터만큼 이동시킨 좌표계입니다. 카카오 맵에서 사용하는 좌표계입니다.

EPSG:5186

+proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +units=m +no_defs

GRS80 타원체의 한국 중부원점이며 Y 축으로 600000미터만큼 이동시킨 좌표계입니다. 국가에서 제공하는 DXF나 NGI 형식의 공간데이터가 이 좌표계를 이용합니다. 중부원점이외에도 서부, 동부, 동해원점이 있으며 각각 EPSG:5185, EPSG:5187, EPSG:5188입니다.

EPSG:3857, EPSG:900913, EPSG:102113 – 네이버(v5), 구글맵, VWorld지도에서 사용함

+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs

구글맵, VWorld 지도에서 사용하는 좌표계입니다. TM 방식의 좌표계가 아니므로 거리 측정에 사용할 수 없는 좌표계입니다.

아울러 아래의 글은 자바스크립트 기반의 좌표계 라이브러리에 대한 설명입니다.

Proj4js

끝으로 앞서 언급한 몇가지 EPSG 코드에 대한 .prj 파일을 공유합니다.


Javascript 기반의 압축 라이브러리, jszip

웹에서는 로컬에 저장된 여러 개의 파일을 사용하기 위해서는 사용자가 해당 파일들을 모두 마우스를 통한 직접적인 선택 행위가 적용되어야 보안상에 문제가 발생하지 않습니다. 이에 대한 대안은 여러개의 파일을 하나의 파일로 묶아 압축하고 압축된 파일 하나에만 이러한 사용자의 선택 행위가 적용되도록 하는 것입니다. 물론 네트워크를 통한 파일의 접근에는 이러한 문제가 발생하지 않습니다.

여튼, 여러 개의 파일이 하나로 묶인, 하나의 압축 파일로써 zip을 처리할 수 있는 자바스크립트 기반의 라이브러리가 몇가지 되는데.. 그중 제가 사용한 라이브러리를 소개합니다. 바로 jszip인데요. 아래의 해맑고 환하게 웃는 인상좋은 개발자의 github에 방문해 다운로드 받을 수 있습니다.

jszip 라이브러리 이외에도 이를 좀더 쉽게 사용하기 위해서 jszip-utils 라이브러리도 함께 받았는데, 위의 그림에서 이 2개의 라이브러리에 대한 다운로드는 파랑색 외곽선 박스로 언급해 두었습니다.

사용코드는 아래와 같습니다. 만약 자바스크립트의 promise라는 개념을 이해하고 있다면 매우 심플하고 직관적이라는 것을 알 수 있습니다. IO 처리이므로 당연이 비동기방식으로 처리됩니다. 과거 버전에서는 동기 방식도 지원했던거 같은데, 제거된 것 같습니다.





위의 코드에서는 압축 파일 안에 저장된 하나 하나의 파일에 순차적으로 접근해서 압축을 풀고, 풀어진 데이터를 ArrayBuffer 타입으로 전달받도록 21번 째 코드가 file.async(“arraybuffer”)라고 지정되어 있으나, arraybuffer 대신 string, blob로도 지정하여 각각 텍스트, BLOB 타입으로도 전달받을 수 있습니다.

현대의 웹은 무한한 접근성이라는 장점은 있으나 여전이 데스크탑 환경에 비해 처리 속도가 느리지만, 이런 문제점 조차도 웹어셈블리 등의 다양한 기술을 통해 극복해 나가고 있습니다. 향후 몇년안에 모든 프로그램은 서비스화될 것이라고 생각되고, 프로그램의 서비스화에는 JavaScript라는 핵심적인 언어가 중추적인 역할을 할 것입니다.