[TS] Section8 데코레이터

lim1313·2022년 2월 10일
0

TIL

목록 보기
15/22

🍊 데코레이터

데코레이터는 앞에서 말한 것처럼 “클래스”, “메서드”, “접근자”, “프로퍼티”, “파라미터”에 적용할 수 있다. 데코레이터는 @expression 형식으로 적용하는데, 여기에서 expression 은 반드시 함수여야 한다.

decorator는 class의 다양한 property 및 method의 정의를 수정 및 교체하는 function이다.
decorator는 runtime에 호출된다 (즉, class instance를 생성하지 않아도 호출됨)

참조(reference)

데코레이터는 @decorator과 같이 사용할 수 있으며 @[name]의 형식일 때 name에 해당하는 이름의 함수를 참조하게 된다.

실행 시점(execute time)

이렇게 데코레이터로 정의된 함수는 데코레이터가 적용된 메소드가 실행되거나 클래스가 new라는 키워드를 통해 인스턴스화 될 때가 아닌 런타임 때 실행된다. 즉, 매번 실행되지 않는다.

tsconfig.json 설정

"target": "es2016",
"experimentalDecorators": true

🍊 클래스 데코레이터

class decorator는 클래스의 생성자를 유일한 인수로 호출한다. 즉, 인자가 하나이다.
class를 리턴한다.

function Logger(constructor: Function) {
  console.log('logger');
  console.log(constructor);
}

@Logger
class Person {
  name = 'max';

  constructor() {
    console.log('creating person object...');
  }
}

const pers = new Person();
console.log(pers);

🌈 데코레이터 팩토리

데코레이터 팩토리는 데코레이터가 런타임에 호출할 표현식을 반환하는 함수이다.
팩토리 함수와 함께 실행된다면 데코레이션 함수가 사용하는 값을 커스터마이즈할 수 있다.

function Logger(logString: string) {
  return function (constructor: Function) {
    console.log(logString);
    console.log(constructor);
  };
}

@Logger('LOGGING')
class Person {
  name = 'max';

  constructor() {
    console.log('creating person object...');
  }
}

const pers = new Person();
console.log(pers);

🌈 유용한 데코레이터

예시 1

function WithTemplate(template: string, hookId: string) {
  // 존재하지만 불필요하기 때문에 _(언더바)로 표시
  return function (_: Function) {
    const hookEl = document.getElementById(hookId);
    if (hookEl) {
      hookEl.innerHTML = template;
    }
  };
}

@WithTemplate('<h1>temp</h1>', 'app')
class Person {
  name = 'max';

  constructor() {
    console.log('creating person object...');
  }
}

const pers = new Person();
console.log(pers);

예시 2

function WithTemplate(template: string, hookId: string) {
  return function (constructor: any) {
    const hookEl = document.getElementById(hookId);
    const p = new constructor();
    if (hookEl) {
      hookEl.innerHTML = template;
      hookEl.querySelector('h1')!.textContent = p.name;
    }
  };
}

@WithTemplate('<h1>temp</h1>', 'app')
class Person {
  name = 'max';

  constructor() {
    console.log('creating person object...');
  }
}

const pers = new Person();
console.log(pers);

예시 3

데코레이터는 아래에서부터 실행된다.
데코레이터 팩토리는 함수를 명시한 순서로 실행된다.

function Logger(logString: string) {
  console.log('Logger Factory');

  return function (constructor: Function) {
    console.log('logger');
    console.log(logString);
    console.log(constructor);
  };
}

function WithTemplate(template: string, hookId: string) {
  console.log('Template Factory');

  return function (constructor: any) {
    console.log('template');
    const hookEl = document.getElementById(hookId);
    const p = new constructor();
    if (hookEl) {
      hookEl.innerHTML = template;
      hookEl.querySelector('h1')!.textContent = p.name;
    }
  };
}

@Logger('Logging')
@WithTemplate('<h1>temp</h1>', 'app')
class Person {
  name = 'max';

  constructor() {
    console.log('creating person object...');
  }
}

const pers = new Person();
console.log(pers);
// 결과
Logger Factory
Template Factory
template
creating person object...
logger
Logging
class Person {
    constructor() {
        this.name = 'max';
        console.log('creating person object...');
    }
}
creating person object...
Person {name: 'max'}

🍊 프로퍼티 데코레이터

target과 propertyName이라는 두가지 인자를 가진다.
리턴은 프로퍼티 description 형태이다.

function Log(target: any, propertyName: string | Symbol) {
  console.log(target, propertyName);
  //=> {constructor: ƒ, getPriceWthTax: ƒ}'title'
}

class Product {
  @Log
  title: string;
  private _price: number;

  set price(val: number) {
    if (val > 0) {
      this._price = val;
    } else {
      throw new Error('Invalid price - should be positive!');
    }
  }

  constructor(t: string, p: number) {
    this.title = t;
    this._price = p;
  }

  getPriceWthTax(tax: number) {
    return this._price * (1 + tax);
  }
}

🍊 접근자(accessor) 데코레이터

function Log2(target: any, name: string, descriptor: PropertyDescriptor) {
  console.log('Accesstor decorator');
  console.log(target);
  console.log(name);
  console.log(descriptor);
}

class Product {
  title: string;
  private _price: number;

  @Log2
  set price(val: number) {
    if (val > 0) {
      this._price = val;
    } else {
      throw new Error('Invalid price - should be positive!');
    }
  }

  constructor(t: string, p: number) {
    this.title = t;
    this._price = p;
  }

  getPriceWthTax(tax: number) {
    return this._price * (1 + tax);
  }
}
// 결과
Accesstor decorator
{constructor: ƒ, getPriceWthTax: ƒ}
price
{get: undefined, enumerable: false, configurable: true, set: ƒ}

🍊 메소드 데코레이터

function Log3(target: any, name: string | Symbol, descriptor: PropertyDescriptor) {
  console.log('Method decorator');
  console.log(target);
  console.log(name);
  console.log(descriptor);
}

class Product {
  @Log
  title: string;
  private _price: number;

  @Log2
  set price(val: number) {
    if (val > 0) {
      this._price = val;
    } else {
      throw new Error('Invalid price - should be positive!');
    }
  }

  constructor(t: string, p: number) {
    this.title = t;
    this._price = p;
  }

  @Log3
  getPriceWthTax(tax: number) {
    return this._price * (1 + tax);
  }
}
// 결과
Method decorator
{constructor: ƒ, getPriceWthTax: ƒ}
getPriceWthTax
{writable: true, enumerable: false, configurable: true, value: ƒ}

🍊 매개변수 데코레이터

function Log4(target: any, name: string | Symbol, position: number) {
  console.log('Method decorator');
  console.log(target);
  console.log(name);
  console.log(position);
}


class Product {
  title: string;
  private _price: number;

  set price(val: number) {
    if (val > 0) {
      this._price = val;
    } else {
      throw new Error('Invalid price - should be positive!');
    }
  }

  constructor(t: string, p: number) {
    this.title = t;
    this._price = p;
  }

  getPriceWthTax(@Log4 tax: number) {
    return this._price * (1 + tax);
  }
}
// 결과
Parameter decorator
{constructor: ƒ, getPriceWthTax: ƒ}
getPriceWthTax
0

🍊

profile
start coding

0개의 댓글