아무 설정 없이 타입스크립트에서 데코레이터를 사용할 수 없다. 우선 tsconfig.json에서 해당 부분을 설정한다.
{
"compilerOptions": {
"experimentalDecorators": true // 데코레이터를 지원을 위한 옵션
},
"exclude": ["node_modules"],
"include": ["src/**/*"]
}
데코레이터 패턴을 떠올리면 되겠다. 기존 코드를 유지하고 거기에 모듈을 덧씌우는 작업이다. 이를 통해 기본 코드는 유지하고 기능을 추가할 수 있다.
자바로 따지면 다이나믹 프록시, 또는 annotation 구현 후 aop를 적용하는 것과 비슷하다고 보면 되겠다.
js에서 데코레이터는 타입 정의시 바로 호출한다. 그렇기에 aop처럼 활용하려면 몇가지 트릭이 필요하다.
데코레이터는 크게 4가지 부분에 적용가능하다.
function Logger(logString: string) {
return function (constructor: Function) {
console.log(constructor.name + " " + logString);
};
}
@Logger("Hello")
class Person {
name: string = "max";
constructor() {
console.log("Creating person object");
}
}
const per = new Person();
클래스 데코레이터는 constructor를 인자로 받아서 function함수를 생성한다. 이 때 처음 function을 정의한 Logger의 인자는 데코레이터에서 받을 인자이다.
클래스 데코레이터는 인스턴스 생성시 호출한다
proxy를 생성해보자
function Log(param1: string) {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
new_prop = param1;
}
}
}
해당 클래스를 상속받아서 기능을 확장한다.
function Log(target: any, propertyName: string | Symbol) {
console.log("Property decorator!");
console.log(target, propertyName);
}
class Product {
@Log
title: string;
private price: number;
constructor(title:string, price:number) {
this.title = title;
this.price = price;
}
}
속성 데코레이터는 인자로 target과 propertyName을 받는다. 리턴값을 propertyDescriptor로 줘서 속성을 설정할 수 있다.
function Constant() {
return function (target: any, propertyName: any): any {
return {
writable: false
};
};
}
function Log2(target: any, name: string, descriptor: PropertyDescriptor) {
console.log("method decorator!");
console.log(target);
console.log(name);
console.log(descriptor);
}
메서드 데코레이터는 PropertyDescriptor의 수정으로 aop처럼 사용할 수 있다.
데코레이터는 타입 생성시 바로 호출되는데 이 때 descriptor 내부를 수정하면 aop처럼 활용할 수 있다.
PropertyDescriptor란 ES5에서 추가된 객체로 객체를 좀 더 세부적으로 정의할 수 있다. PropertyDescriptor가지는 속성은 다음과 같다.
{
value: [Function: hello],
writable: true,
enumerable: false,
configurable: true
}
value는 함수
writable은 수정 가능 여부
enumerable은 순회 여부
configurable은 property definition이 수정 및 삭제 가능한지 여부
핵심은 value로 자바에서 reflection Method와 비슷한역할이다. decriptor를 활용해 재정의 해보자
function TimeCheck(target: any, name: string, descriptor: PropertyDescriptor) {
let method = descriptor.value;
descriptor.value = function (...args: any) {
console.log("before");
const resultObject = method.apply(this, args);
console.log("after");
return resultObject;
};
}
descriptor.value를 수정하면 데코레이터 패턴처럼 사용가능하다.
function Log3(target: any, name: string | symbol, position: number) {
console.log("Parameter decorator!");
console.log(target);
console.log(name);
console.log(position);
}
파라미터 데코레이터는 target, name, position을 입력받는다.