끄적끄적

[타입스크립트] 데코레이터(Decorator) 본문

Front-end/Typescript

[타입스크립트] 데코레이터(Decorator)

mashko 2021. 6. 2. 23:09
반응형

타입스크립트의 데코레이터에 대해서 알아보고 정리해 보고자 합니다.
타입스크립트로 개발을 진행하다보면 필연적으로 데코레이터란 것을 접하게 됩니다. 자바를 경험한 사람이라면 어노테이션과 굉장히 흡사하다고 생각이 들 정도로 비슷하게 보이더라구요 (제가...그렇습니다.) 데코레이터는 함수 라고 할 수 있습니다. 데코레이터는 말 그대로 코드 조각을 장식해주는 역할을 하며 타입스크립트에서는 그 기능을 함수로 구현할 수 있습니다.

데코레이터(decorator)

  • 데코레이터는 클래스 선언, 메서드, 접근자, 프로퍼티 또는 매개 변수에 첨부할 수 있는 특수한 종류의 선언입니다.
  • 데코레이터 함수에는 target(현재타겟), key(속성이름), descriptor(설명)가 전달됩니다.
  • 메소드나 클래스 인스턴스가 만들어지는 런타임에 실행됩니다. 즉, 매번 실행되지 않습니다.
  • 메서드, 접근자 또는 프로퍼티 데코레이터가 다음에 오는 매개 변수 데코레이터는 각 인스턴스 멤버에 적용됩니다.
  • 메서드, 접근자 또는 프로퍼티 데코레이터가 다음에 오는 매개 변수 데코레이터는 각 정적 멤버에 적용됩니다.
  • 매개 변수 데코레이터는 생성자에 적용됩니다.
  • 클래스 데코레이터는 클래스에 적용됩니다.

Setup

$ npm install babel-core babel-plugin-transform-decorators-legacy --save-dev
/* .babelrc */
{
  //...
  "plugins": ["transform-decorators-legacy"]
}

tsconfig에 아래의 코드를 변경해 줍니다.

tsc --target ES5 --experimentalDecorators
/* tsconfig.json */
{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true
  }
}

Class Decorator
클래스 데코레이터는 클래스 선언 직전에 선언됩니다. 클래스 데코레이터는 클래스 생성자에 적용되며 클래스 정의를 관찰, 수정 또는 교체하는 데 사용할 수 있습니다.

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        hello = "test2";
    }
}

@classDecorator
class Example {
    test:string;
    constructor(m: string) {
        this.hello = m;
    }
}

Method decorator
메서드 데코레이터는 메서드 선언 직전에 선언됩니다. 메서드 관찰, 수정 또는 대체하는 데 사용할 수 있습니다.

function readOnly() {
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.writable = false;

        return descriptor;
    }
}

class Example {
    @readOnly
    test(): string {
        return 'test';
    }
}

Accessor Decorators
접근자 데코레이터는 접근자 선언 바로 전에 선언됩니다. 접근자 데코레이터는 접근자의 프로퍼티 설명자에 적용되며 접근자의 정의를 관찰, 수정 또는 교체하는 데 사용할 수 있습니다.

function configurable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
    };
}

class Point {
    private _x: number;
    private _y: number;
    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }

    @configurable(false)
    get x() { return this._x; }

    @configurable(false)
    get y() { return this._y; }
}

Property Decorators
프로퍼티 데코레이터는 프로퍼티 선언 바로 전에 선언됩니다. 프로퍼티 데코레이터는 선언 파일이나 다른 주변 컨텍스트에서 사용할 수 없습니다.

import "reflect-metadata";

const formatMetadataKey = Symbol("format");

function format(formatString: string) {
    return Reflect.metadata(formatMetadataKey, formatString);
}

function getFormat(target: any, propertyKey: string) {
    return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

class Example {
    @format("test, %s")
    text: string;

    constructor(message: string) {
        this.text = message;
    }
    test() {
        let formatString = getFormat(this, "test");
        return formatString.replace("%s", this.text);
    }
}

Parameter Decorators
매개변수 데코레이터는 매개 변수 선언 직전에 선언됩니다. 매개변수 데코레이터는 클래스 생성자 또는 메서드 선언의 함수에 적용됩니다. 매개변수 데코레이터는 선언 파일, 오버로드 또는 다른 주변 컨텍스트에서 사용할 수 없습니다.

import "reflect-metadata";

const requiredMetadataKey = Symbol("required");

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
    let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}

class Example {
    test(@required text: string) {
        return text;
    }
}

여러형태의 데코레이터를 만들어 프로젝트에서 쓰게 된다면 조금 더 코드에 대한 퀄리티와 관심사에 대해서 데코레이터로 만들어 둔어 라이브러리 형태로 만들어 둔다면 꽤나 편하게 개발 할 수 있을 것 같다는 생각이 듭니다.
아래의 링크를 통해 조금 더 많은 정보를 얻을 수 있습니다.

참고

반응형
Comments