카테고리 보관물: Programming

[Script] bash root 권한 확인

Bash 스크립트 작성시 ROOT 여부 확인

아래와 같이 코드를 도입부에 추가하면 루트 권한이 있는 경우에만 실행 되도록 할 수 있습니다.

#!/bin/bash
ROOT_UID=0
E_NOTROOT=67

if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Script have to execute on ROOT mode"
exit $E_NOTROOT
fi

Cordova Pros & Cons

Cordova


최근에는 Feedly 를 이용하여 제가 주로 보는 글을 RSS 형태로 보고 있습니다. 매번 사이트에 들어가서 확인하기도 힘들고 해서 이용하는 굉장히 편리한 어플리케이션 입니다.

오늘 본 글 중에 Cordova의 정의, 동작원리, 장점, 단점등에 대한 내용중 장, 단점과 Phonegapcordova 에 대한 내용만 정리해보려고 합니다.

정리 내용중에는 제 경험도 일부 포함하였습니다.

원문: http://code.tutsplus.com/tutorials/an-introduction-to-cordova-basics–cms-25146

장단점

새로운 프로젝트에 cordova 또는 Phonegap을 도입하려고 한다면 아무래도 집중적으로 살펴야 하는 부분이 단점에 대한 부분인거 같습니다.

신경쓰지 않으면 나중에 아주 힘든 상황이 발생될 수 있습니다.

장점

1. 쉽게 배울 수 있다.

cordova는 HTML페이지만 잘 만든다면 바로 사용할 수 있습니다.

2. 네이티브 API 접근 가능

카메라, 연락처, 위치정보, 미디어등 기기에서 제공하는 항목에 대해 접근이 가능합니다.

3. 공짜

제가 생각하는 가장 중요한 이유입니다.

4. 오픈소스

5. 다양한 커뮤니티 및 많은 사용자

cordova에 관련된 이슈에 대해 문제 해결을 하기 위해 검색하면 많은 결과가 나옵니다. 비슷한 이슈사항을 가진 사람이 많으면 버그도 빠르게 수정될 수 있습니다.

6. 다양한 플랫폼 지원

안드로이드, iOS, Windows에 모두 지원되는 어플리케이션을 단일 코드로 작성할 수 있습니다.

단점

단점에 대한 경험을 제가 아는대로 공유해보겠습니다.

1. 문서 미흡

2. 네이티브 소스보다 느리다.

당연한 내용이지만 cordova 로 어플리케이션을 개발하면서 네이티브 어플리케이션과 동일한 성능을 나타낸다는 것은 어불성설입니다.

cordova는 하나의 소스로 다양한 플랫폼을 지원하는 편의성이 있지만 거기에 따른 비용을 무시할 수는 없습니다. 이것은 다른 하이브리드 어플리케이션도 비슷하다고 생각합니다.

3. 프레임워크

iOS 네이티브 어플리케이션의 경우 UITableView 형태의 구현이 정말 쉽습니다. 그렇지만 이와 같은 형태를 Phonegap으로 구현하기 위해서는 네이티브보다 훨씬 어려운 방법이 동원되어야 합니다.

또한 네이티브에서 사용되는 페이지 전환 애니메이션 효과를 나타내기 위해서도 많은 코드및 꼼수가 포함되어야 합니다.

cordova가 HTML 페이지를 Webview 에서 표시하는 형태이기 때문에 어플리케이션 사용시 네이티브 어플리케이션과 유사하게 만들어야 한다면 많이 어렵습니다.

4. 플러그인 버그

iOS 9 버전이 발표되며 고객사의 요구사항에 어플리케이션 대상에 iOS 9이 포함되었습니다. 요청에 대해 대응을하고자 빌드를 하였지만 iOS8에서는 정상동작을 하지만 iOS 9에서 잘못 표시되는 항목이 발생하였습니다.

원인은 포함된 플러그인의 버그 였습니다. 저희가 선택할 수 있는 방법은 플러그인이 업데이트 되기를 기다리는 방법과 플러그인을 사용하지 않고 빌드된 파일을 변경하는 방법이었습니다.

결론은 빌드된 파일을 변경하였습니다. 3rd 파티 플러그인의 경우 개발자가 빠르게 대응해준다면 고마운 일이지만 그렇지 않다면 언제 개선될지 모르기 때문입니다.

5. 모든 플랫폼에 동일하지 않다

폰갭으로 빌드한 결과물을 실제 단말에 올려보기 전에는 화면 표시 및 다른 부분이 어떻게 동작할지 예상할 수 없습니다. iOS에서 동작이 잘되니까 Android에서도 잘되겠지라는 생각은 금물입니다.

폰갭(Phonegap)과 Cordova

Phonegap API를 참조하다 보면 링크가 cordova 페이지로 넘어가는 경우가 많습니다. 간단하게 생각하면 cordova를 한번 감싼게 Phonegap이라고 생각됩니다.

두개를 로컬에서 빌드한다면 명령어만 다르고 나머지는 대동소이합니다.

[Nodejs] CentOS 설치

1. Download Node binary

https://nodejs.org/en/download/ 링크로 이동하여 바이너리 파일을 다운로드 합니다.

* 아래 링크는 설치 일시 및 노드 버전에 따라 달라질 수 있습니다.

$ wget https://nodejs.org/dist/v4.2.4/node-v4.2.4-linux-x64.tar.gz

2. 압축 해제 및 압축 해제 파일 이동

2.1 다운로드 파일 압축 해제

$ tar xfz node-v4.2.4-linux-x64.tar.gz

2.2 압축 해제 파일 이동

$ mv node-v4.2.4-linux-x64 /usr/local/node

3. 환경 변수 설정하기

자신의 환경 변수 파일을 수정합니다. (.profile or .bashrc)

[~/.profile]

export NODE_HOME=/usr/local/node
export PATH=$PATH:$NODE_HOME/bin

4. 설치 결과 확인

[coozplz@localhost ~]$ node
> console.log('helloworld')
helloworld
undefined

[Java] Lambda(Stream) 는 항상 좋을까?

[원글] http://www.javacodegeeks.com/2015/12/3-reasons-shouldnt-replace-loops-stream-foreach.html

Java 1.8이 출시된 후 많은 블로그 및 홈페이지에서 Lambda 를 소개하는 글들이 많이 올라와 있습니다. 그렇지만 대부분의 글에 Lambda의 장점들에만 치중하는 내용이 대부분인데 오늘 RSS 피드에 괜찮은 내용이 있어 요약해 봅니다.

1. 성능

Lambda(Stream) 와 For-loop 방식의 처리에는 성능의 차이가 발생된다.

ArrayList, for-loop : 6.55 ms
ArrayList, seq. stream: 8.33 ms
int-array, for-loop : 0.36 ms
int-array, seq. stream: 5.35 ms

위의 성능의 차이는 어찌 보면 큰 차이는 아닌것 같지만 대량의 데이터를 처리해야 하는 로직에 포함된 Stream API라면 많은 성능 차이를 일으킬 수 있습니다.

2. 가독성

저도 간단하게 Stream API를 이용하여 Filter및 Collector를 이용하면서 소스 코드가 확연히 줄어든 경험이 있지만 가독성은 Stream API를 사용하기 전보다 떨어지는 것을 느꼈습니다.

단순 리스트에서 동일한 값을 뽑아내는 형태의 것은 문제가 없다고 생각하지만 Filter를 이용해서 값을 추출하고 추출한 값을 다시 가공하고 연산하여 데이터를 취합하는 형태를 구현 한다면 훨씬 복잡하고 이해하기 힘든 코드가 될 것이라는 생각입니다.

또한 다수의 사용자가 동일한 코드를 작업하는 환경이라면 모든 사용자가 익숙하지 않다면 Stream API 사용을 자제하는 것이 좋다고 생각됩니다.

3. 유지보수

대부분의 코드는 단발성이 아닌 수정이 빈번하게 발생되는 경우가 있습니다. Stream API를 사용하는 경우 StackTrace에 불필요한 내용이 출력되어 오류 분석에 어려움이 있을 수 있습니다.

[For-loop]

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:13)

[Lambda]

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.lambda$1(Test.java:18)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
    at Test.lambda$0(Test.java:17)
    at java.util.Arrays$ArrayList.forEach(Arrays.java:3880)
    at Test.main(Test.java:16)

이상입니다.

[InnoSetup] MsgBox 일본어 깨짐

문제점

InnoSetup에서 경고창을 띄우기위해 code 섹션에 MsgBox 함수를 호출하는 경우가 있는데 iss 파일에서는 정상적으로 일본어를 입력하였으나 실제 출력되는 내용은 깨진 글자가 출력되는 현상

해결방안

아래 소스와 같이 언어 설정을 하고 커스텀메시지를 추가한다. 추가한 커스텀 메시지를 MsgBox에서 호출하도록 처리하면 일본어 글자가 정상적으로 출력된다.

[Languages]
Name: jp; MessagesFile: "compiler:Default.isl"

[CustomMessage]
jp.CheckError='権限がありません。'

[Code]
procedure InitializeWizard;
begin
MsgBox(ExpandConstant('{cm:CheckError}'), mbInformation, MB_OK);
end;

[Python] 크리스마스트리

제작 의도

어제 다음 카페 게시글을 보다 보니 각 학과별 크리스마스트리를 그리는 방법이라는 글이 올라와 있어 Python을 이용하여 크리스마스 트리를 제작해봤습니다.

모두 즐거운 성탄절 되세요..!!

소스 코드

ROW = 9


def printTree(maxCountOfSpaceNStart):
    for i in range(1, maxCountOfSpaceNStart):
        print '%*s%*s' % (ROW-i, ' ', 0, '*' * (i * 2 -1))

if __name__ == '__main__':
    print '=' * 20
    print '%2s%s' % (' ', 'Merry Christmas!!!')
    print '=' * 20
    trees = [5, 7, 9]
    for i in trees:
       printTree(i)

실행 결과

====================
  Merry Christmas!!!
====================
        *
       ***
      *****
     *******
        *
       ***
      *****
     *******
    *********
   ***********
        *
       ***
      *****
     *******
    *********
   ***********
  *************
 ***************

[AS3] Custom Sliderbar

플래시 가로 슬라이더바

개발이유

플래시에서 제공하는 슬라이더 컴포넌트는 너무 플래시스럽지 않아 자체적으로 제작하여 사용하였습니다.

개발내용

  1. 슬라이더는 이미지를 사용하지 않고 Rectangle 객체를 이용하여 처리한다.
  2. 슬라이더의 값이 변경되면 이벤트를 발생한다.
  3. 슬라이더의 단계를 입력하여 단계별로 이벤트를 발생시킨다.

결과물

CustomSlider

소스

[HorizontalSlider.as]

package  {
    import flash.geom.Rectangle;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.display.GradientType;
    import flash.geom.Matrix;
    import flash.display.SpreadMethod;

    public class HorizontalSlider extends Sprite {

        public static const SLIDER_CHANGE:String = "sliderChange";

        var sliderBackground:Sprite;
        var sliderKnob:Sprite;

        var sliderKnobWidth:Number = 25;
        var sliderKnobHeight:Number = 40;

        var sliderBackgroundWidth:Number;
        var sliderBackgroundHeight:Number;

        var prevX:Number;
        var boundaries:Rectangle;

        var stepWidth:Number = 40;

        /**
        * 슬라이더 초기 좌표를 저장한다. StepIndex에 따라 슬라이더를 가운데에서 좌, 우로 이동한다.
        */
        var sliderKnobCenterPositionX:Number = 0;

        protected var _isPressed:Boolean;

        /**
        * 슬라이더 바를 생성하도록 한다. 슬라이더는 Rectangle Object 두개를 이용하도록 처리한다.
        * Background에 생성되는 Rectangle도 MOUSE_CLICK 이벤트를 수신하여 슬라이더 버튼을 이동시킨다.
        *
        * @param sliderWidth    슬라이더의 너비을 설정한다.
        * @param sliderHeight   슬라이더의 높이를 설정한다. 
        * @param stepCount      슬라이더 버튼을 Index의 위치에 맞게 이동시킨다.
        *                       슬라이더 이동 범위 (sliderWidth / stepCount) 룰을 따른다.
        *
        */
        public function HorizontalSlider(sliderWidth:Number, sliderHeight:Number, stepCount:Number) {
            _isPressed = false;

            this.sliderBackgroundWidth = sliderWidth;
            this.sliderBackgroundHeight = sliderHeight;

            this.sliderKnobWidth = Math.round(sliderBackgroundWidth / 15);
            this.sliderKnobHeight = Math.round(sliderHeight + 20);

            boundaries = new Rectangle(0, -10, sliderWidth - this.sliderKnobWidth, 0);          

            this.stepWidth = int(sliderWidth / stepCount);

            drawSliderBackground();
            drawSliderKnob();           
            bindSliderEvents();

        }

        private function bindSliderEvents() : void {
            this.sliderKnob.addEventListener(MouseEvent.MOUSE_DOWN, onSliderKnobMouseDownHandler);
            this.sliderKnob.addEventListener(MouseEvent.MOUSE_UP, onSliderKnobMouseUpHandler);

            this.sliderBackground.addEventListener(MouseEvent.MOUSE_UP, onSliderBackgroundMouseUpHandler);
        }




        private function drawSliderBackground() : void {            
            sliderBackground = new Sprite();


            sliderBackground.graphics.lineStyle(2, 0xF298A2);
            sliderBackground.graphics.beginFill(0xFFFFFF);
            sliderBackground.graphics.drawRoundRect(0, 0, this.sliderBackgroundWidth, this.sliderBackgroundHeight, 5);          
            sliderBackground.graphics.endFill();
            sliderBackground.width = this.sliderBackgroundWidth;
            sliderBackground.height = this.sliderBackgroundHeight;
            trace(sliderBackground.width);
            trace(sliderBackground.height);

            this.addChild(sliderBackground);
            sliderBackground.x = 0;
            sliderBackground.y = 0;         
        }


        private function drawSliderKnob() : void {
            this.sliderKnob = new Sprite();
            this.addChild(sliderKnob);
            this.sliderKnob.graphics.beginFill(0xE53F00);
            this.sliderKnob.graphics.drawRoundRect(0, 0, sliderKnobWidth, sliderKnobHeight, 5);
            this.sliderKnob.graphics.endFill();     



            sliderKnob.x = (sliderBackground.width - sliderKnob.width) / 2;
            sliderKnob.y = sliderBackground.y - 10;

            //
            // 중간값을 저장하고 슬라이더 버튼을 이동시킨다.
            //
            sliderKnobCenterPositionX = sliderKnob.x;           
        }


        private function onSliderKnobMouseDownHandler(e:MouseEvent) : void {            
            this.sliderKnob.startDrag(false, boundaries);

            stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUpHandler);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMoveHandler);
            this.prevX = this.sliderKnob.x;
            _isPressed = true;
            this.sliderBackground.removeEventListener(MouseEvent.MOUSE_UP, onSliderBackgroundMouseUpHandler);

        }


        private function onStageMouseMoveHandler(e:MouseEvent) : void {
            var currentPositionX = this.sliderKnob.x;

            if (_isPressed) {               
                if (Math.abs(currentPositionX - prevX) > 0) {                                        
                    prevX = currentPositionX;
                    // dispatchEvent(new Event(HorizontalSlider.SLIDER_CHANGE));
                    e.updateAfterEvent();
                } 

            }
        }


        private function onStageMouseUpHandler(e:MouseEvent) : void {
            this.sliderKnob.stopDrag();
            stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUpHandler);
            stage.removeEventListener(MouseEvent.MOUSE_DOWN, onStageMouseMoveHandler);
            _isPressed = false;
            this.sliderBackground.addEventListener(MouseEvent.MOUSE_UP, onSliderBackgroundMouseUpHandler);

            this.calculatePositionX(sliderKnob.x);
        }


        private function onSliderKnobMouseUpHandler(e:MouseEvent) : void {
            this.sliderKnob.stopDrag();

            stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUpHandler);
            stage.removeEventListener(MouseEvent.MOUSE_DOWN, onStageMouseMoveHandler);
            _isPressed = false;
            this.sliderBackground.addEventListener(MouseEvent.MOUSE_UP, onSliderBackgroundMouseUpHandler);

            this.calculatePositionX(sliderKnob.x);

        }


        private function onSliderBackgroundMouseUpHandler(e:MouseEvent) : void {
            var target:Sprite = e.target as Sprite;                         
            sliderKnob.x = e.localX - (this.sliderKnob.width / 2);

            this.calculatePositionX(sliderKnob.x);
        }


        private function calculatePositionX(x:Number) : void {
            var stepIndex:int = int(sliderKnob.x / this.stepWidth) + 1;                                     
            var offsetCount:Number = stepIndex - (int(int(this.sliderBackgroundWidth / this.stepWidth) / 2)+1);         
            this.sliderKnob.x = this.sliderKnobCenterPositionX + (offsetCount * this.stepWidth);        



            var param:Object = {
                "index" : stepIndex
            };
            this.dispatchEvent(new HorizontalSliderEvent(HorizontalSliderEvent.HorizontalSliderEvent, param));

        }
    }

}

[HorizontalSliderEvent.as]

package  {
    import flash.events.Event;

    /**
    * 슬라이더값 변경시 발생되는 이벤트
    * 
    * <사용법>
    * 
    *   //
    *   // Dispatch Event
    *   //
    *   var param:Object = {
    *       "index" : currentIndex
    *   };
    *   this.dispatchEvent(new HorizontalSliderEvent(HorizontalSliderEvent.HorizontalSliderEvent, param));
    *
    */
    public class HorizontalSliderEvent extends Event {

        public static const HorizontalSliderEvent:String = "HorizontalSliderEvent";

        public var param:Object;    


        public function HorizontalSliderEvent(type:String, param:Object, bubbles:Boolean = false, cancelable:Boolean = false) {
            super(type, bubbles, cancelable);
            this.param = param;
        }


        override public function toString() : String {
            return "HorizontalSliderEvent: " + param.toString();
        }

    }

}