Pages

September 30, 2013

[Android] android.app.application 을 이용한 공유 변수사용하기

안드로이드 앱을 만들다 보면 각 Activity 전환에 상관없이 항상 유지해야 하는 상태값등을 어떻게 저장해야 할지에 대한 애매한 상황이 발생한다.

이러한 경우 안드로이드 앱이 실행되면 자동으로 객체가 생성되는 android.app.application을 상속받는 클래스를 사용하는 것이 한 방법이 될 수 있다. 이 경우 Context.getApplicationContext() 함수를 호출하여 필요한곳 어디서든 android.app.application  상속 클래스의 인스턴스를 받아서 활용할 수 있다.

AndroidManifest.xml 내에서 아래와 같이 application에 해당하는 클래스를 지정해준다.



...



그리고 android.app.application 을 상속받는 MainClass는 아래와 같이 구현한다.


public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // 전역 범위의 초기화 작업 진행
    }
}

더 자세한 사항은 이 링크를 참조하도록 하자

Google Web Designer



Google Web Designer 프로덕트의 이름만 보명 웹페이지를 쉽게 디자인할 수 있게 해주는 툴 같은데 사실은.. 구글의 광고 플랫폼을 타겟으로 광고 배너를 제작할 수 있게 해주는 툴이다.

 WYSWYG 에디터를 통해 배너를 제작하면 그에 해당하는 HTML5 코드를 생성해준다.

애플은 예전 부터지만 안드로이드 최신버전에서도 플래쉬 지원을 중단했기 때문에 구글로써도 광고 수익을 높이기 위해서는 이런 툴을 제공해서 광고주들이 쉽게 다이내믹한 배너를 제작할 수 있도록 해주는 것이 어찌보면 당연한 선택이지만 이렇게 큰 틀에서 개발자들로 하여금 구글의 프로덕트에 tie-in 되도록하는 동시에 자신의 수익성을 높이는 좋은 전략으로 보인다.

툴은 상당히 구글 스럽게 깔끔하고 향후에 이 툴이 웹을 디자인하는 툴로써 확장이 될 수 있을지는 의문이지만 플래쉬를 쓸 수 없는 환경에서 지금까지는 애니메이션을 코딩하는 수 밖에 없었지만 이 툴이 그런 수고를 많이 덜어 줄 수 있을것으로 기대된다.

알아보니 애플에도 비슷한 프로덕트가 이미 있었다.  iAD Producer란 툴이 공개되어 있다.


애플의 툴은 디벨로퍼 어카운트가 있어야 다운로드 받을 수 있는 듯 하다.


September 27, 2013

Why Git rebase?

Git rebase 를 하면 어떤 일이 발생하고 어떤 경우에 이것을 사용하는 것이 좋은지에 대해서 글을 써보려고 했으나.... 이 링크에서 너무나도 쉽게 그림과 함께 설명이 되어 있어서 글쓰기를 포기했다.

세상엔 능력자 님들이 너무 많다. 하아 난 언제 그런 능력자 반열에 올라서려나..

September 26, 2013

[Android] 디바이스 시간 설정 변경 이벤트 캐치하기

디바이스의 시간 설정이 변경되었음을 나의 앱이 알아차리기 위해서는 시스템이 시간 관련 설정이 변경 되었을 때 Broadcast하는 Intent를 앱이 캐치하도록 하면된다.

여기서 시간 설정 변경이란 사용자가 디바이스의 시간을 수동으로 변경하는 경우나 시간 서버와 동기화로 인해 시간이 변경되는 경우를 의미하고 시간이 흐르는 이벤트는 별개의 것으로 다룬다.

사용자가 시간을 변경하는 경우


사용자가 설정 메뉴을 이용하여 수동으로 시간을 바꾸는 경우 발생하는 이벤트이다. 이 경우 관심을 가져야 하는 Intent는 ACTION_TIME_CHANGED,  ACTION_TIMEZONE_CHANGED로 다음과 같이 Broadcast Receive에 Intent filter를 적용하고 이벤트를 캐치하도록 한다.

  1. ACTION_TIME_CHANGED : 시간, 날짜 변경에 관계 없이 어느 하나라도 변경하는 경우 발생한다. 실제로 디바이스상에서 시간값을 변경하면 이 Intent가 2번 브로드캐스트 되는데 안드로이드 OS의 버그로 보인다. 시간 변경창을 띄우고 시간을 바꾸지 않더라도 뒤로가기 버튼을 누르면 창이 닫히면서 1번 브로드캐스트된다. 명백한 버그로 보인다.
  2. ACTION_TIMEZONE_CHANGED : 타임존 관련 세팅이 변경되면 발생한다. 차를 타고 시간 존이 변경되는 지역을 지나가면 이 이벤트가 발생하는지 안하는지는 테스트 해보지 않았으나 정상적으로 OS가 구현이 되었다면 발생해야 할 것 같지만 위에서 보듯이 안드로이드 OS도 버그가 많아서..어쩔지는 미지수이다. 


AndroidManifest.xml

    
        
        
        
        
    


DeviceEventReceiver.java
public class DeviceEventReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();

        if (Intent.ACTION_TIME_CHANGED.equals(action)
            || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                // 시간이 변경된 경우 해야 될 작업을 한다.
         doSomething();
        }
    }
}


시간이 흐름에 따라 시간이 변경되는 경우

이 경우 캐치해야 하는 Intent는 ACTION_TIME_TICK 혹은 ACTION_DATE_CHANGED이다.

  1. ACTION_TIME_TICK : 매 분마다 이벤트가 발생한다. 이 이벤트는 AndroidManifest에 Intent filter를 적용하는 것으로 캐치할 수 없고 코드내에서 동적으로 등록을 해야한다. 아마도 실수로 이 이벤트에 대한 로직을 추가하여 디바이스 배터리 광탈을 막기위한 목적이 아닌가 한다.
  2. ACTION_DATE_CHANGED : 이 이벤트는 날짜가 변경 될 때 발생한다. 다시 설명하면 어느 날의 11:59  PM에서 자정으로 넘어가 날짜가 변경되는 경우 브로드캐스트 되는 Intent이다. 

각 경우의 예제 코드는 다음과 같다.

ACTION_TIME_TICK 이벤트

SomeActivity.java
private static BroadcastReceiver _tickReceiver;

@Override
public void onCreate() {
    super.onCreate();
    
 _tickReceiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent intent) {
         final String action = intent.getAction();

         if (action.equals(Intent.ACTION_TIME_TICK)) {
             doSomeTask();
         }
     }
    };

    IntentFilter _intentFilter = new IntentFilter();
 _intentFilter.addAction(Intent.ACTION_TIME_TICK);

    registerReceiver(tickReceiver, _intentFilter);
}

@Override
public void onStop() {
 super.onStop();
 
 //unregister broadcast receiver.
 if (_tickReceiver != null)
  unregisterReceiver(_tickReceiver);
}
ACTION_DATE_CHANGED 이벤트

AndroidManifest.xml

    
        
        
    


DeviceEventReceiver.java
public class DeviceEventReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();

        if (Intent.ACTION_DATE_CHANGED.equals(action)) {
                // 날짜가 변경된 경우 해야 될 작업을 한다.
         doSomething();
        }
    }
}


September 25, 2013

[Unity] 유니티의 메모리 관리

유니티 문서를 보면 언급되는 Managed Memory란 단서에서 흔히 추측하기로 유니티가 알아서 모든 메모리를 잘 관리하고 있을 것 처럼 생각되지만 사실은 그렇지 않다는 것이 함정.

사실은 유니티 시스템은 메모리를 어떻게 처리할 것인가에 대한 단서를 당신이 만드는 코드내에서 제공해주기를 원한다. 따라서 잘못된 코드는 줄줄 새는 메모리로 당신의 앱을 디바이스에서 뻗어 버리게 만들것이다. 요즘은 모바일 디바이스에서 조차 64bit 시스템(iPhone 5s)가 올라가고 기본 장착 메모리가 2Gb 이상이 되는 등 모바일 앱으로서는 무한한 자원이 있는것 같지만 사실상 아직도 지구상의 대부분의 유저는 허접한 디바이스로 연명하고 있다는 것을 기억해야 한다.

유니티 어플리케이션이 사용하는 메모리 종류


유니티에서 사용하는 메모리는 3가지 종류의 영역이 있다.

첫번째는 코드 영역으로 유니티 엔진과 라이브러리 그리고 당신이 만든 게임코드가 컴파일 되어서 디바이스의 메모리 코드 영역에 로딩되게 된다. 사실상 이 부분을 최적화 할 필요성은 크지 않다. 실제로 유니티 프로젝트에서 코드 텍스트 파일들이 차지하는 용량이 얼마나 되는지만 계산 해 봐도 알 수 있다. 유니티 코드가 얼마나 클지는 모르지만 예상컨데 큰 사이즈의 그림 파일 몇장에도 못 미치는 용량일것이다.

다음은 Managed Heap영역인데, 이 부분은 Mono가 관리하고 있는 영역이다. 아시다시피 Mono란 .Net Framework의 오픈 소스 버전이다. 이 Managed Heap영역에 클래스가 instantiated object, variable 들이 거주하게 되는 영역이다. "Managed"인 이유는 Mono Framework이 이 영역의 메모리를 할당하거나 해제하면서 "관리"를 하고 있기 때문이다. 실제로 어플리케이션이 실행중에 이따금씩 Mono가 garbage collection 작업을 통해 할당 되었으나 더이상 참조가 존재하지 않는 메모리 영역을 해제한다. 당신의 코드에서 더이상 사용되지 않는 변수나 클래스 오브젝트를 실수든 부주의에 의해서든 참조를 유지하고 있다면 해당 메모리 영역은 절대 해제되지 않고 메모리를 차지하고 있게 된다.

마지막으로 Native Heap 영역인데 이 부분은 Unity 엔진이 OS에서 메모리를 할당 받아 texture, sound effect, level data 등을 저장하고 있는 영역이다. 유니티 엔진이 이 영역을 담당하여 Mono가 메모리를 관리하는 것 처럼 현재 scene에서 필요 없게 된 리소스가 차지하는 메모리 영역을 해제하는 작업을 수행하기도 하나 manual하게 할당된 리소스에 대해서는 유니티가 관리를 하지 못하므로 부주의 하다면 심각한 메모리 부족 사태에 이를 수 있다.

어플리케이션 코드 영역 메모리 최적화


어플리케이션 코드 영역을 최적화하는 작업은 비교적 쉽게 프로젝트 세팅을 바꾸어 주는 것 만으로도 해결할 수 있다. 그러나 이 최적화 옵션을 바꿈으로 인해 사용가능하지 않을 feature를 당신의 어플리케이션이 사용하지 않을 것 인지 확인할 필요가 있다.

사실 Mono Framework는 수많은 .Net 라이브러리를 포함하고 있는데 이 모든 라이브러리를 로딩하는데 꽤 많은 메모리 리소스를 소모하게 된다. 유니티에서는 이 overhead를 줄일 수 있는 옵션을 제공하는데 유니티 메뉴중 Edit > Project Settings > Player 메뉴를 선택하여 보자.


위와 같은 옵션을 볼 수 있을 것인데 중요한것은 아래쪽 Optimization 부분의 옵션들이다. "Api Compatibility Level"옵션을 ".NET 2.0 Subset"으로 바꾸어 유니티에게 더 적은 수의 .Net 라이브러리를 사용하도록 알릴 수 있다. 그리고 그 아래 "Stripping Level"의 옵션을 바꾸어 더 적은 용량의 빌드된 코드를 사용하도록 할 수 있다.  (Stripping Level 옵션의 자세한 내용에 대해서는 링크를 참조하도록 하자.) 해당 옵션의 수정을 통해 빌드 사이즈를 줄일 수 있으며 줄어든 빌드 사이즈는 곧 더 적은 메모리 사용을 의미한다. Stripping Level옵션은 Unity Pro에서만 제공되는 기능이다.

그럼 그냥 가장 minimal한 것으로 설정을 하면 되겠지 싶은데 세상일이 그렇게 간단하지 않다. 그 전에 당신의 어플리케이션이 옵션 조정으로 인해 빠지게 되는 라이브러리를 사용하지 않는지 확인할 필요가 있다. 가장 많이 쓰지만 이외로 minimize되서 빠지게 되는 라이브러리 중에는 System.Xml이 있다. 이 경우는 3rd party minimal 라이브러리를 사용하면 된다.

옵션에 따라 지원되는 라이브러리를 확인할 수 있는 페이지는 이곳을 참조하도록 하자. 실제로 빠지게 되는 많은 라이브러리들이 게임을 개발할 때는 잘 사용되지 않는 것들이 많다. 따라서 한번 시도해 볼만한 가치가 충분히 있다고 본다.

Managed Heap 영역의 메모리 최적화


유니티에서 최근에 Managed Heap 영역의 메모리를 관리하는 법에 대해 훌륭한 메뉴얼을 추가하였다. Managed Heap영역은 당신이 만든 코드에서 new 키워드나 Instantiate() 함수를 통해 할당한 메모리가 Mono Framework에 의해 관리되는 영역이다. 만약 메모리가 필요해 할당을 하는데 Manges Heap영역에 메모리가 모자랄 경우 heap 사이즈를 키우게 된다. 이 경우가 실제로 디바이스 상에서 메모리가 부족해지는 경우이다.

일반적으로는 어떤 인스턴스가 더이상 필요해지지 않을때 해당 인스턴스를 참조하는 곳이 없어지게 된다. 주기적으로 Mono의 Garbage Collector가 이러한 참조가 없어진 인스턴스 등을 찾아 Manged Heap상에서 해제하게 된다. 그러나 Garbage Collector가 메모리를 수거하는 작업을 하는 동안에는 framerate가 떨어지거나 어플리케이션의 반응이 늦어질 수가 있기 때문에 일반적으로 어플리케이션이 활발히 동작하고 있는 동안에 Garbage Collector의 동작을 원치는 않을 것이다. 따라서 메모리가 새는 것을 방지하기 위해 할당한 변수나 오브젝트가 필요가 없어지게 되면 null을 할당하거나 Destory로 명시적으로 메모리상에서 제거되도록 표시를 해두어야 하지만 동시에 수많은 메모리 해제로 과도한 Garbage Collector의 동작을 바라지도 않을 것이다.

특히 게임의 경우 Destory를 호출해야만 하는 수많은 object가 게임 플레이 도중에 생성될 수 있다. 예를 들면 대포가 수많은 대포알을 발사하는 경우가 그렇다. 각 대포알을 발포 되기전에 메모리를 할당 받고 대포알이 사라지는 순간 해제해야 하는데 이러한 작업이 각 대포알에게 일어나야 하는 것은 너무나도 낭비적인 일이 아닐 수 없다. 이 문제를 해결하는 일반적인 방법은 해당 오브젝트를 생성후 필요 없게 되는 때에 Destory하지 않고 일단 숨긴 후 필요할 때 다시 Activate시켜서 사용하는 이른바 Object Recycling 기법이 있다. 항상 이 방법이 유용한것은 아니지만 과도한 Garbage Collection을 피하는 훌륭한 방법이다. 

Automatic Memory Management와 Object Pool에 관련해서는 이곳의 유니티 문서를 통해 자세한 사항을 확인하도록 하자.

 Garbage Collection이 일어나는 것을 막거나 컨트롤 할 수 있는 방법은 딱히 없지만 시스템에 적당한 시점에 지금이 바로 Garbage Collection을 하기에 좋은 타이밍이라는 것을 힌트를 줄 수는 있다. System.GC.Collect()를 호출하는 방법이 그것인데 이 함수를 호출하는 것이 바로 Garbage Collection을 하도록 하지는 않고 정말 단지 지금이 좋은 타이밍이라는 것을 알려줄 수 있을 뿐이지만 적당한 시점에 예를 들어 화면이 전환되는 시점이나 리소스 로딩이 일어나는 시점에 호출해준다면 큰 효과를 볼 수 있다. 

Native Heap 영역의 메모리 최적화


Unity에서 Scene을 로드하면 그 Scene 에 포함된 모든 Asset들을 같이 메모리 상에 로드되고 이어서 해당 Scene이 유저에게 보여진다. 그리고 해당 Scene이 끝나서 다음 Scene으로 넘어갈 경우 이전 Scene의 리소스중 다음 Scene에서도 사용되지 않는 다면 Asset은 메모리상에서 자동으로 해제된다.

그러나 어떤 두가지의 경우에 Asset이 자동으로 해제되지 않는 경우가 있는데, 첫번째로는 DontDestroyOnLoad(Object target) 함수를 통해 개발자에 의해 다른 Scene이 Loading 되더라도 Destroy되지 않도록 지정해둔 경우로 이 경우 해당 GameObject와 GameObject의 Child GameObject, 연계된 Asset 모두가 Scene 전환에 따라 자동 해제되지 않는다. 실제로 이 함수는 모든 Scene간에 보존되어야 하는 데이터를 전달하는 방법으로 사용되고 있는데 이 경우에 보존되는 Object가 무거운 Asset을 참조하거나 그 자체가 아니도록 주의하여야 한다.

또 다른 경우는 Script내에서 Scene Object를 참조하고 있는 경우이다. 대부분 Script의 참조는 Scene이 전환됨에 따라 같이 파괴되게 되지만 Scene이 전환되더라도 남아 있는 GameObject가 있고 해당 GameObject 가 Script를 가지고 있는데 이 Script내에서 다른 Asset 예를 들어 사운드 이펙트나 텍스쳐에 대한 참조를 유지하고 있는 경우 무심코 필요하지 않은 리소스가 메모리상에 남아서 메모리 자원부족에 이르게 할 위험이 있다. 또 Static 변수 혹은 Singleton인스턴스가 자원에 대한 참조를 가지고 있는 경우도 마찬가지 문제가 발생할 소지가 있다.

따라서 스크립트내에서 이러한 참조를 유지하고 있다면 필요없게 되는 시점에 잊지않고 Destroy함수를 호출하거나 null을 할당하여 참조를 해제할 필요가 있다. 

유니티는 Scene이 Load되는 시점에 해당 Scene의 모든 Asset을 자동으로 같이 Load하지만 이러한 Asset을 해제할 유일한 방법은 Scene을 다시 로드하거나 다른 Scene으로 전환하는 방법밖에 없다는 것에 주의할 필요가 있다.

리소스 로딩을 수동으로 관리하기


약간의 노동이 필요하지만 수동으로 Asset을 현재 Scene에 Load할 방법도 당연히 존재한다. Resources 라는 폴더를 만들어서 Asset들을 그 안에 위치시키면 해당 Asset들은 Resources.Load(resourcePath)함수를 통해 수동으로 Load할 수 있게 된다.  이 Asset들은 텍스쳐, 오디오파일, 프리팹, 매터리얼등이다. 

해당 자원에 대한 이용이 끝났다면 그 자원에 할당된 메모리를 Resources.UnloadUnusedAssets() 함수를 호출하여 강제로 해제할 수 있는데, 유니티가 해당 자원을 해제하기 위해서는 스크립트에서 해당 자원에 대한 참조가 모두 없어져야 한다. (Destroy혹은 null을 할당해서) 그리고 이 함수는 Resources.Load() 함수를 통해서 할당된 자원만 해제를 하고 유니티가 Scene을 Load하면서 자동으로 같이 Load한 자원은 해당되지 않는다는 점에 주의할 필요가 있다.


이 포스트는 이 링크의 내용을 의역한 내용이란 것을 밝힙니다.



September 24, 2013

[C#] Nullable Type - int?, char?, bool?

C# Programming Guide - Nullable type

C#에서는 Nullable Type을 선언할 수 있는데 예를 들어 다음과 같이 ?를 기본 자료형에 붙여쓰면 해당 변수는 Nullable Type이 된다. 

int? compound;
char? sigleCharacter;

이 자료형은 기본 자료형의 표현 범위에 null 값을 추가한 형태로 예를 들어 int? 의 경우 int 값외에 null이 할당 가능하다.


주로 해당 타입을 선언 후 값을 바로 할당 할 수 없는 경우에 null을 할당하여 해당 변수가 초기화 되어 있지 않음을 구별하여 쓸 수 있다.


Nullable Type은 두가지 read-only property를 가지는데 


  • HasValue (bool) : true인경우 값이 할당 되었음을 의미하고, false의 경우 값이 할당 되지 않은 상태 (null)임을 의미한다.
  • Value : 실제 할당 된 값을 의미하며, 값을 할당하지 않고 이 속성을 참조하려 하면 InvalidOperation 예외가 발생한다.
int? x = 10;
if (x.HasValue)
{
    System.Console.WriteLine(x.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}


기본 연산자나 비교 구문에서 Nullable Type을 쓸 경우 예상과는 다르게 동작할 수 있으므로 주의를 해서 써야 한다. 자세한 내용은 위의 링크를 참조하자.

PyCharm 3 - Free Python IDE



구글에서 배포하는 IntelliJ 기반의 무료 안드로이드 앱개발 IDE AndroidStudio를 만족스럽게 쓰고 있었는데 같은 회사에서 Python IDE도 무료로 공개하고 있다는 소식을 접했다.

게다가 jetBrains에서 cross-platform C++ IDE도 개발중이라는 소식에 많은 사람들이 기다리고 있는것 같다.

PHP, Ruby를 위한 IDE도 있지만 해당 IDE들은 Community Free Edition을 공개할 예정은 없다고 한다. (Hacker News에 이 회사 CTO가 직접 커멘트를 달았다.)

September 20, 2013

트렌디한 웹 개발을 위해 알아야 할 모든 것들



이최근 웹개발 분야를 이끌어 가고 있는 모든 핫 토픽들이 망라되어 있고 그에 대한 내용을 배울 수 있는 링크를 제공한다.

개발 언어, 플랫폼, 서비스 다양한 분야에 대한 내용이 올라와있다. 이중에 나는 얼마나 알고 있고 얼마나 활용하고 있는지.. 정말 많기도 하다...



인터렉티브 자바스크립트 스터디 사이트



Learn JS라는 사이트인데 이곳에서는 자바스크립트에 대해 공부를 하면서 사이트 아래쪽에 콘솔 창을 통해 자바스크립트를 직접 입력하고 실행한 결과를 볼 수 도 있다.

보는 것보다 한번 직접 해보는 것이 훨씬 더 기억에도 오래 남는법. 배우며 바로 배운 것을 응용해서 써먹어 보자.

September 18, 2013

Cyanogen $7M 투자를 유치

Cyanogen raises $7 million to build a better version of Android

 이 기사를 보기전까지는 Cyanogen이라는 조직이 회사의 형태로 존재한다는 사실도 몰랐었는데 정말 놀라운 일이다. Cyanogen은 Android의 오픈된 소스를 이용하여 커스텀 CyanogenMod라는 커스텀 롬을 만들어 배포하던 조직이었고 많은 사람들이 자신의 Android 스마트폰을 루팅하여 CyanogenMod의 커스텀롬을 펌웨어 업데이트로 스마트폰 제조사에서 개발된 롬을 대체해 사용해 오고 있었다.


 이 기사에 따르면 Cyanogen 에서 파악하기로는 이미 윈도우즈 폰을 사용하는 사람들 보다 더 많은 사람들이 자신들의 커스텀롬 OS를 사용하고 있는 것으로 파악하고 있다고 한다.

CyanogenMod의 경우 기본적으로 공개된 안드로이드 소스(AOSP) 를 기반으로 제작되고 있으니 기존 안드로이드 커뮤니티의 혜택을 그대로 받을 수 있는 것이 장점이고 그 것에 기반에 유저 편의를 위한 많은 기능을 추가해서 고급 사용자들의 호응을 받고 있다.

재미있는 사실은 한사람의 취미로 시작한 프로젝트가 8Million 유저가 사용하고 있는 거대한 프로젝트로 발전했다는 사실이다.

CEO Kirt McMaster는 앞으로 CynogenMod 펌웨어 업데이트를 쉽게 할 수 있는 1-click Windows 소프트웨어와 스마트폰 제조 하드웨어 업체와의 파트너쉽에 힘쓸 예정이라고 한다.
큰 금액의 투자도 받았으니 앞으로 어떤 방향으로 발전을 해갈지 귀추가 주목된다.





Trello - 팀 협력 프로젝트 관리 툴

Trello라는 태스크 관리 툴 및 팀 협력 도구를 소개하고자 한다.

아래 스크린샷에서 볼 수 있듯이 각 프로젝트마다 작업 현황을 볼 수 있는 Blackboard같은 것이 제공이 되고 각 카테고리 별로 Task를 추가/삭제/이동 할 수 있도록 되어있다.

무엇보다 여러 사람의 협력으로 프로젝트가 진행되는 경우 프로젝트 참여자 모두가 같은 현황판을 공유하게 되어 현재 프로젝트에 어떤일이 벌어지고 있는지 파악하는 것이 용이하다.



그리고 각 태스크 별로 Due date, 라벨 설정, 태스크 할당 등이 가능하고 아래 스샷에서 볼 수 있듯이 커멘트, 첨부파일도 추가할 수 있다. 어떤 태스크를 진행해가면서 코멘트, 결과물 등을 태스크를 통해 전달 받을 수 있도록 되어있다.



개인적으로 진행하고 있는 프로젝트와 회사에서 진행하고 있는 프로젝트 모두에서 Trello를 사용중인데 현재 상당히 만족하고 있고 더 좋은 점은 Free라는 점이다.

이런 서비스를 공짜로 제공하면서 어떻게 돈을 버나 했는데 최근에 Trello Gold 라는 것을 발표했는데 유료로 제공되는 기능은 스티커를 붙이거나 바탕화면을 변경할 수 있는 기능 그리고 첨부 파일 크기가 250mb정도로 확장된 점 등이다. 월 $5정도로 Gold 서비스를 결제할 수 있는데 그다지 유료 기능이 나에게 쓸모가 있어 보이진 않는다. 그래도 이 서비스를 이용하면서 얻는 효용에 비해 $5은 굉장히 싼 비용이 아닌가 한다.








September 17, 2013

Apple 앱스토어에서 이전 버전의 앱 다운로드를 허용

애플이 오래된 하드웨어 디바이스들의 수명을 연장해주기로 결정했나보다. (링크기사)

핵심은 오래된 버전의 OS를 가진 디바이스에서 그 OS를 지원하는 이전 버전의 앱을 앱스토어에서 다운로드해 줄 수 있도록 앱스토어가 업데이트 되었다고 한다.

오래된 디바이스를 가진 유저들의 입장에서야 반가운 일이지만 이것 또한 정말 애플스럽지 않은 결정중의 하나로 보인다.

이전 버전의 앱이 오래된 디바이스에서 동작하기는 하지만 나쁜 사용자 경험을 가지고 있다면 오히려 앱 개발자의 입장에서는 골치아픈 이슈가 되지 않을까? 긁어서 부스럼 만든 느낌?

새로 구입한 디바이스에서 나쁜 경험을 주었던 앱에 대한 선입관을 가지고 외면해버릴 모르는 문제가 발생? 혹은 앱스토어에 제출된 앱의 모든 버전은 항시 최고의 경험을 추구하기 위함으로써 존재해야 하는것인가? 서버 연결이 필요하다면 평생토록 서버와의 연결 지원?

득이 될지 실이 될지 모르겠다.

September 15, 2013

RoboVM JAVA로 iOS 앱 개발하기.

 아직은 v0.0.4정도의 초기 버전에 불과하지만 Java코드로 iOS 앱을 만들 수 있는 툴이 개발되고 있다.




RoboVM이라는 이름에서 추측될법한 iOS상에서 돌아가는 Java Virtual Machine이 아니라 RoboVM compiler가 Java byte code를 타겟 디바이스의 native x86 혹은 ARM 코드로 컴파일이 되어 실행되는 방식인듯 하다.

따라서 중간에 Layer를 두는 방식이 아니라 Performance측면에서 장점을 가지고 있을듯 하다.

또 하나의 장점은 Ubuntu Linux 머신에서도 iOS 앱을 개발할 수 있는것 같은데 간단한 iOS앱을 개발하기 위해 Mac Machine을 장만해야 하는 비용도 조금은 아낄 수 있을듯..

v0.0.4버전에서 기본적인 iOS7 기능을 지원한다고 되어 있는데 홈페이지 Document도 아직은 부실한 편이고 Virtual Machine 없이 메모리 관리는 어떻게 되고 있는 것인지 상세한 설명도 안보이는것 같고..

실제로 이 Product로 Production App을 만들기에는 아직 갈길이 멀어 보인다.


덧. C#으로 iOS앱을 개발할 수 있는 Xamarin 이라는 프로젝트도 있는데 이미 어느정도 성숙한 프로젝트인지 Monetization 모델도 있고 전용 IDE 및 Component Maketplace도 존재한다.

덧2. RoboVM은 Garbage Collector Bohem GC라는 것을 구현에 포함하였다고 한다.

September 14, 2013

Blogger에 Google Analytics 연동시키기.

당연하지만 가장 먼저 Google Analytics 서비스에 계정을 만들어야 한다.

그리고 나서 Blogger서비스에 적용하는 방법은 여기 저기 찾아 본 바로는 아래 동영상을 참고하도록 하자.


September 13, 2013

[Unity] 유니티3D 코루틴의 매커니즘

유니티는 기본적으로 싱글 스레드로 동작한다. 따라서 긴시간이 걸리는 작업을 (Especially I/O Operation) 로직내에서 수행할 경우 프레임이 떨어지는 문제를 겪게 된다.

만약 긴 시간이 걸리는 작업을 멀티스레드를 사용해서 골치아프게 동기화등을 고려하지 않고, 여러 프레임이 지나가는 동안 나누어서 처리할 수 있다면 깔끔하게 문제를 해결할 수 있지 않을까?

이 상황을 해결하는 솔루션으로 유니티는 코루틴(coroutine)이라는 것을 제공한다.

사실 유니티 코루틴은 긴 시간이 걸리는 작업을 분할하여 처리하는데에도 쓰이지만 잠깐 로직이 일정 시간 멈출 필요가 있을때에도 사용된다.

아래는 유니티 C# 스크립트로 만든 코루틴의 예제이다.
IEnumerator HeavyLogic()
{
    while(someCondition)
    {
        /* 일정 부분의 작업 수행 */
 
        // 이 로직은 여기서 멈추고 다음 프레임을 진행한다.
        yield return null;

        /* 다음 프레임 이후에 처리될 로직 */
    }
}

그렇다면 이 코루틴이라는 것은 어떻게 돌아가는 것인가? 사실 내가 글을 쓰는 목적인 코루틴의 활용 보다는 그것이 어떻게 유니티내에서 돌아가는 것인가 하는 매커니즘 파악에 촛점이 맞추어져 있다.

이것을 이해하는 키는 해당 코루틴 로직이 IEnumerator형을 리턴하는 함수라는 것과 yield라는 키워드를 사용하는 점에서 유추해볼 수 있다.

IEnumerator 타입은 일종의 시퀀스에 대한 커서처럼 작용한다. IEnumberator 는 Current라는 멤버변수와 MoveNext()라는 멤버함수를 가진다.

Current는 현재 시퀀스의 커서에 대한 요소를 가진 속성이고, MoveNext() 는 현재 시퀀스에서 다음 시퀀스로 진행하도록 하는 함수이다.

IEnumerator는 단지 Interface이므로 이런 멤버들이 어떻게 Implementation되었는지에 대해서 정의하지는 않는다.

MoveNext() 함수의 구현은 Current에 1을 더하는 로직이 될 수도 있고 인터넷 상에서 이미지를 다운 받아 그것의 해쉬값을 Current 변수에 저장하는 로직이 될 수도 있는 것이다. 또한 이 함수는 처음에는 로직 시퀀스상의 어떤 연산을 하다가 그 다음 호출시에는 처음과는 완전히 다른 로직을 수행할 수 도 있다.

MoveNext()가 호출이 되면 해당하는 로직을 수행후 결과는 Current멤버 변수에 저장이 된다. 그리고 더이상 진행할 것이 없다면 false를 리턴한다.

자 따라서 원래대로라면 IEnumerator를 상속받은 Class를 구현을 해야 한다. 골치가 살살 아파 올것이다 그러나 상심은 아직 금물 C#에서는 몇가지 Rule만 따른다면 컴파일러가 자동으로 IEnumerator 구현체를 컴파일 타임에 자동으로 생성하여 준다.

이렇게 Rule을 따라 정의한 함수를 C#에서는 Iterator block이라고 부르는 것 같다.

그렇다면 Iterator block이란 무엇인가?

Iterator block이란 일반 함수와 다르지 않지만 a) IEnumuerator를 리턴할 것, b) yield 키워드를 사용할 것 이 두가지를 갖추면 Iterator block이 된다. (위의 예제 코드 참고)

그렇다면 yield 키워드는 과연 무엇을 하는 것인가.

yield 키워드는 로직 시퀀스에서 다음에 나올 값이 무엇인지 혹은 값이 더이상 없는지 를 가르쳐주는 키워드이다. 따라서 코드 진행중 yield return X 나 yield break를 마주친 지점이 IEnumerator.MoveNext() 함수가 중단되는 지점이다.

yield return X 는 MoveNext()는 true를 리턴하고 Current 는 X로 할당되도록 한다. 반면에 yield break는 MoveNext()가 false를 반환하도록 한다.

자 여기서 발생할 수 잇는 트릭은 시퀀스가 어떤 값을 실제로 반환하는지는 상관이 없다는 것이다. MoveNext()를 반복해서 호출하고 Current값을 무시할 수 있다. 그러면 계산은 계속해서 진행될 것이며 매번 MoveNext()가 호출 될 때 마다 iterator block은 실제로 어떤 식을 yield 하느냐에 상관 없이 다음 yield 키워드까지 진행되게 된다.

그래서 다음과 같은 Iterator block도 생각해볼 수 있다.

IEnumerator TellMeASecret()
{
   PlayAnimation("LeanInConspiratorially");
   while(playingAnimation)
     yield return null;
 
   Say("I stole the cookie from the cookie jar!");
   while(speaking)
     yield return null;
 
   PlayAnimation("LeanOutRelieved");
   while(playingAnimation)
     yield return null;
}

이 Iterator block은 긴 연속적인 null 값을 생성하게 된다. 그러나 여기서 중요한 것은 이 로직을 실행하기 위해 발생한 사이드 이펙트이다.

아마도 아래와 같은 간단한 루프 내에서 위의 iterator block의 코루틴을 실행할 수 있다.

IEnumerator e = TellMeASecret();
while(e.MoveNext()) { }

좀 더 실용성있는 예문은 다음과 같이 쓸 수 있을것 같다.

IEnumerator e = TellMeASecret();
while(e.MoveNext()) 
{ 
  // 'Escape'키를 누를 경우 컷씬을 건너뛴다.
  if(Input.GetKeyDown(KeyCode.Escape)) { break; }
}

마지막의 예문으로 위의 코루틴이 어떻게 실행될지를 예측해보면,

먼저 PlayAnimation("LeanInConspiratorially"); 함수내에서 애니메이션이 구동되고 playingAnimation 변수는 true로 세팅이 된다.
while(playingAnimation) 에 의해 playingAnimation이 true 인 동안은 while 루프를 반복하게 되는데 루프안에 있는 첫번째 명령은 yield return null; 이다 따라서 여기서 MoveNext의 진행은 멈추고 return 값으로 null을 Current에 할당한다.

원래 유니티에서 Coroutine의 yield값이 null인경우 이번 프레임을 쭉 실행하고 다음 프레임에 다시 해당 코루틴을 중단 지점에서 다시 진행하도록 되어 있으나 이 예제는 Current 멤버 변수를 무시한다고 가정하고 있으므로 그냥 while 루프를 실행하는 MoveNext가 yield에서 잠시 멈추고 애니메이션을 진행하는 작업을 조금 하다가 다음번에 다시 while문으로 돌아 오게 되어 playingAnimation이 값을 다시 평가하고 같은 작업을 반복하게 된다.

중요한것은 MoveNext함수가 yield 키워드를 만나면 그 곳에서 함수의 진행을 멈추고 다른 작업에 제어권을 넘겨주는 점이다.

자 이제 yield로 넘겨주는 값이 Current 멤버 변수에 할당이 되는 점을 고려하여 유니티에서 해당 사항을 어떻게 처리하는지 알아 보자.

위의 예제에서 null을 yield하는 경우는 유니티에서 한프레임을 넘기게 된다. 그러나 만약 애니메이션을 보여주는 데 일정 시간이 필요하다거나 아니면 일정 시간이 지난 후에 무엇인가를 해야 한다거나 하면 위와 같이 yield return null;을 통해 프레임을 넘기고 매번 어떤 상황을 매 프레임 체크하는 것은 꽤나 비효율적이지 않은가?

그래서 유니티는 yield를 통해 Current에 할당된 값에 따른 몇가지 다른 종류의 기다림 로직을 YieldInstruction 타입으로 정의 해 두었다.

WaitForSeconds값을 만났을 경우는 Coroutine을 일정 시간이 지난 후 다시 재개 되도록 하고, WaitForEndOfFrame의 경우는 코루틴을 프레임이 렌더링 된 이후에 재개 되도록 하였다. 그리고 코루틴 자체를 Current 값으로도 받을 수 있는데 예를 들어 Coroutine A가 Couroutine B를 yield 하도록 한 경우는 Coroutine B가 끝나야 Coroutine B가 재개된다.

이러한 유니티의 로직을 코드로 표현해보면,

List<IEnumerator> unblockedCoroutines;
List<IEnumerator> shouldRunNextFrame;
List<IEnumerator> shouldRunAtEndOfFrame;
SortedList<float, IEnumerator> shouldRunAfterTimes;
 
foreach(IEnumerator coroutine in unblockedCoroutines)
{
    if(!coroutine.MoveNext())
        // This coroutine has finished
        continue;
 
    if(!coroutine.Current is YieldInstruction)
    {
        // This coroutine yielded null, or some other value we don't understand; run it next frame.
        shouldRunNextFrame.Add(coroutine);
        continue;
    }
 
    if(coroutine.Current is WaitForSeconds)
    {
        WaitForSeconds wait = (WaitForSeconds)coroutine.Current;
        shouldRunAfterTimes.Add(Time.time + wait.duration, coroutine);
    }
    else if(coroutine.Current is WaitForEndOfFrame)
    {
        shouldRunAtEndOfFrame.Add(coroutine);
    }
    else /* similar stuff for other YieldInstruction subtypes */
}
 
unblockedCoroutines = shouldRunNextFrame;

더 많은 YieldInstruction 타입의 로직이 어떻게 추가로 구현 되어 각기 다른 상황을 처리 할 것인지는 어렵지 않을 것이다.

한가지 팁은 yield return은 하나의 표현일 뿐이다. 따라서 다음과 같은 응용법도 얼마든지 가능하다.

YieldInstruction y;
 
if(something)
 y = null;
else if(somethingElse)
 y = new WaitForEndOfFrame();
else
 y = new WaitForSeconds(1.0f);
 
yield return y;

위와 같이 something, somethingElse 에 따라 코루틴이 언제 재개 되는지를 다르게 정의할 수 있다.

또한 유니티의 코루틴은 일반적인 C#의 iterator block이므로 해당 코루틴을 iterate 하는 로직을 스스로 만들 수도 있다.

IEnumerator DoSomething()
{
  /* ... */
}
 
IEnumerator DoSomethingUnlessInterrupted()
{
  IEnumerator e = DoSomething();
  bool interrupted = false;
  while(!interrupted)
  {
    e.MoveNext();
    yield return e.Current;
    interrupted = HasBeenInterrupted();
  }
}

위의 예제는 코루틴 내에서 iterator block을 직접 iterate 하는 로직을 구현하고 있다.

이상의 내용들이 유니티에서 코루틴을 사용할 때 무슨 일들이 일어나는지 이해하는데 있어 도움이 될 수 있었으면 좋겠다.

참고: http://www.altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/






Node.Js Express에서 모든 웹경로 요청에 대해 로그를 남기기

이제 갓 Express에 첫발을 디뎌서 이렇게 간단한 내용도 찾아봐야 알 수 있어서 메모 해 둔다.

1) Middleware를 만들어서 사용하는 방법

var app = express.createServer();

// Your own super cool function
var logger = function(req, res, next) {
    console.log("GOT REQUEST !");
    next(); // Passing the request to the next handler in the stack.
}

app.configure(function(){
    app.use(logger); // Here you add your logger to the stack.
    app.use(app.router); // The Express routes handler.
});

app.get('/', function(req, res){
    res.send('Hello World');
});

app.listen(3000);

2) app.all() 함수를 사용하는 방법

  
...

app.all('/*', function(req, res){
    console.log('GOT REQUEST !');
});
...
  


이상의 두가지 방법이 있으나 2)의 방법은 해당 함수를 호출하는 위치에 따라 다른 GET, POST 리퀘스트가 처리되지 않을 수 도 있으니 항상 다른 리퀘스트보다 늦게 등록이 되어야 한다.

Blogger에 Syntax Highlighter 적용하기.

지금 쓰고 있는 Blogger 서비스는 뭔가 부족한것 같으면서도 Template을 직접 수정할 수 있다는 점이 강력한 점 같다.

앞으로 본문에 코드를 많이 쓰게 될 예정이므로 Syntax Highlighting 을 적용하는 방법을 정리해본다.


1) Blogger's Admin Panel에서 Template메뉴를 클릭

2) Template 메뉴에서 현재 사용중인 디자인의 HTML 편집 클릭

3) 사용중인 Template의 html 코드가 나오는데 여기가 중요한 부분이다. Template 코드중 <head>와 ... </head> 사이에 아래의 코드를 넣도록 하자






윗 코드에서 추가되는 Javascript의 이름을 확인하여 실제로 사용하지 않을 Language는 삭제해서 페이지 로딩 속도를 줄이도록 하자. 나 같은 경우는 AS3, ColdFusion, Delphi등등 쓰지 않을 언어에 대한 부분은 모조리 삭제하였다.

4) 이제 수정된 Template 을 저장하고 테스트 삼아 아래의 글을 작성해보도록 하자.
    /*
     * SyntaxHighlighter
     */
    function foo()
    {
        if (counter <= 10)
            return;
        // it works!
    }

5) 미리 보기를 통해서 보면 이 블로그 글과 같이 코드 부분이 따로 처리되어 보이는 것을 알 수 있다. 코드 Language에 따른 brush값은 링크에서 확인 할 수 있다.


이 Syntax Highlighter에 대한 추가적인 정보는 원작자 사이트 중 링크에서 확인해보도록 하자.

한가지 팁을 더하자면 Syntax Highlighter 색상 Theme을 변경하기 위해서는 위에서 Template에 추가한 코드중 아래 CSS 파일 링크를 원하는 Theme의 파일 이름으로 수정하면 되는데, 사용가능한 Theme에 대한 정보는 링크를 참조하도록 하자.

덧. 한가지 주의점이 있는데
SortedList shouldRunAfterTimes;


위와 같이 코드에 <나 >와 같은 꺽쇠기호가 들어 있으면 문제가 있다.
이 경우는 SyntaxHighlighter가 <나 >와 같은 꺽쇠기호를 제대로 인식하지 못하기 때문인데, 이 경우에 <는 "&lt;"로 >는 "&gt;"로 바꿔서 올리면 된다.

SortedList<float, IEnumerator> shouldRunAfterTimes;


September 11, 2013

익숙한 퇴근길

3년간 다녀온 익숙한 퇴근길.
내 나라가 아닌 곳 그 어딘가가 익숙해질 수 있다는 것 자체가 3년전의 나에게는 어색한 일이 었을텐데..
지금은 이 길이 너무나도 평범해 그지없는 일상의 일부분이 되었다.