Decorator

김동현·2024년 2월 18일
0

Decorator

목록 보기
1/3
post-thumbnail

Decorator

Decorator
장식가, 도장 및 도배업자

데코레이터는 장식하는 사람, 도배하는 사람이라는 뜻을 가지고 있다.
Typescript에서 Decorator는 클래스, 메서드, 접근자, 프로퍼티, 매개 변수에 장식할 수 있는 특수한 선언이다.

데코레이터는 @expression 표현식을 사용하며 선언된 표현식과 함께 런타임에 호출되는 함수이다.

Declarative?

선언 宣言
1.명사 널리 펴서 말함. 또는 그런 내용.
2. 명사 국가나 집단이 자기의 방침, 의견, 주장 따위를 외부에 정식으로 표명함.
3.명사 어떤 회의의 진행에 한계를 두기 위하여 말함. 또는 그런 말.`

데코레이터 표현식(@express)으로 선언된 함수는 프로그램이 실행된 시점에 호출되어 장식된 피조물(?)에 추가적인 선언을 해주는 것이다.

@express 데코레이터 함수 선언
class or property << 해당 요소를 매개변수로 활용

Decorator Factories

커스텀 데코레이터를 만들고 싶을 때, 함수를 데코레이터 팩토리 방식으로 작성하면 된다.
데코레이터 팩토리는 단순하게, 데코레이터가 런테임에 호출할 표현식을 반환하는 함수이다.

// 데코레이터 팩토리 형식을 따름

function Decorator(value: string) {

 // 💡 런타임에 호출될 함수를 반환함
 return function (target: Function) {
   console.log('전달받은 value 값', value);
   console.log('target 정보: ', target);
 };
}

데코레이터 평가와 호출

  1. 평가(Evaluation)

프로그래밍에서 평가란 표현식이나 변수의 값을 결정하는 과정이다.
즉, 평가가 되면 으로 바뀌게된다.

(1 + 2) => 평가 => 3

여러개의 데코레이터가 선언되어 있을 경우 평가는 위 -> 아래 순서이다.

  1. 호출

여러개의 데코레이터가 선언되어 있을 경후 데코레이터의 호출은 아래 -> 위 순서이다.

function first() {
  console.log('first(): factory evaluated');
  return function (target) {
    console.log(`first(): called parma: ${target.name}`);
  };
}

function second() {
  console.log('second(): factory evaluated');
  return function (target) {
    console.log(`second(): called parma: ${target.name}`);
  };
}

@first() // 시점에 first 함수는 평가된다.
@second() // 시점에 second 함수는 평가된다.
export class AppModule {}

Class Decorators

클래스 데코레이터를 사용하기 위해서는 클래스를 선언하기 직전에 선언하면 된다.

그럼, 런타임 시점에 데코레이터가 반환하는 함수가 호출되며 해당 매개변수 target은 선언된 클래스가 넘어간다.

function ClassDecorator() {
  return function (target) {
    console.log(`target: `, target);
    console.log('target === AppModule: ', target === AppModule);
  };
}

@ClassDecorator()
export class AppModule {}

Method Decorators

메서드 데코레이터를 사용하기 위해서는 메서드 선언 직전에 선언하면된다.

메서드 데코레이터는 3개의 인수를 가진 함수를 반환하며 된다.
1. 생성자 함수 또는 인스턴스 멤버의 프로토 타입
2. 멤버의 이름
3. 멤버의 프로퍼티 설명자

속성 설명자
MDN의 Object.getOwnPropertyDescriptor()를 보면 해당 메서드를 통해 객체 속성들에 대한 설명자를 확인할 수 있다.

속성 설명자 인터페이스는 아래와 같다.

// lib.es5.d.ts
interface PropertyDescriptor {
    configurable?: boolean; // 유형이 바뀔 수 있는 지 여부, 삭제될 수 있는 경우 true
    enumerable?: boolean; // 속성이 열거 가능한지에 대한 여부
    value?: any; // 속성이 가지는 값 (데이터 설명자만 존재)
    writable?: boolean; // 변경될 수 있는 여무 (데이터 설명자만 존재)
    get?(): any; // 속성에 대해 getter로 제공되는 함수(접근자 설명자만 있음)
    set?(v: any): void; // 속성애 대해 setter로 제공되는 함수(접근자 설명자만)
}

다시 Method Decorator로 돌아와서, writable 메서드 데코레이터를 만들어 콘솔을 찍어보자.

function writable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log(target);
    console.log(propertyKey);
    console.log('1: ', descriptor);

    descriptor.writable = value;
    console.log('2: ', descriptor);
  };
}
@Injectable()
export class AppService {
  @writable(false)
  getHello() {
    return new TestApiDto('Hello World!');
  }
}

즉, 메서드 데코레이터를 사용해 메서드의 속성을 바꾸는 등 메서드를 더 다양하게 선언할 수 있게 되는 것이다!

Parameter Decorators

매개 변수 데코레이터를 사용하기 위해 매개 변수 선언 직전에 선언하면 됩니다.

매개 변수 데코레이터도 3개의 인수를 가진 함수를 반환하며 된다.
1. 생성자 함수 또는 인스턴스 멤버의 프로토 타입
2. 멤버의 이름
3. 함수의 매개 변수 목록에 있는 매개 변수의 위치(서수 색인)

⚠️ 매개 변수의 반환 값은 무시됩니다.

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  console.log(target);
  console.log(propertyKey);
  console.log(parameterIndex);
}

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/test/parameter')
                         //required
  parameterDecoratorTest(@required param0, @required param1, @required param2) {
    return 'test';
  }
  ...
}

✋ 멈춰! required 함수는 뭔가 이상한데?! 데코레이터 팩토리 형식이 아니잖아!

데코레이터 팩토리 형식으로 만들면 @표현식을 사용할 때 아래와 같이 사용한다.

  • @DecoratorFactoryPattern() <<

하지만, 데코레이터 팩토리 패턴을 사용하지 않고, 함수 선언문으로 사용하면 아래와 같이 @표현식을 사용한다.

  • @function ex) @required

이렇게 되면, 함수 평가 없이, 런타임 시에 바로 데코레이터 함수를 호출한다!

vs

수학의 합성 함수와 유사합니다. 이 모델에서 함수 f와 g을 합성할 때 (f∘g)(x)의 합성 결과는 f(g(x))와 같습니다. 출처: Typescript
수학의 합성 함수와 유사하게 만들기 위해서 이러한 순서가 나온 것 아닐까??

데코레이터?

데코레이터는 특정 선언에 대해 선언 앞에 @Express 표현식을 사용함으로 써 추가적인 선언 및 작업을 하고 싶을 때 도움이 되는 기능이다.

쉽게 이야기 하면, 클래스, 매개 변수, 프로퍼티 등이 선언 될 때, 데코레이션 된 함수의 매개 변수로 값들로 넘어와 함수가 실행되는 것이다.

이러한 데코레이터를 활용해서 메타 데이터를 지정하거나, 런타임 내 특정 시점에 공통으로 함수를 적용하는 등의 기능을 수행할 수 있는 것이다.

profile
달려보자

0개의 댓글