[JavaScript] 클래스 정의 API ㅡ 1/3

자바스크립트는 C++이나 C#/Java 등에서와 같이 클래스(Class)를 직접적으로 정의할 수 있는 API를 제공해 주지 않습니다. 그러나 자바스크립트에서는 프로토타입(Prototype)을 이용하여 클래스의 특성을 제공할 수 있습니다. 그러나 이러한 프로토타입 기반의 클래스 정의에 대한 API는 개발자가 직접 만들어 사용해야 합니다. 게다가 비록 프로토타입을 이용해 클래스 개념을 자바스크립에서 사용할 수 있다고는 하여도 C++이나 C#/Java와 같은 완전한 클래스를 정의하지는 못합니다. 대신 개발자는 스스로 엄격한 약속을 통해 이러한 부족함으로 매꿔야만 합니다.
아래의 코드는 자바스크립트에서 클래스를 정의하기 위한 API의 한가지 예입니다. 자바스크립트를 이용해 클래스를 정의하는 API는 매우 다양하고 많습니다. 그중 제가 필요에 의해 아래의 제시한 클래스 정의 API는 데이비드 플래너건의 자바스크립트 완벽가이드라는 책을 많은 부분 참고했으며 불필요한 부분을 제거한 예입니다.

function Class(x) {
    var classname = x.name;
    var superclass = x.extend || Object;
    var constructor = x.construct || function() {};
    var methods = x.methods || {};
    var statics = x.statics || {};
    var requires = x.requires || [];
    var proto = new superclass();

    for(var p in proto) if(proto.hasOwnProperty(p)) delete proto[p];
 
    proto.constructor = constructor;
    proto.superclass = superclass;
    proto.classname = classname; 
    constructor.prototype = proto;
 
    for(var p in methods) proto[p] = methods[p];
    for(var p in statics) constructor[p] = statics[p];

    for(var i=0; i        var c = requires[i];
        for(var p in c.prototype) {
            if(typeof c.prototype[p] != "function" || 
                p == "constructor" || p == "superclass") continue;
   
            if(p in proto && typeof proto[p] == "function" && 
                proto[p].length == c.prototype[p].length) continue;
    
            throw new Error(classname + " class requires " + p + " method.");
        }
    } 

    return constructor;
}

위의 클래스 정의 API는 다음과 같이 클래스의 4가지 본연의 기능에 충실할 것을 전제로 하였습니다.

  • 클래스를 정의하는 방법 제공할 것
  • 클래스 상속을 지원할 것
  • 추상 클래스을 구현할때 해당 추상 클래스의 모든 매서드를 구현하고 있는지 검사할 것
  • 객체가 어떤 클래스의 타입인지 검사할 수 있도록 할 것

위의 전제 조건들에 대해서 실제 위의 클래스 정의 API를 대한 사용예를 통해 살펴보겠습니다. 위의 클래스 정의 API를 통해 다음과 같은 클래스 계층을 구성하고자 합니다.


사용자 삽입 이미지Shape는 draw라는 매서드를 갖는 추상 클래스입니다. 그리고 이 Shape를 구현하는 클래스로 Rectangle과 Circle 클래스가 있습니다. 여기에 PositionedRectangle라는 클래스는 Rectangle를 상속받습니다. 또한 Coordinate라는 클래스가 존재하며 이 클래스는 PositionedRectangle와 Circle 클래스 사용합니다.

.NET ListView 컨트롤의 가상모드(VirtualMode) 사용하기

.NET의 리스트뷰에는 가상모드를 지원합니다. 상당히 많은 수의 데이터를 리스트뷰의 항목으로 추가할때 속도면에서, 메모리 면에서 효율적으로 화면에 표시하기 위한 목적으로 사용할만한 매우 좋은 방법입니다.

사용자 삽입 이미지
위의 화면은 리스트뷰 컨트롤의 View 속성을 Details로 하여 그리드(Grid) 형태로 데이터를 표시하고 있습니다. 좀더 자세히 설명하면 SHP 파일에 대한 속성을 전체적으로 살펴볼 목적으로 만들어진 UI 입니다.

일반적으로 SHP 파일을 구성하는 레코드의 개수는 적게는 수십개에서 많게는 수천만개 정도됩니다. 개수가 매우 많을 경우 레코드를 한번에 리스트뷰 컨트롤에 올려 놓을 경우 UI이 얼어버리는(Freeze) 현상이 발생하고 스크롤시 매우 느린 경험을 하게 됩니다.

이럴때 가상모드를 활용해 언급한 문제를 말끔히 해결할 수 있습니다. 리스트뷰에서 가상모드를 사용하기 위해서 최소한 3가지 작업을 해줘야 합니다. 먼저 리스트뷰 컨트롤의 VirtualMode를 true로 지정하기와 다음으로 리스트뷰에서 표시할 항목의 정확한 개수를 지정하기 위한 리스트뷰 컨트롤의 VirtualListSize 값의 지정입니다. 사용자에게 제공해야할 항목의 개수가 백만개라면 이 VirtualListSize의 값을 백만으로 주면됩니다. 그리고 끝으로 실제 제공할 데이터를 필요할때 마다 그때 그때 제공해주는 RetrieveVirtualItem 이벤트 함수입니다.

private void lvAttributes_RetrieveVirtualItem(object sender, 
    RetrieveVirtualItemEventArgs e)
{
    Xr.StopMapDrawing();

    int FID = e.ItemIndex;
    XrMapLib.AttributeRow Row = SML.AttributeTable.GetRow(FID);

    if (Row != null && Row.Load())
    {
        e.Item = new ListViewItem();
        e.Item.Text = FID.ToString();
        int ToIndex = (lvAttributes.Columns.Count - 1);

        for (int iField = 0; iField < ToIndex; iField++)
        {
            String FieldValue = Row.GetValueAsString(iField);
            e.Item.SubItems.Add(FieldValue);
        }

        Row.Unload();
    }
}

위의 코드 중 중요한 부분은 e.Item에 제공할 리스트뷰의 항목을 생성해 할당한다는 것입니다. 이정도의 코드만으로도 가상모드를 충분히 실무에서 적용해 활용할 수 있지만 이에더해 캐쉬 기법을 제공함으로써 더욱 속도를 향상시킬 수 있는 방법을 제공합니다.

끝으로, 가상모드가 항상 모든 경우에서 좋은 것은 아닙니다. 필요할때마다 그때 그때 데이터를 가져오므로 전체 항목에 대한 정렬과 같은 기능을 제공할 수 없다는 점 역시 주의하시기 바랍니다.