2020년 7월 25일 토요일

TypeScript Essence - Decorator

TypeScript Essence - Decorator

Decorator

타입스크립트의 데코레이터는 파이썬의 데코레이터와 마찬가지로 함수를 파라미터로 받는 함수를 쉽게 선언하는 방법입니다.

tsconfig.json 파일에서 experimentalDecorators 컴파일러 옵션을 활성화해야합니다.

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}

Class Decorator

클래스 위에 데코레이터를 설정하면 데코레이터 함수에게 클래스 객체의 참조가 전달된다.

// Constructor Interface
interface PersonConstructor {
  new(name: string): Person;
}

/**
 * 파라미터 타입으로 Person을 사용하는 경우
 * 다음과 같은 에러가 발생할 수 있다.
 * 
 * 1. Cannot use 'new' with an expression
 * whose type lacks a call or construct signature.
 * 2. Argument of type 'typeof Person' is not assignable
 * to parameter of type 'Person'. 
 * Property 'name' is missing in type 'typeof Person'.
 * 3. Property 'prototype' does not exist on type 'Person'.
 *
 * 다음과 같이 처리하여 해결할 수 있다.
 * 1. 파라미터 타입으로 any를 사용한다.
 * 2. 파라미터 타입으로 인터페이스 PersonConstructor를 사용한다.
 */
function Hello(construct: PersonConstructor) {
  construct.prototype.say = function() {
    console.log(`Hello ${this.name}`);
  }
}

@Hello
class Person {
  constructor(private name: string = 'Anonymous') {
    this.name = name;
  }
}

let p = new Person('Tom');
(<any>p).say(); // Hello Tom

함수를 중첩해서 정의하면 데코레이터 사용 시 파라미터를 추가로 받을 수 있습니다.

(function() {
  function Component(param: any) {
    return function(constructor: any) {
      return class extends constructor {
        constructor() {
          super();
          this.name = param.name;
        }
      }
    }
  }

  @Component({
    name: 'Tom'
  })
  class Person {

  }

  let p = new Person();
  console.log(p); // class_1 { name: 'Tom' }
}());

앵귤러는 적극적으로 데코레이터를 받아들여서 앵귤러의 구성 요소를 선언할 때 데코레이터에게 설정정보를 가진 객체를 전달하면 앵귤러 프레임워크가 클래스를 직접 객체로 생성하고 객체가 기본적으로 앵귤러의 구성요소로써의 역할을 수행할 수 있도록 조작합니다.

Method Decorator

(function() {
  function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(target);
    // P { foo: [Function] }
    console.log(`${propertyKey}() was called!`);
    // foo() was called!
    console.log(descriptor);
    // {
    //   value: [Function],
    //   writable: true,
    //   enumerable: true,
    //   configurable: true
    // }
  }

  class P {
    @log
    foo() {

    }
  }

  const p = new P();
  p.foo();
})();

메소드 위에 @log 데코레이터를 설정하면 해당 메소드가 호출될 때 마다 로그가 출력됩니다. 다음 예제를 보시면 어떻게 기능을 추가할 수 있는지 힌트를 얻게 됩니다.

(function() {
  function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const method = descriptor.value;

    // 타겟 객체의 메소드를 포장한 새 함수를 설치한다.
    descriptor.value = function() {
      // arguments: 타겟객체의 메소드가 받아야 할 파라미터를 대신 받는다.
      console.log(`${propertyKey}() was called. arguments:`, arguments);
      // 타겟 객체의 메소드에게 파라미터를 전달하면서 호출한다.
      var result = method.apply(this, arguments);
      // 타겟 객체의 메소드가 리턴 값을 받아서 재 리턴한다.
      return result;
    };
  }

  class P {
    @log
    foo(a: string, b: string) {
      console.log(`Do something`);
    }
  }

  const p = new P();
  p.foo('Hello', 'World');
  // foo() was called. arguments: { '0': 'Hello', '1': 'World' }
  // Do something
})();

데코레이터는 프로퍼티나 파라미터에도 적용할 수 있습니다. 이에 대한 부분은 생략하오니 관심이 있는 분은 다음 사이트를 참고하시기 바랍니다.

https://www.typescriptlang.org/docs/handbook/decorators.html

댓글 없음:

댓글 쓰기

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...