웹서버에 대한 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" }));