개발성 향상을 위한 JavaScript의 주석

JavaScript는 type이 불명확하므로 문서화를 통해 그 상세정보를 남겨야 합니다. 그러나 소스코드와 분리된 문서는 사실상 그 활용가치가 매우 떨어집니다. 소스코드와 일원화된 방식으로 문서화가 되어야 하는데, 바로 주석입니다. 아래는 어떤 클래스의 특정함수에 대한 주석을 통한 문서화에 대한 간단한 예시입니다.

/**
 * 메뉴 항목을 추가한다.
 * @param {string} id 항목에 대한 고유 식별자 
 * @param {string} url 항목 아이콘 
 * @param {string} title 항목 타이틀
 * @param {function} callback 실행에 대한 호출 함수
 * @returns {boolean} 성공 여부
 */
addMenu(id, url, title, callback) {
   ...

IDE는 이 주석을 실시간으로 해석해 개발자에게 매우 직관적인 힌트를 제공합니다. 예를들어서 VSCode에서는 아래와 같은 힌트를 제공합니다.

아울러 이러한 주석은 다양한 문서 형식으로써, 예를들어 pdf, docx, html 등과 같은 별도의 문서를 자동으로 생성해 낼 수 있습니다.

[OpenLayers] 특정 지점에 HotSpot 애니메이션 효과 주기

지도에 특정한 지점을 효과적으로 표현하기 애니메이션 효과를 사용하는 내용을 정리합니다. 먼저 그 결과는 아래와 같습니다.

화면은 지도에 대한 div 요소 하나입니다. 필요한 스타일과 함께 언급하면 아래와 같습니다.



이제 실제 코드인데요. 먼저 필요한 css와 모듈을 추가합니다.

import 'ol/ol.css';

import Feature from 'ol/Feature.js';
import Map from 'ol/Map.js';
import {unByKey} from 'ol/Observable.js';
import View from 'ol/View.js';
import {easeOut} from 'ol/easing.js';
import Point from 'ol/geom/Point.js';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer.js';
import {fromLonLat} from 'ol/proj.js';
import {OSM, Vector as VectorSource} from 'ol/source.js';
import {Circle as CircleStyle, Style, Fill} from 'ol/style.js';

이제 레이어와 지도를 구성합니다.

var map = new Map({
    layers: [
        new TileLayer({
            source: new OSM({
                wrapX: false
            })
        })
    ],
    target: 'map',
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

var source = new VectorSource({
    wrapX: false
});

var vector = new VectorLayer({
    source: source
});

map.addLayer(vector);

HotSpot 지점은 위의 20번 코드의 vector 객체의 데이터소스인 source 객체에 Feature로 추가됩니다. 위치는 난수로 지정되는데 아래의 함수 호출을 통해 추가됩니다.

function addRandomFeature() {
    var x = Math.random() * 360 - 180;
    var y = Math.random() * 180 - 90;
    var coord = fromLonLat([x, y]);
    var geom = new Point(coord);
    var feature = new Feature(geom);

    source.addFeature(feature);
}

위치를 난수로 정하고 Geometry를 구성한 뒤 Feature를 추가하는 형태입니다. Feature가 추가될 때 이벤트가 발생하게 할 수 있는데 아래와 같습니다.

source.on('addfeature', function(e) {
    flash(e.feature);
});

이벤트의 호출 함수에 전달되는 인자를 통해 어떤 Feature가 추가되었는지를 알 수 있고, 이 Feature을 인자로 flash 함수를 호출합니다. flash 함수는 다음과 같습니다.

var duration = 2000;
function flash(feature) {
    var start = new Date().getTime();
    var listenerKey = map.on('postcompose', animate);

    function animate(event) {
        var vectorContext = event.vectorContext;
        var frameState = event.frameState;
        var flashGeom = feature.getGeometry().clone();
        var elapsed = frameState.time - start;
        var elapsedRatio = elapsed / duration;

        var radius = easeOut(elapsedRatio) * 30;
        var opacity = easeOut(1 - elapsedRatio);

        var style = new Style({
            image: new CircleStyle({
                radius: radius,
                fill: new Fill({
                    color: 'rgba(255, 0, 0, ' + 0.5 * opacity + ')',        
                })
            })
        });

        vectorContext.setStyle(style);
        vectorContext.drawGeometry(flashGeom);

        if (elapsed > duration) {
            unByKey(listenerKey);
        } else {
            map.render();
        }
    }
}

짧다고 하기엔 조금 긴데요. 내부적으로 복잡한 처리가 실행됩니다. 이 flash 함수가 처음 호출되면 결국 31번의 map.render()가 호출됩니다. 이 map.render()가 호출되면 지도가 (데이터 요청없이) 다시 그려지고4번 코드에서 등록된 postcompose 이벤트 함수인 animate가 호출됩니다. animate 함수는 함수 내의 함수인데 6번 라인에 정의되어 있습니다. 해당 Feature의 위치를 기준으로 시간을 기반 투명도와 반지름의 크기를 변경하면서 빨간색 채움색으로 원을 그려주는 함수입니다. 이 flash 함수는 계속 자신의 호출하는 재귀함수로써 1번의 duration으로 지정된 시간이 지나면 28번의 조건이 True가 되어 앞서 등록한 postcompose 이벤트 등록을 제거합니다.

이제 마지막으로 1초마다 무작위로 Feature를 추가하도록 하는 코드는 아래와 같습니다.

window.setInterval(addRandomFeature, 1000);