플래시 빌더(Flash Builder)에서 협업을 위한 모듈화

플래시 빌더의 단점은 협업 개발이 어렵다는 것입니다. 사실.. 아직까지도 플래시에서 모듈을 통한 협업 개발이 충분한가라는 점에서.. 실제 프로젝트에 적용해보기 전까지는 확신할 수는 없지만 파악해본 바로는 충분히 가능하다.. 라고 판단됩니다.

메인 어플리케이션 프로젝트가 있고.. 이 메인 어플리케이션에서 사용하는 또 다른 별도의 모듈을 위한 프로젝트(모듈 프로젝트)가 있다고 하겠습니다. 모듈 프로젝트는 일반적인 플렉스 프로젝트(Flex Project)로 생성합니다.

이 모듈 프로젝트에 2개의 모듈을 추가하도록 하겠습니다. 첫번째는 단순히 함수만을 제공하는 모듈 클래스이고 두번째는 UI를 제공하는 모듈 클래스입니다. 먼저 함수만을 제공하는 모듈 클래스는 ActionScript Class로 해서 생성하며 클래스명은 Module1으로 지정하고 Super Class로는 반드시 ModuleBase로 지정합니다. 생성된 Module1.as 파일을 다음처럼 코딩합니다.

package
{
    import mx.modules.ModuleBase;
 
    public class Module1 extends ModuleBase
    {
        private var arg:Number = 1;
  
        public function Module1(arg:Number)
        {
            super();
            this.arg = arg;
        }
  
        public function sum(a:Number, b:Number):Number
        {
            return (a + b) * arg;
        }
    }
}

모듈 클래스의 생성자가 하나의 인자를 갖습니다. 그리고 sum이라는 함수를 제공합니다. 매우 단순한 모듈 클래스입니다. 이 모듈 클래스는 컴파일되어 Module1.swf 파일로 만들어져야 합니다만 확장자가 as는 자동으로 모듈로 만들어지지 않으므로 모듈 프로젝트의 속성창에서 Flex Modules에서 추가해야 합니다.

사용자 삽입 이미지
추가할때 Output Size 옵션을 Do not optimize로 선택해서 다양한 많은 어플리케이션에서 활용될 수 있도록 지정합니다.

이제 추가된 모듈 클래스인 Module1을 별개의 어플리케이션 프로젝트에서 사용해 보도록 하겠습니다. 버튼을 클릭했다고 할때 Module1 모듈 클래스를 활용하는 방식으로 하겠습니다. 버튼 클릭시 실행되는 코드는 다음과 같습니다.

private var iModuleInfo:IModuleInfo;
protected function onTest(event:MouseEvent):void
{
    iModuleInfo = ModuleManager.getModule(
        "../../eTAS_Modules/tstModule/bin-debug/Module1.swf");
    iModuleInfo.addEventListener(ModuleEvent.READY, onModuleReady);
    iModuleInfo.addEventListener(ModuleEvent.ERROR, onModuleError);
    iModuleInfo.load();
}

5번째 줄에 모듈 프로젝트에서 생성된 Module1.swf를 지정하고 있습니다. 로컬에서 테스트할때는 이렇게 폴더 경로로 사용되지만 실제로 웹서버에 올려질때는 URL 경로가 사용됩니다. 모듈의 준비가 잘되었을때 실행되는 onModuleReady와 실패했을때 실행되는 onModuleError 함수는 다음과 같습니다.

private function onModuleReady(e:Event):void
{
    e.target.removeEventListener(ModuleEvent.READY, onModuleReady);
    e.target.removeEventListener(ModuleEvent.ERROR, onModuleError);
    
    var mainClassName:String = iModuleInfo.factory.info().mainClassName;
    
    var mainClass:Class = 
        iModuleInfo.factory["getDefinitionByName"](mainClassName) as Class;
    var module:Object = new mainClass(100); 

    text.text = module.sum(100, 200);
 }
   
private function onModuleError(e:Event):void
{
    e.target.removeEventListener(ModuleEvent.READY, onModuleReady);
    e.target.removeEventListener(ModuleEvent.ERROR, onModuleError);
    
    // ERROR 
}

6번에서 모듈의 클래스명을 가져오고 이 클래스 명으로부터 실제 클래스 타입을 8번 코드를 통해 얻습니다. 그리고 이 클래스 타입을 통해 실제 인스턴스를 10번 코드를 통해 생성합니다. 그리고 12번 코드처럼 사용합니다.

이제 다음으로 UI를 제공하는 모듈에 대한 생성과 사용에 대해서 정리해 보겠습니다. 모듈 프로젝트에서 MXML Module로 하여 새로운 파일을 생성합니다.
 
사용자 삽입 이미지
파일명은 Module2로 하고 Output Size 옵션을 Do not optimize로 하여 다른 많은 프로젝트에서 재활용할 수 있도록 합니다. 생성된 Module2.mxml을 다음처럼 코딩합니다.

  xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
 
    
                    import geoservice.XrMap;
   
            private var map:XrMap = null;
   
            public function setup(map:XrMap):void
            {
                this.map = map;
            }
   
            protected function onClick(event:MouseEvent):void
            {
                map.coordMapper.setMapScale(500);
                map.update();
            }
        ]]>

하나의 버튼 컴포넌트를 배치했고 이 버튼에 대한 클릭 이벤트를 지정했습니다. 그리고 setup이라는 함수를 하나 추가해서 이 UI 모듈의 코드들이 실행하기 위해 필요한 설정값들을 지정할 수 있도록 하였습니다.

이 Module2에 대한 모듈 UI를 별도의 어플리케이션 프로젝트에 사용해 보도록 하겠습니다. 어클리케이션 프로젝트의 메인 mxml에 다음과 같은 UI 컴포넌트를 추가합니다.

네! 바로 앞서 만들어 놓은 Module2 모듈 UI가 들어갈 자리입니다. id는 modLoader로 했음을 기억합시다! 버튼을 클릭하면 이 modLoader에 앞서 만들어 놓은 UI 모듈을 올려 보도록 하겠습니다.

protected function onTest(event:MouseEvent):void
{
    modLoader.addEventListener(ModuleEvent.READY, onUIModuleReady);
    modLoader.addEventListener(ModuleEvent.ERROR, onUIModuleError);
    modLoader.url = "../../eTAS_Modules/tstModule/bin-debug/Module2.swf"
}

모듈 로딩이 성공했을때와 실패했을때에 대한 이벤트 리스너를 지정했고 modLoader의 url 속성에 적재할 UI 모듈에 대한 경로와 파일명을 지정했습니다. 로컬에서는 이처럼 디렉토리 경로지만 실제 웹서버에 올라가면 URL 경로로 변경해줘야 합니다. 모듈 로딩이 성공했을때와 실패했을때에 대한 이벤트 리스너의 코드는 다음과 같습니다.

private function onUIModuleReady(e:Event):void
{
    modLoader.removeEventListener(ModuleEvent.READY, onModuleReady);
    modLoader.removeEventListener(ModuleEvent.ERROR, onModuleError);
    
    modLoader.child.addEventListener(FlexEvent.CREATION_COMPLETE, 
        onUIModuleCreateCompleted);
}

private function onUIModuleError(e:Event):void
{
    modLoader.removeEventListener(ModuleEvent.READY, onUIModuleReady);
    modLoader.removeEventListener(ModuleEvent.ERROR, onUIModuleError);
    
    // ERROR!
}

모듈이 성공적으로 로딩이 되면 6번 코드처럼 UI 모듈을 구성하는 UI가 생성되는 이벤트 리스너를 지정해야 합니다. UI가 생성되는 이벤트 리스너 함수인 onUIModuleCreateCompleted의 코드는 다음과 같습니다.

private function onUIModuleCreateCompleted(e:Event):void
{
    modLoader.child.removeEventListener(FlexEvent.CREATION_COMPLETE, 
        onUIModuleCreateCompleted);
    
    var ui:Object = modLoader.child;
    ui.setup(map);
}

7번 코드가 바로 앞서 UI 모듈에서 추가했던 함수입니다. UI 모듈의 버튼을 클릭하면 지정된 코드가 실행되게 됩니다.

비록…. 모듈에 대한 설명예를 단순하게 들었지만 필요한 것들에 대해서 최소한으로.. 그리고 필요한 모든 것을 설명한듯합니다. 물론 실전에서 적용해보고 부족한 부분에 대해서 덧붙이도록 하겠습니다.

[GIS] 정규표현식을 이용한 SHP 추출, ExpressionsSHP

개발자 피드백 : 포인트 타입의 SHP 파일에 대해 문제가 발생하는 것을 해결 하였습니다.

SHP 파일의 속성값을 기준으로 원하는 값과 일치하는 도형만을 추출해 새로운 SHP 파일로 추출해 내는 툴입니다. 일치하는 속성값의 기준은 정규표현식을 사용합니다.

지적도를 예로 들어 JIBUN이라는 필드값이 ‘도’로 끝나는 모든 도형을 추출하라는 정규 표현식은 ‘도$’가 됩니다. 참고로 ‘도’로 끝나는 경우 도로에 해당합니다. 아래의 실행 화면은 이와 같은 예에 대한 실행 예입니다.

사용자 삽입 이미지
보시는 것처럼 입력 SHP 파일을 지정하고 대상이 되는 필드, 정규표현식값.. 끝으로 조건과 일치하는 도형을 저장할 새로운 SHP 파일명을 지정하면 됩니다. 위의 실행에 대한 결과는 아래와 같습니다.

사용자 삽입 이미지
보시는 것처럼.. 도로만이 추출된 결과를 볼 수 있습니다. 이 프로그램은 듀라맵(http://www.gisdeveloper.co.kr/notice/574)을 이용해 개발되었으므로 듀라맵을 PC에 등록해줘야 합니다. 이 프로그램에 대한 소스 코드와 실행 파일을 아래 링크를 통해 다운받을 수 있습니다.

정규표현식을 통해 원하는 값을 선택해 내는 일은 매우 유연한 방법입니다. 원하는 경우와 상황에 대한 정규표현식에 대해서 댓글을 통해 알려주시면 성의껏 답변해 드리겠습니다.

[iOS] 바이너리 파일(Binary File) 쓰고 읽기

iOS에서 바이너리 파일을 생성하고 정수, 실수 그리고 문자열 값을 순서대로 쓴 후에 다시 읽기 위한 코드에 대해 정리해 봅니다. Objective-C에서 제공하는 파일 관련 클래스를 사용을 시도했으나 Ansi-C에서 제공하는 파일처리 함수를 사용했습니다. 진정 ‘구관이 명관’입니다 !!

UI 상에 정수, 실수 그리고 문자열을 입력받는 텍스트필드 컨트롤을 추가하여 UI를 구성합니다. 그 화면은 다음과 같습니다.
사용자 삽입 이미지
여기서 Save 버튼을 클릭했을때 입력된 값들을 바이너리 파일에 저장하는 코드 예는 다음과 같습니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "wb");
    if(pFile != NULL) {
        int intValue = [tiInteger.text intValue];
        fwrite((void *)&intValue, sizeof(intValue), 1, pFile);
        
        float floatValue = [tiFloat.text floatValue];
        fwrite((void *)&floatValue, sizeof(floatValue), 1, pFile);

        NSString *stringValue = tiString.text;
        const char *bytesStringValue = [stringValue UTF8String];
        int lenStringValue = strlen(bytesStringValue) + sizeof(int);
        fwrite((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        fwrite((void *)bytesStringValue, sizeof(char), lenStringValue, pFile);
        
        fclose(pFile);
    }

iOS는 기본적으로 파일을 쓸 수 있는 디렉토리를 제한해 놓습니다. 파일을 쓸 수 있는 디렉토리를 얻기 위해 1번 ~ 5번 코드가 사용되었습니다. 뭐… 나머지는 fopen과 fwrite와 같은 파일 열기 및 쓰기에 대한 일반 C 함수를 사용했습니다.

다음은 UI 중 Restore 버튼을 클릭했을때 앞서 Save 버튼에 의해 저장된 값들을 복원하는 코드입니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "rb");
    if(pFile != NULL) {
        int intValue;
        fread((void *)&intValue, sizeof(intValue), 1, pFile);

        float floatValue;
        fread((void *)&floatValue, sizeof(floatValue), 1, pFile);
        
        int lenStringValue;
        fread((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        
        NSMutableData *data = [NSMutableData dataWithCapacity:lenStringValue];
        char *bytesStringValue = (char *)[data bytes];
        fread((void *)bytesStringValue, lenStringValue, 1, pFile);
        
        fclose(pFile);
        
        tiInteger.text = [NSString stringWithFormat:@"%d", intValue];
        tiFloat.text = [NSString stringWithFormat:@"%f", floatValue];
        tiString.text = [NSString stringWithUTF8String:bytesStringValue];
    }

저장했던 값들에 대해서 순서대로 읽어 오고 있습니다. 주의할 점은 문자열 값을 읽기 위해서 char 배열을 생성하기 위해 18번 ~ 19번 코드처럼 NSMutableData 클래스를 사용했습니다. Objective-C는 new 연산자를 지원하지 않습니다 !!

“위안부 강제동원 증거 없다”

사용자 삽입 이미지
한 나라의 수장이라는 총리라는 놈이.. 한다는 소리가 고작.. “증거 없다”라니.. 니가 말하는 그 증거 찾을라치면 한없이 찾을 수 있을 건데.. 도대체.. 어제 무슨 술을 쳐먹고 이런 소릴 하는것인가..
 
생각 좀 하고 떠들어라.. 너의 그 말 한마디가 결국 너의 손과 다리를 묶고.. 네가 대표하는 한 나라를 곤란한 지경에 치닫게 되리라는 걸 모르는거냐..

하긴.. 아니되면 ‘총리사퇴’가 이미 익숙해져 버린 네 놈들 나라 풍습이지만 말이다.. 니들 후손을 생각한다면 어찌 이런 말로 거짓됨을 마치 사실인것 처럼 씨부렁댄단 말이냐.. 일본, 너희는 어쩔 수 없는 ‘망국(亡國)’이다..