소개

  • TypeScript 및 ES6에 클래스가 도입되면서 클래스 및 클래스 멤버에 주석(annotation)을 달거나 수정하기 위해 추가 기능이 필요한 특정 시나리오가 있습니다.
  • 데코레이터는 클래스 선언과 멤버에 대한 주석(annotation)메타 프로그래밍 구문을 모두 추가 할 수있는 방법을 제공합니다.
    • 정말 개꿀
  • 데코레이터는 JavaScript에 대한 2단계 제안이며 TypeScript의 실험 기능으로 제공됩니다.

참고 데코레이터는 향후 릴리스에서 변경 될 수있는 실험적인 기능입니다. ( Nest.js 같은 서버 프레임워크에서 정말 애용하는 문법이다. 고로, 없어질 가능성은 엄청나게 적으며 없어진다는 발표가 나면 엄청난 반발을 사게 될 것 이다. )

 

  • 데코레이터에 대한 실험 지원을 활성화하려면 명령 행 또는 tsconfig.json에서 ExperimentDecorators 컴파일러 옵션을 활성화해야합니다.
    • Nest.js 프로젝트의 tsconfig.json 파일에 아주 자랑스럽게 명시되어 있다. => 개꿀

커멘드 라인

tsc --target ES5 --experimentalDecorators

 

tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}

 

데코레이터(Decorators)

  • 데코레이터는 class declarationmethodaccessorproperty또는 parameter.에 첨부 할 수있는 특수한 종류의 선언입니다.
  • 데코레이터는 @expression 형식을 사용합니다. 여기서 expression은 데코레이트 된 선언에 대한 정보와 함께 런타임에 호출 될 함수로 평가되어야합니다.

 

  • 예를 들어, 데코레이터 @sealed를 사용하면 다음과 같이 sealed 밀봉 함수를 작성할 수 있습니다.
function sealed(target) {
    // do something with 'target' ...
}

 

참고, 아래의 Class Decorators에서 데코레이터에 대한 자세한 예를 볼 수 있습니다.

데코레이터 팩토리(Decorator Factories)

  • 데코레이터가 선언에 적용되는 방식을 사용자 정의하고 싶다면, 데코레이터 팩토리를 작성하여도 좋습니다.
  • 데코레이터 팩토리는 단순히 데코레이터가 런타임에 호출 할 표현식을 반환하는 함수입니다.

 

  • 다음과 같은 방식으로 데코레이터 팩토리를 작성할 수 있습니다.
function color(value: string) { // this is the decorator factory
    return function (target) { // this is the decorator
        // do something with 'target' and 'value'...
    }
}

 

참고, 아래 Method Decorators에서 데코레이터 팩토리에 대한 자세한 예를 볼 수 있습니다. 

데코레이터 컴포지션

  • 다음 예제와 같이 선언에 여러 데코레이터를 적용 할 수 있습니다.

한 줄 적용

@f @g x

 

여러줄 적용

@f
@g
x

 

  • 여러 데코레이터가 단일 선언에 적용되는 경우 평가는 function composition in mathematics과 유사합니다.
  • 이 모델에서 함수 fg을 구성 할 때 결과 합성 (f ∘ g) (x)f( g(x))와 같습니다.

 

  • 따라서 TypeScript의 단일 선언에서 여러 데코레이터를 평가할 때 다음 단계가 수행됩니다.
  1. 각 데코레이터의 평가는 위에서 아래로 평가됩니다.
  2. 그런 다음 결과는 아래에서 위로 함수가 호출됩니다.
  • decorator factories를 사용하는 경우 다음 예제를 통해이 평가 순서를 관찰 할 수 있습니다.
function f() {
    console.log("f(): evaluated");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("f(): called");
    }
}

function g() {
    console.log("g(): evaluated");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("g(): called");
    }
}

class C {
    @f()
    @g()
    method() {}
}

 

  • 이 출력을 콘솔에 인쇄하는 것 입니다.
f(): evaluated
g(): evaluated
g(): called
f(): called

데코레이터 평가

클래스 내부의 다양한 선언에 적용된 데코레이터가 적용되는 방법에는 잘 정의 된 순서가 있습니다.

 

  1. 매개 변수 데코레이터 뒤에 오는 메소드, 접근자 또는 프로퍼티 데코레이터는 각 인스턴스 멤버에 적용됩니다.
  2. 매개 변수 데코레이터 뒤에 오는 메소드, 접근자 또는 프로퍼티 데코레이터는 각 정적 멤버에 적용됩니다.
  3. 매개 변수 데코레이터는 생성자에 적용됩니다.
  4. 클래스 데코레이터는 클래스에 적용됩니다.

클래스 데코레이터(Class Decorators)

  • 클래스 데코레이터는 클래스 선언 직전에 선언됩니다.
  • 클래스 데코레이터는 클래스의 생성자에 적용되며 클래스 정의를 관찰, 수정 또는 교체하는 데 사용할 수 있습니다.
  • 클래스 데코레이터는 선언 파일 또는 다른 주변 환경(예 : 선언 클래스)에서 사용할 수 없습니다.

 

  • 클래스 데코레이터의 표현식은 런타임에 함수로 호출되며 데코레이트 된 클래스의 생성자는 유일한 인수로 사용됩니다.

 

  • 클래스 데코레이터가 값을 반환하면 클래스 선언을 제공된 생성자 함수로 바꿉니다.

참고, 새 생성자 함수를 반환하도록 선택한 경우 원래 프로토 타입을 유지 관리해야합니다. 런타임시 데코레이터를 적용하는 로직은이를 수행하지 않습니다.

 

  • 다음은 Greeter 클래스에 적용된 클래스 데코레이터 (@sealed)의 예입니다.
@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

 

  • 다음 함수 선언을 사용하여 @sealed 데코레이터를 정의 할 수 있습니다.
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

 

 

  • @sealed가 실행되면 생성자와 프로토 타입이 모두 봉인됩니다.
  • 다음으로 생성자를 재정의하는 방법에 대한 예제가 있습니다.
function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

console.log(new Greeter("world"));

 

 

알아야하는 게 있어지면 또 정리....

 

블로그 이미지

_김은찬

두번 다시는 꺾이지 않으리

,