웹서버에 대한 Cross Domain 허용하기

개발 시에 다양한 웹서버에 대한 자원에 접근하기 위해 일시적인 방안으로 Cross Domain 접근을 허용할 필요가 있습니다. 이에 대해 각 웹서버에서의 방법이 상이한데요. 이에 대해 정리해 봅니다.

IE Express

IE Express가 실치된 Program Files(x86)/IIS Express/AppServer 폴더 안에 존재하는 applicationhost.config 파일에서 <customHeaders/> 부분에 <add name=”Access-Control-Allow-Origin” value=”*” />와 <add name=”Access-Control-Allow-Headers” value=”Content-Type” />를 추가합니다. 예를 들어 아래와 같습니다.

<httpProtocol>
    <customHeaders>
        <clear/>
        <add name="X-Powered-By" value="ASP.NET"/>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
    </customHeaders>
    <redirectHeaders>
        <clear/>
    </redirectHeaders>
</httpProtocol>

NGINX

NGINX가 설치된 폴더 안에 conf/nginx.conf 파일에서 ‘location /’ 부분의 블럭에 ‘add_header ‘Access-Control-Allow-Origin’ ‘*’;’ 추가합니다. 예를 들어 아래와 같습니다.

location / {
    #root   html;
    root e:/;
    add_header 'Access-Control-Allow-Origin' '*';
    index  index.html index.htm;
}

JavaScript 새로운 문법 정리

자바스크립트에서 var로 정의된 변수의 유효범위는 함수입니다. 일반적인 프로그래밍 언어에서 변수의 유효범위는 블럭(Block)인데요. 자바스크립트에서 블럭의 범위 지정을 위한 시작은 ‘{‘이며 끝은 ‘}’입니다. let을 통한 변수의 정의는 바로 이러한 블럭 범위에서만 유효한 변수를 정의할 수 있도록 합니다.

이러한 let 키워드를 통해 보다 분명한 의미로 변수를 정의해, 필요한 시점에서만 메모리에 적재되어 사용되고 필요하지 않은 시점에서는 메모리에서 제거되도록 할 수 있는데요. 아래의 예는 배열에 대한 각 요소를 저장하기 위해 i라는 변수를 let으로 선언하고 있습니다.

let a = [1, 2, 3];
for (let i of a) {
    alert(i);
}

let 키워드는 IE나 Chrome 모두에서 지원되지만, 위의 for 문은 IE(v11)에서 지원하지 않습니다.

아래의 코드는 배열에 대한 각 소요를 순회하며 처리할 수 있는 함수를 지정할 수 있는 배열의 forEach 매서드에 대한 예제 코드입니다.

function logArrayElements(element, index, array) {
    alert("a[" + index + "] = " + element);
}

[2, 5, 9].forEach(logArrayElements);

아래의 코드는 C++, Java, C#의 람다 함수와 매우 유사한 Javascript의 문법입니다. IE(v11)에서는 지원하지 않으며 Chrome에서만 지원합니다.

// Case 1 Start
sayHello = name => alert("Hello, " + name);
sayHello("Dip2K");
// Case 1 End

// Case 2 Start
setTimeout(() => alert("timeout!"), 2000);
// Case 2 End

// Case 3 Start
[2, 4, 8].forEach(item => alert(item));
// Case 3 End  
      
// Case 4 Start
calcCircumference = diameter => {
    return Math.PI * diameter;
}

alert(calcCircumference(10));
// Case 4 End

Javascript에서 함수를 정의시에 인자에 기본값을 지정할 수 있는데요. 아래의 코드에서 그 예를 명확히 살펴볼 수 있습니다.

function volume(l = 10, w, h = 10) {
    alert(l + " " + w + " " + h);
}

volume();

volume2 = (l = 10, w, h = 10) => {
    alert(l + " " + w + " " + h);
};

volume2();

Javascript에서는 C나 C#에서처럼 변수를 활용한 형식화된 문자열을 쉽게 구성할 수 있는 문법을 제공합니다. 아래는 first와 last라는 변수를 문자열을 구성하기 위해서 ${} 사용하고 있습니다. 문자열 구성을 위해 외따움표(‘)나 쌍따움표(“)가 아닌 `를 사용해야 한다는 점에 유의해야 합니다.

let first = "Kim";
let last = "Hyoung Jun";

const welcome = `You have logged in as ${first} ${last}`;

alert(welcome);

아래의 문법은 IE(v11)에서는 지원하지 않지만, 매우 유용한 것으로서 배열을 합치거나 복제하는데 유용하게 사용될 수 있습니다.

// Case 1 Start
const odd = [1, 3, 5];
const nums = [2, 4, 6, ...odd];
alert(nums);
// Case 1 End

// Case 2 Start
const odd2 = [...odd];
alert(odd2);
// Case 2 End

// Case 3 Start
const { a, b, ...z } = { a: 1, b: 2, c: 3, d: 4 };
alert(a);
alert(b);
alert(z);
// Case 3 End

Case 1은 odd 배열과 nums 배열을 합치는 것이고, Case 2는 odd 배열을 odd2 배열로 복제(참조가 아닌)하는 것입니다. Case 3는 다소 그 성격이 다른데 a는 1, b는 2, z는 {c:3, d:4} 객체가 됩니다.

아래의 문법은 Javascript의 람다 함수(명확히 람다 함수라고 불르는 것이 맞는지는 확실치 않으나)를 사용해 코드를 간결하게 표현한 것으로써, 배열의 요소 중 특정 조건에 일치하는 요소를 검색하는 것으로, 특정 조건을 람다 함수로 정의해 표현하고 있습니다.

const pets = [
    { type: 'Dog', name: 'Max' },
    { type: 'Cat', name: 'Karl' },
    { type: 'Dog', name: 'Tommy' }
];

pet = pets.find(pet => pet.name === 'Tommy');
alert(pet.type);

FingerEyes-Xr for HTML5의 DeferableLabelText 추상 클래스 사용하기

FingerEyes-Xr에서 제공하는 DeferableLabelText 클래스는 이원화된 DBMS로부터 데이터와 공간 데이터를 연계해 라벨의 텍스트로 표현할 수 있는 기능을 제공합니다. 이 글은 DeferableLabelText 클래스의 활용예에 대해서 최대한 간략하게 정리한 글입니다.

먼저 DeferableLabelText를 상속받은 예는 아래와 같습니다.

MyDeferableLabelText = Xr.Class({
    name: "MyDeferableLabelText",
    extend: Xr.theme.DeferableLabelText,

    construct: function () {
        this.superclass();
    },

    methods: {
        /* void */ requestLabelText: function (fid) {
            var that = this;

            // 이 부분은 DBMS로부터 데이터를 가져오는 AJAX 코드로 대체되어야 할 부분  
            setTimeout(function () {
                that.setText(fid, "HELLO");
            }, 1000);
        }
    }
});

이렇게 정의한 MyDeferableLabelText 클래스는 수치지도 레이어에 지정되며 아래의 예와 같습니다.

var lyr = new Xr.layers.ShapeMapLayer(layerId, ...);
lyr.deferableLabelText(new MyDeferableLabelText());

FingerEyes-Xr의 DeferableShapeDrawTheme 추상 클래스

FingerEyes-Xr for HTML5는 DeferableShapeDrawTheme 추상 클래스를 제공합니다. Deferable은 “미룰 수 있는”이라는 의미인데요. 수치지도 레이어를 그리기 위한 심벌의 지정을 미룬다는 의미입니다. 이 추상 클래스는 상속 받아 구현해야 하는 매서드는 /* void */ requestCondition: function (/* int */fid)입니다. 이 클래스의 목적에 대한 설명은 잠시 미루고 이 클래스를 상속받아 만든 예로써 MyDeferableShapeDrawTheme 클래스는 아래와 같습니다.

MyDeferableShapeDrawTheme = Xr.Class({
    name: "MyDeferableShapeDrawTheme",
    extend: Xr.theme.DeferableShapeDrawTheme,

    construct: function() {
        this.superclass();

        this._ON_sds = new Xr.symbol.ShapeDrawSymbol();
        this._ON_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
            { width: 16, height: 16, url: "images/gis/facility/OH_SW_ON.png" }));

        this._OFF_sds = new Xr.symbol.ShapeDrawSymbol();
        this._OFF_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
            { width: 16, height: 16, url: "images/gis/facility/OH_SW_OFF.png" }));
    },

    methods: {
        /* void */ requestCondition: function (fid) {
            var that = this;
            var sql = "SELECT swstatcd FROM ecl_sw_p WHERE fid = " + fid;
            var url = mg_MapLayers.GIS_HTTP_SERVER + "/Xr?sql|" + encodeURIComponent(sql) + "|mg_db|1"

            $.ajax({
                url: url,
                dataType: "text",
                type: "GET",
                statusCode: {
                    200: function (response) {
                        // response 문자열 끝에 \0 문자를 제거
                        response = response.substr(0, response.length - 1); 

                        var result = JSON.parse(response);
                        if (result.length == 1) {
                            if (result[0]["swstatcd"] === "ON") {
                                that.setSymbol(fid, that._ON_sds);
                            } else {
                                that.setSymbol(fid, that._OFF_sds);
                            }
                        }                        
                    }
                }
            });            
        }
    }
});

위의 코드는 순수한 Javascript 코드이며, FingerEyes-Xr 방식의 클래스 정의입니다. 위의 코드에서 보이는 것처럼 3번 코드에서 Xr.theme.DeferableShapeDrawTheme 클래스는 확장한다고 명시되어 있고, 18번 코드에서 requestCondition 매서드를 구현하고 있습니다. 5번 코드가 생성자 함수인데요. 이 생성자에서는 2개의 이미지 심벌을 정의하고 있습니다. 이 2개의 이미지 심벌은 설비 따위에 대한 상태값으로 ON 상태의 이미지와 OFF 상태의 이미지를 나타냅니다. 이렇게 정의된 심벌은 18번 코드의 매서드인 requestCondition에서 사용되는데요. 이 requestCondition은 이름 그대로 “조건을 요청한다”라는 매서드로 그 구현 코드를 보면 어떤 SQL문을 서버측에 요청하고 있습니다. 그 요청 결과는 JSON으로 받게 되는데요. 34~38번 코드에서처럼 JSON 결과 중 swstatcd 필드의 값이 ON일때와 그 외의 값일때에 대해 setSymbol 매서드를 호출해 표현할 심벌을 지정해 줍니다.

이렇게 정의된 MyDeferableShapeDrawTheme 클래스는 수치지도 레이어를 생성하고 추가할때 사용되는데요. 아래의 예와 같습니다.

var lyr = new Xr.layers.ShapeMapLayer(layerId, ...);
lyr.deferableTheme(new MyDeferableShapeDrawTheme());

이해를 돕고자 위의 코드가 적용된 실행 결과에 대한 지도 화면은 다음과 같습니다.

위의 지도 화면에서 파랑색의 ON 아이콘과 빨간색의 OFF 아이콘 심벌이 위의 코드를 통해 반영된 심벌 결과입니다.

그렇다면 이 클래스의 제공하는 이유는 무엇일까요? 일반적으로 GIS의 DB 구조를 살펴보면 공간 데이터와 속성 데이터가 동일한 DBMS의 동일한 Database 내에 존재하게 됩니다. 그러나 전체 시스템이 커지거나, 타 시스템와의 연계가 필요할 경우 도형 데이터와 속성 데이터가 전혀 다른 DBMS로 분리 구분되어 저장됩니다. 바로 이러한 환경에서도 공간 데이터와 다른 DBMS의 속성 데이터가 접목되어 유연하게 조합되어 활용될 수 있도록 하기 위해 제공되는 클래스가 바로 DeferableShapeDrawTheme입니다.

끝으로, ShapeDrawSymbol 클래스를 통한 심벌 정의 시에 Brush와 Pen에 대한 심벌의 정의 예는 아래와 같습니다.

this._ON_sds = new Xr.symbol.ShapeDrawSymbol();
this._sds.brushSymbol(new Xr.symbol.BrushSymbol({ color: 'red' }));
this._sds.penSymbol(new Xr.symbol.PenSymbol({ color: 'yellow', width: 2 }));
this._ON_sds.markerSymbol(new Xr.symbol.ImageMarkerSymbol(
    { width: 16, height: 16, url: "images/gis/facility/OH_SW_ON.png" }));

DuraMap-Xr에서 특정 도형 하일라이팅(Highlighting) 시키기

DuraMap-Xr에서 SHP 파일로 구성된 레이어의 특정 번호의 도형을 아래의 화면처럼 하일라이팅(깜빡거림) 시키는 코드에 대한 설명 입니다.

듀라맵에서는 특정 도형을 직접적으로 하일라이팅 시키는 API를 제공하지는 않지만, 기본적으로 제공하는 API와 Event를 이용하면 가능합니다. 이와 같은 기능은 GIS 어플리케이션에서 많이 사용되므로, 이 기능 하나에 대해 쉽게 재사용할 수 있도록 클래스화하여 제공합니다. 클래스의 이름은 DuraMapUtility_HilightingShape인데요. 이 글은 DuraMapUtility_HilightingShape 클래스를 사용하는 내용에 대한 정리입니다.

먼저 DuraMapUtility_HilightingShape 클래스에 대한 객체 변수를 원하는 곳에 선언합니다. 필자의 경우 듀라맵의 지도 컴포넌트가 놓여진 폼(Form)의 private 변수로 선언하였습니다.

private DuraMapUtility_HilightingShape HilightingShape = null;

위의 DuraMapUtility_HilightingShape 클래스에 대한 객체 변수를 폼의 생성자에서 다음처럼 생성 하는데요. 생성자의 인자로 받는 것은 듀라맵의 지도뷰 객체입니다.

public Form1()
{
    ....

    HilightingShape = new DuraMapUtility_HilightingShape(axXr1);
}

그리고 듀라맵의 지도뷰 객체이 제공하는 이벤트 중 OnCustomDraw에 대해 다음처럼 입력합니다.

private void axXr1_OnCustomDraw(object sender, AxXrMapLib._IXrMapControlEvents_OnCustomDrawEvent e)
{
    HilightingShape.OnCustomDrawEvent(e);
}

여기까지가 원하는 도형을 하일라이팅하기 위한 모든 준비 과정입니다.

이제 다음처럼 언제든지 어떤 레이어를 구성하는 도형을 하일라이팅할 수 있는데요. Hilighting 함수의 첫번째 인자는 하일라이팅하고자 하는 레이어이고 877이라는 숫자값은 하일라이팅되는 도형에 대한 FID 값입니다.

HilightingShape.Hilighting("lyr", 877);

참고로 아래의 코드는 사용자가 지도 상에서 마우스를 클릭했을 때 클릭되어진 도형의 FID를 얻는 함수로, DuraMap-Xr의 지도뷰가 제공하는 OnLButtonUp 이벤트에서 실행되는 코드입니다.

private void axXr1_OnLButtonUp(object sender, AxXrMapLib._IXrMapControlEvents_OnLButtonUpEvent e)
{
    XrMapLib.IValueList FIDs = axXr1.GetFIDFromMousePoint("lyr", e.x, e.y, false);
    if (FIDs.Count > 0)
    {
        int FID = FIDs.GetValueAsInteger(0);
        // 위의 FID가 사용자가 클릭한 도형의 고유한 Feature ID 값 입니다.
    }
}

DuraMapUtility_HilightingShape 클래스와 이 클래스를 사용하는 예제 코드는 다음의 URL에서 다운로드할 수 있습니다.