고급 항목

ActionScript OOP 지원 이력

ActionScript 3.0은 이전 버전의 ActionScript를 기초로 제작되었으므로 ActionScript 객체 모델의 발전 과정을 이해하면 도움이 됩니다. ActionScript는 초기 버전의 Flash Professional을 위한 간단한 스크립트 메커니즘으로 처음 개발되었습니다. 이후 프로그래머들은 ActionScript로 점차 복잡한 응용 프로그램을 작성하기 시작했습니다. 이러한 프로그래머의 수요에 대응하기 위해 이후 릴리스에서는 복잡한 응용 프로그램을 만드는 데 필요한 언어 기능이 계속 추가되었습니다.

ActionScript 1.0

ActionScript 1.0은 Flash Player 6 및 이전 버전에서 사용된 언어 버전입니다. 이러한 초기 개발 단계에서도 ActionScript 객체 모델은 기초적인 데이터 유형으로서 객체라는 개념에 기반을 두었습니다. ActionScript 객체는 속성 그룹이 있는 복합 데이터 유형입니다. 객체 모델에서 속성 이라는 용어에는 객체에 연결된 변수, 함수 또는 메서드 등의 모든 항목이 포함됩니다.

이러한 1세대 ActionScript에서는 class 키워드를 통한 클래스 정의가 지원되지 않지만 프로토타입 객체라는 특수한 객체를 사용하여 클래스를 정의할 수 있습니다. Java 및 C++ 등의 클래스 기반 언어와 같이 class 키워드를 사용하여 구체적인 객체로 인스턴스화하는 추상적인 클래스 정의를 만드는 대신, ActionScript 1.0과 같은 프로토타입 기반 언어에서는 기존 객체를 모델(프로토타입)로 사용하여 다른 객체를 만듭니다. 클래스 기반 언어의 객체는 자신의 템플릿 역할을 하는 클래스를 가리킬 수 있지만, 프로토타입 기반 언어의 객체는 자신의 템플릿 역할을 하는 프로토타입이라는 다른 객체를 대신 가리킵니다.

ActionScript 1.0에서 클래스를 만들려면 해당 클래스의 생성자 함수를 정의해야 합니다. ActionScript에서는 함수가 추상적인 정의가 아닌 실제 객체입니다. 작성된 생성자 함수는 해당 클래스 인스턴스의 프로토타입 객체 역할을 합니다. 다음 코드에서는 Shape라는 클래스를 만들고 기본적으로 true 로 설정되는 visible 이라는 속성 하나를 정의합니다.

// base class 
function Shape() {} 
// Create a property named visible. 
Shape.prototype.visible = true;

이 생성자 함수는 다음과 같이 new 연산자로 인스턴스화할 수 있는 Shape 클래스를 정의합니다.

myShape = new Shape();

Shape() 생성자 함수는 Shape 클래스 인스턴스의 프로토타입 역할을 할 뿐만 아니라 Shape의 하위 클래스(Shape 클래스를 확장하는 다른 클래스)의 프로토타입 역할도 합니다.

Shape 클래스의 하위 클래스를 만들려면 두 단계를 거쳐야 합니다. 우선 다음과 같이 클래스의 생성자 함수를 정의하여 클래스를 만듭니다.

// child class 
function Circle(id, radius) 
{ 
this.id = id; 
this.radius = radius; 
}

다음으로 new 연산자를 사용하여 Shape 클래스가 Circle 클래스의 프로토타입임을 선언합니다. 기본적으로 작성되는 모든 클래스에는 Object 클래스가 프로토타입으로 사용됩니다. 즉, Circle.prototype 에는 현재 일반 객체(Object 클래스의 인스턴스)가 들어 있습니다. Circle의 프로토타입을 Object 대신 Shape로 지정하려면 다음 코드를 사용하여 Circle.prototype 에 일반 객체 대신 Shape 객체가 포함되도록 값을 변경합니다.

// Make Circle a subclass of Shape. 
Circle.prototype = new Shape();

Shape 클래스와 Circle 클래스는 이제 상속 관계로 연결되었으며 이러한 관계를 일반적으로 프로토타입 체인 이라고 합니다. 다음 그림은 프로토타입 체인 관계를 보여 줍니다.

모든 프로토타입 체인 끝에 있는 기본 클래스는 Object 클래스입니다. Object 클래스에는 Object.prototype 이라는 정적 속성이 들어 있으며 이 속성은 ActionScript 1.0에서 만드는 모든 객체의 기본 프로토타입 객체를 가리킵니다. 예제 프로토타입 체인에서 다음에 있는 객체는 Shape 객체입니다. 이는 Shape.prototype 속성을 명시적으로 설정하지 않았으므로 일반 객체(Object 클래스의 인스턴스)가 계속 들어 있기 때문입니다. 이 체인의 마지막 링크는 해당 프로토타입인 Shape 클래스에 연결되어 있는 Circle 클래스입니다. Circle.prototype 속성에는 Shape 객체가 들어 있습니다.

다음 예제와 같이 Circle 클래스의 인스턴스를 만들면 Circle 클래스의 프로토타입 체인이 인스턴스로 상속됩니다.

// Create an instance of the Circle class. 
myCircle = new Circle();

앞의 예제에서 Shape 클래스의 멤버로 visible 이라는 속성을 포함했습니다. 이 예제에서는 visible 속성이 myCircle 객체에 포함되어 있지 않고 Shape 객체의 멤버일 뿐이지만, 다음 코드 행을 실행하면 true 가 산출됩니다.

trace(myCircle.visible); // output: true

런타임은 프로토타입 체인을 확인하여 myCircle 객체에 visible 속성이 상속됨을 인식할 수 있습니다. 이 코드를 실행하면 런타임은 우선 myCircle 객체의 속성 중에서 이름이 visible 인 속성을 검색하지만 이러한 속성을 찾을 수 없습니다. 다음으로 Circle.prototype 객체에서 속성을 검색하지만 visible 이라는 속성을 여전히 찾을 수 없습니다. 런타임은 프로토타입 체인을 거슬러 올라가면서 Shape.prototype 객체에 정의된 visible 속성을 찾게 되고 이 속성의 값을 출력합니다.

이 설명서에는 이해를 돕기 위해 프로토타입 체인과 관련된 자세한 세부 내용이 상당 부분 생략되어 있습니다. 대신 ActionScript 3.0 객체 모델을 이해하는 데 필요한 수준의 정보만을 제공합니다.

ActionScript2.0

ActionScript 2.0에는 class , extends , public private 등의 키워드가 새로 도입되었으며, 이를 통해 Java 및 C++ 등의 클래스 기반 언어를 사용하는 작업자에게 익숙한 방식으로 클래스를 정의할 수 있습니다. 단, ActionScript 1.0과 ActionScript 2.0 사이에서 기본 상속 메커니즘은 변경되지 않았습니다. ActionScript 2.0에는 클래스를 정의하는 구문이 새로 추가되었을 뿐입니다. 프로토타입 체인은 두 가지 언어 버전에서 동일한 방식으로 작동합니다.

다음 인용 코드와 같이 ActionScript 2.0에 새로 도입된 구문을 통해 많은 프로그래머에게 친숙한 방식으로 클래스를 정의할 수 있습니다.

// base class 
class Shape 
{ 
var visible:Boolean = true; 
}

ActionScript 2.0에는 컴파일 타임 유형 확인에 사용되는 유형 약어도 도입되었습니다. 이를 통해 이전 예제의 visible 속성에 부울 값만 포함되도록 선언할 수 있습니다. 또한 새로운 extends 키워드를 통해 하위 클래스를 간편하게 만들 수 있습니다. 다음 예제에서는 ActionScript 1.0의 경우 두 단계가 필요한 작업을 extends 키워드를 사용하여 한 단계로 수행합니다.

// child class 
class Circle extends Shape 
{ 
    var id:Number; 
    var radius:Number; 
    function Circle(id, radius) 
    { 
        this.id = id; 
        this.radius = radius; 
        } 
}

생성자는 이제 클래스 정의의 일부로 선언되며 클래스 속성인 id radius 도 명시적으로 선언해야 합니다.

ActionScript 2.0에는 인터페이스를 정의하는 기능이 추가되었으며, 이를 통해 객체 간 통신을 위해 정식으로 정의된 프로토콜을 사용하여 객체 지향 프로그램을 개선할 수 있습니다.

ActionScript 3.0 클래스 객체

일반적으로 Java 및 C++와 관련된 객체 지향 프로그래밍 패러다임에서는 클래스를 사용하여 객체 유형을 정의합니다. 또한 이러한 패러다임을 채택한 프로그래밍 언어에서는 클래스를 사용하여 클래스가 정의하는 데이터 유형의 인스턴스를 만듭니다. ActionScript에서도 이러한 두 가지 용도로 클래스를 사용하지만, ActionScript는 프로토타입 기반 언어로 시작되었으므로 독특한 특성이 있습니다. ActionScript에서는 각 클래스 정의마다 비헤이비어와 상태를 공유할 수 있는 특수 클래스 객체를 만듭니다. 그러나 대부분의 ActionScript 프로그래머는 이 특징으로 인해 코딩에 실질적인 영향을 받지는 않습니다. ActionScript 3.0은 이러한 특수 클래스 객체를 사용하거나 이해하지 않고도 복잡한 객체 지향 ActionScript 응용 프로그램을 만들 수 있도록 설계되었습니다.

다음 그림에는 class A {} 문으로 정의된 A라는 간단한 클래스를 나타내는 클래스 객체의 구조가 표현되어 있습니다.

그림의 각 사각형은 객체를 나타냅니다. 그림의 각 객체에는 A라는 아래 첨자가 있으며 이 문자는 클래스 A에 속함을 나타냅니다. 클래스 객체(CA)에는 중요한 몇 가지 다른 객체를 가리키는 참조가 들어 있습니다. 인스턴스 traits 객체(TA)에는 클래스 정의 내에 정의된 인스턴스 속성이 저장됩니다. 클래스 traits 객체(TCA)는 클래스의 내부 유형을 나타내며 이 객체에는 클래스에 정의된 정적 속성이 저장됩니다. 여기에서 아래 첨자 C는 "클래스"를 나타냅니다. 프로토타입 객체(PA)는 항상 constructor 속성을 통해 자신이 원래 연결된 클래스 객체를 참조합니다.

traits 객체

ActionScript 3.0에 새로 도입된 traits 객체는 성능을 고려하여 구현되었습니다. 이전 버전의 ActionScript에서는 이름을 조회할 때 Flash Player에서 프로토타입 체인을 확인했으므로 시간이 오래 걸렸습니다. ActionScript 3.0에서는 상속된 속성이 수퍼 클래스에서 하위 클래스의 traits 객체로 복사되므로 이름을 훨씬 빠르고 효율적으로 조회할 수 있습니다.

traits 객체는 프로그래머 코드에서 직접 액세스할 수 없지만 성능 및 메모리 사용을 개선해 줍니다. traits 객체는 AVM2에 클래스의 레이아웃과 내용에 대한 자세한 정보를 제공합니다. AVM2에서는 이러한 정보를 바탕으로 시간이 걸리는 이름 조회를 수행하지 않고 직접 속성에 액세스하거나 메서드를 호출하는 기계어 명령을 생성할 수 있으므로 실행 시간이 크게 줄어듭니다.

traits 객체가 도입됨에 따라 객체가 점유하는 메모리가 이전 버전의 ActionScript보다 크게 줄어듭니다. 예를 들어 클래스가 동적으로 선언되지 않아 봉인된 경우에는 클래스의 인스턴스에 동적으로 추가된 속성을 위한 해시 테이블이 필요하지 않으므로, 인스턴스에는 traits 객체에 대한 포인터 및 클래스에 정의된 고정 속성을 위한 슬롯 몇 개만 있으면 됩니다. 따라서 ActionScript 2.0에서는 100바이트의 메모리가 필요한 객체가 ActionScript 3.0에서는 20바이트만 필요할 수도 있습니다.

참고: traits 객체는 내부적으로 구현되는 세부 사항이며, 이후 버전의 ActionScript에서는 변경되거나 없어질 수도 있습니다.

프로토타입 객체

모든 ActionScript 클래스 객체에는 클래스의 프로토타입 객체를 참조하는 prototype 이라는 속성이 있습니다. 프로토타입 객체는 ActionScript가 프로토타입 기반 언어로 시작되었기 때문에 남아 있는 항목입니다. 자세한 내용은 ActionScript OOP 지원 이력을 참조하십시오.

prototype 속성은 읽기 전용이므로 다른 객체를 가리키도록 수정할 수 없습니다. 이는 ActionScript 이전 버전의 클래스 prototype 속성과 다른 점입니다. 이전 버전에서는 다른 클래스를 가리키도록 프로토타입을 다시 지정할 수 있었습니다. prototype 속성은 읽기 전용이지만, 이 속성이 참조하는 프로토타입 객체는 그렇지 않습니다. 즉, 프로토타입 객체에 새 속성을 추가할 수 있습니다. 프로토타입 객체에 추가된 속성은 클래스의 모든 인스턴스 사이에 공유됩니다.

이전 버전의 ActionScript에서 유일한 상속 메커니즘인 프로토타입 체인은 ActionScript 3.0에서 보조적인 역할만 합니다. 기본 상속 메커니즘인 고정 속성 상속은 traits 객체에 의해 내부적으로 처리됩니다. 고정된 속성은 클래스 정의의 일부로 정의된 변수 또는 메서드입니다. 고정 속성 상속은 class , extends override 등의 키워드와 관련된 상속 메커니즘이므로 클래스 상속이라고도 합니다.

프로토타입 체인은 고정 속성 상속 대신 사용할 수 있는 더욱 동적인 상속 메커니즘을 제공합니다. 클래스를 정의할 때뿐만 아니라 런타임에도 클래스 객체의 prototype 속성을 통해 클래스의 프로토타입 객체에 속성을 추가할 수 있습니다. 그러나 컴파일러가 엄격 모드로 설정되어 있으면 클래스를 dynamic 키워드로 선언하지 않은 경우에는 프로토타입 객체에 추가된 속성에 액세스하지 못할 수도 있습니다.

Object 클래스는 프로토타입 객체에 연결된 여러 속성을 가진 클래스의 전형입니다. Object 클래스의 toString() valueOf() 메서드는 실제로는 Object 클래스의 프로토타입 객체의 속성에 지정된 함수입니다. 다음 예제에서는 이러한 메서드가 어떻게 선언될 수 있는지를 이론적 관점에서 보여 줍니다. 그러나 실제 구현은 세부 구현 사항에 따라 다릅니다.

public dynamic class Object 
{ 
    prototype.toString = function() 
    { 
        // statements 
    }; 
    prototype.valueOf = function() 
    { 
        // statements 
    }; 
}

앞에서 언급했듯이 클래스 정의 외부에 있는 클래스의 프로토타입 객체에 속성을 연결할 수 있습니다. 예를 들어 toString() 메서드를 다음과 같이 Object 클래스 정의 외부에 정의할 수도 있습니다.

Object.prototype.toString = function() 
{ 
    // statements 
};

그러나 고정 속성 상속과는 달리 프로토타입 상속에서는 하위 클래스에서 메서드를 재정의하는 경우 override 키워드가 필요하지 않습니다. 예를 들어 Object 클래스의 하위 클래스에서 valueOf() 메서드를 재정의하려는 경우 세 가지 옵션 중 하나를 선택할 수 있습니다. 첫 번째 옵션은 클래스 정의 내에 있는 하위 클래스의 프로토타입 객체에 valueOf() 메서드를 정의하는 것입니다. 다음 코드에서는 Object의 하위 클래스 Foo를 만들고 클래스 정의의 일부로서 Foo의 프로토타입 객체에 valueOf() 메서드를 재정의합니다. 모든 클래스는 Object에서 상속되므로 extends 키워드는 사용하지 않아도 됩니다.

dynamic class Foo 
{ 
    prototype.valueOf = function() 
    { 
        return "Instance of Foo"; 
    }; 
}

두 번째 옵션은 다음 코드에 표시된 대로 클래스 정의 외부에 있는 Foo의 프로토타입 객체에 valueOf() 메서드를 정의하는 것입니다.

Foo.prototype.valueOf = function() 
{ 
    return "Instance of Foo"; 
};

세 번째 옵션은 valueOf() 라는 이름의 고정 속성을 Foo 클래스의 일부로서 정의하는 것입니다. 이 기법은 고정 속성 상속과 프로토타입 상속을 혼합한다는 점에서 다른 두 옵션과 차별화됩니다. Foo의 하위 클래스에서 valueOf() 메서드를 재정의할 때는 항상 override 키워드를 사용해야 합니다. 다음 코드에서는 Foo에 고정 속성으로 정의된 valueOf() 메서드를 보여 줍니다.

class Foo 
{ 
    function valueOf():String 
    { 
        return "Instance of Foo"; 
    } 
}

AS3 네임스페이스

고정 속성 상속과 프로토타입 상속이라는 별개의 상속 메커니즘이 공존함으로 인해 기본 클래스의 속성 및 메서드와 관련하여 흥미로운 호환성 문제가 발생합니다. ActionScript의 기반이 되는 ECMAScript 언어 사양과의 호환성을 위해서는 프로토타입 상속을 사용해야 합니다. 즉, 기본 클래스의 속성 및 메서드가 해당 클래스의 프로토타입 객체에 정의되어 있어야 합니다. 반면에 ActionScript 3.0과의 호환성을 위해서는 고정 속성 상속을 사용해야 합니다. 즉, 기본 클래스의 속성 및 메서드가 const , var function 키워드를 통해 해당 클래스 정의에 정의되어 있어야 합니다. 프로토타입 버전 대신 고정 속성을 사용하면 런타임 성능이 크게 향상되는 장점도 있습니다.

ActionScript 3.0에서는 기본 클래스에 대해 프로토타입 상속 및 고정 속성 상속을 모두 사용하여 이 문제를 해결합니다. 각 기본 클래스에는 속성 및 메서드 집합이 두 개씩 있습니다. 한 집합은 ECMAScript 사양과의 호환성을 위해 프로토타입 객체에 정의되어 있고 다른 집합은 ActionScript 3.0과의 호환성을 위해 고정 속성 및 AS3 네임스페이스를 사용하여 정의되어 있습니다.

AS3 네임스페이스는 두 개의 속성 및 메서드 집합 중 하나를 선택할 때 사용할 수 있는 편리한 메커니즘을 제공합니다. AS3 네임스페이스를 사용하지 않으면 기본 클래스의 인스턴스에서는 기본 클래스의 프로토타입 객체에 정의된 속성 및 메서드를 상속합니다. AS3 네임스페이스를 사용하면 항상 프로토타입 속성 대신 고정 속성이 선택되므로 기본 클래스의 인스턴스에서는 AS3 버전을 상속합니다. 즉, 고정 속성을 사용할 수 있는 경우에는 언제나 동일한 이름의 프로토타입 속성 대신 고정 속성이 사용됩니다.

속성 또는 메서드를 선택하여 AS3 네임스페이스로 정규화하면 해당 속성 또는 메서드의 AS3 네임스페이스 버전을 사용할 수 있습니다. 예를 들어 다음 코드에서는 Array.pop() 메서드의 AS3 버전이 사용됩니다.

var nums:Array = new Array(1, 2, 3); 
nums.AS3::pop(); 
trace(nums); // output: 1,2

또는 use namespace 지시문을 사용하여 코드 블록 내의 모든 정의에 대한 AS3 네임스페이스를 열 수 있습니다. 예를 들어 다음 코드에서는 use namespace 지시문을 사용하여 pop() push() 메서드에 대한 AS3 네임스페이스를 엽니다.

use namespace AS3; 
 
var nums:Array = new Array(1, 2, 3); 
nums.pop(); 
nums.push(5); 
trace(nums) // output: 1,2,5

ActionScript 3.0에서는 각 속성 집합에 대한 컴파일러 옵션을 제공하여 전체 프로그램에 AS3 네임스페이스를 적용할 수 있도록 합니다. -as3 컴파일러 옵션은 AS3 네임스페이스를 나타내며 -es 컴파일러 옵션은 프로토타입 상속 옵션을 나타냅니다. 여기서 es 는 ECMAScript를 의미합니다. 전체 프로그램에 대해 AS3 네임스페이스를 열려면 -as3 컴파일러 옵션을 true 로 설정하고 -es 컴파일러 옵션을 false 로 설정합니다. 프로토타입 버전을 사용하려면 컴파일러 옵션의 값을 반대로 설정하면 됩니다. Flash Builder 및 Adobe Flash Professional에서 기본 컴파일러 설정은 -as3 = true -es = false 입니다.

기본 클래스를 확장하고 메서드를 재정의하려면 재정의된 메서드를 선언하는 방법에 AS3 네임스페이스가 어떻게 영향을 미칠 수 있는지 이해해야 합니다. AS3 네임스페이스를 사용하고 있는 경우에 기본 클래스의 메서드를 재정의하려면 override 특성과 함께 AS3 네임스페이스를 사용해야 합니다. AS3 네임스페이스를 사용하지 않는 상태에서 기본 클래스 메서드를 하위 클래스에서 재정의하려면 AS3 네임스페이스나 override 키워드를 사용해서는 안됩니다.