본문으로 바로가기

typescript-kr.github.io/pages/decorators.html

 

TypeScript 한글 문서

TypeScript 한글 번역 문서입니다

typescript-kr.github.io

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

 

Documentation - Decorators

TypeScript Decorators overview

www.typescriptlang.org

 

Decorator 사용을 위한 settings

 

Nest를 공부하다보면 Decorator를 자주 사용하는 것을 확인할 수 있다. 사실 decorator로 만들어졌다고 해도 과언은 아니다.

 

Nest is built around a language feature called decorators. Decorators are a well-known concept in a lot of commonly used programming languages, but in the JavaScript world, they're still relatively new. In order to better understand how decorators work, we recommend reading this article. Here's a simple definition:

An ES2016 decorator is an expression which returns a function and can take a target, name and property descriptor as arguments. You apply it by prefixing the decorator with an @ character and placing this at the very top of what you are trying to decorate. Decorators can be defined for either a class, a method or a property. 출처 - https://docs.nestjs.com/custom-decorators

 

nest 프레임워크가 아닌 TS 자체에서는 decorator가 어떻게 사용되는지 살펴보자

 

만약 js 환경에서 decorator를 사용하고 싶다면 다음과 같이 의존성을 설치하고 bablerc 중 plugin을 아래와 같이 설치해주자

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

 

tsconfig에서 experimentalDecorators와 emitDecoratorMetadata를 true로 설정합시다.

experimentalDecorators 는 데코레이터 사용을 위한 것이고

emitDecoratorMetadata는 reflect-metadata를 사용하기 위한 것입니다.

{
    "compilerOptions": {
        ... 생략
        "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
        "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
    }
}

 

 

 

 

Decorator의 실행 순서

 

decorator는 런타임에 실행됩니다.

따라서, decorator를 덧붙인 클래스에서 인스턴스가 생성된 순간도 아니고, 클래스 내 메서드가 사용된 순간도 아니고 단순히 코드를 실행하는 순간(런타임)에 실행됩니다.

 

decorator가 실행되는 순서에 따라 사용할 수 있는 곳을 나열하면 다음과 같습니다.

프로퍼티 데코레이션이 무언가 생성한 값을 전역 변수에 담아서 다시 클래스 데코레이션에 사용하는 등의 코드를 작성할 때면 실행되는 순서에 주의해야 합니다.

(그런데 그런 동작을 하는 코드를 작성할 일이 생길까요? 지금까지는 없었습니다.)

 

1순위 : 프로퍼티(property) 

2순위 : 메서드(method) 
3순위 : 파라미터(param)
4순위 : 클래스(class)

 

 

Decorator Factory

 

데코레이터에 파라미터를 전달하여 사용하고 싶은 경우 일반 decotrator가 처리하는 함수의 역할을 return하는 함수(팩토리)를 짜야 합니다. 일반적으로 데코레이터에서는 팩토리를 만들어 사용하고 있기 때문에 거의 디폴트처럼 사용하고 있습니다.

 

일반적인 프로퍼티 데코레이터입니다. @expression 꼴로 사용합니다.

function propertyDecorator(target, name) {
  console.log(target, name); // {}, coffeName 반환
}

class Coffee {
  @propertyDecorator
  coffeeName = "indian";
}

console.log("인스턴스 생성 전에 데코레이터 함수가 실행됨");
const p = new Coffee();

 

그런데, @expression(params) 꼴로 사용하고 싶다면 아래와 같이 decorator factory를 만들면 되겠습니다.

function propertyDecorator(param) {
  console.log(param); // "hello"
  return function (target, name) {
    console.log(target, name); // {}, coffeName 반환
  };
}

class Coffee {
  @propertyDecorator("hello")
  coffeeName = "indian";
}

console.log("인스턴스 생성 전에 데코레이터 함수가 실행됨");
const p = new Coffee();

 

 

 

Decorator Signature

같은 Decorator라는 이름을 달고 있지만 각 Decorator 마다 signature가 다르다. 즉, 사용법이 조금씩 다르다.

따라서, 하나의 decorator를 만들어서 클래스에도 쓰고, 메서드에서도 쓰는 등 범용적이지는 않다.

 

아래 정리는 haeguri.github.io/2019/08/25/typescript-decorator/ 여기를 참고하여 작성했음을 미리 밝힌다.

 

타입스크립트의 Decorator 살펴보기 · Devlog

타입스크립트의 데코레이터를 사용하면 클래스, 프로퍼티, 메서드 등에 이전에는 제공하지 않던 방법으로 새로운 기능을 추가할 수 있습니다. 사실 데코레이터라는 문법은 이미 자바스크립트

haeguri.github.io

 

 

class decorator

  1. 클래스(생성자 함수)가 전달됨 (살펴보니 유일하게 인자가 1개 뿐이더라)

  2. 리턴할 수 있는 값은 class 혹은 void 

function classDecorator<T extends { new (...args: any[]): {} }>(constructorFn: T) {...}

 

property decorator :

  1. static 프로퍼티라면 클래스의 생성자 함수, 인스턴스 프로퍼티라면 클래스의 prototype 객체

  2. 프로퍼티 이름

  3. return하는 값이 Property Descriptor 형태임. 혹은 void. 이로서 해당 property의 writable 등을 조작할 수 있음.

function propertyDecorator(target: any, propName: string) {...}

 

parameter decorator

  1. static 메서드의 파라미터 데코레이터라면 클래스의 생성자 함수, 인스턴스의 메서드라면 prototype 객체

  2. 파라미터 데코레이터가 적용된 메서드의 이름

  3. 메서드 파라미터 목록에서의 index

  4. return 값 무시됨.

function parameterDecorator(
	target: any,
	methodName: string,
	paramIndex: number
) {...}

 

method decorator :

  1. static 메서드라면 클래스의 생성자 함수, 인스턴스의 메서드라면 클래스의 prototype 객체

  2. 메서드 이름

  3. 메서드의 Property Descriptor

  4. return 값 무시됨.

function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log(target); // 메서드 집합
  console.log(propertyKey); // 메서드 이름
  console.log(descriptor); // 메서드의 Property Descriptor 
}

 

 

 

 

구체적인 사용법

 

Property Decorator

  1. static 프로퍼티라면 클래스의 생성자 함수, 인스턴스 프로퍼티라면 클래스의 prototype 객체

  2. 프로퍼티 이름

 

(1) 기본형

function propertyDecorator(target, name) {
  console.log(target, name); // {}, coffeName 반환
}

class Coffee {
  @propertyDecorator
  coffeeName = "indian";
}

console.log("인스턴스 생성 전에 데코레이터 함수가 실행됨");
const p = new Coffee();

 

(2) factory

간단하므로 생략.

 

 

MethodDecorator

  1. static 메서드라면 클래스의 생성자 함수, 인스턴스의 메서드라면 클래스의 prototype 객체

  2. 메서드 이름

  3. 메서드의 Property Descriptor

 

 

(1) 기본 형태

function methodDecorator(target: any, name: string, description: PropertyDescriptor) {
  console.log(target); // 메서드 집합
  console.log(name); // 메서드 이름
  console.log(description); // 메서드의 Property Descriptor 
}

class Coffee {
  coffeeName = "indian";

  @methodDecorator
  getCoffeeName() {
    return this.coffeeName;
  }
}

 

속성 설명자(property descriptor) 에 대한 설명은 다음 포스트를 참고합시다. darrengwon.tistory.com/986

 

 

(2) factory

간단하므로 생략

 

 

Parameter Decorator

  1. static 메서드의 파라미터 데코레이터라면 클래스의 생성자 함수, 인스턴스의 메서드라면 prototype 객체

  2. 파라미터 데코레이터가 적용된 메서드의 이름

  3. 메서드 파라미터 목록에서의 index

 

(1) 기본적인 형태

function parameterDecorator(target: any, methodName: string, paramIndex: number) {
  console.log(target); // { getCoffeeName: [Function (anonymous)] }
  console.log(methodName); // getCoffeeName
  console.log(paramIndex); // 몇 번째 인자인가. 당연히 여기선 0
}

class Coffee {
  coffeeName = "indian";

  getCoffeeName(@parameterDecorator message: string) {
    console.log(message);
    return this.coffeeName;
  }
}

 

(2) factory

간단하므로 생략.

 

 

Class Decorator

클래스 선언에 사용되는 클래스 데코레이터는 기존의 클래스 정의를 확장하는 용도로 사용할 수 있습니다.

 

  1.  클래스(생성자 함수)가 전달됨

 

 

(1) 가장 기본적 형태

가장 간단한 형태의 class decorator로, class Decorator의 signature인 constructor func를 필수적으로 넣어 주어야 한다.

function classDecorator(constructor: Function) {

  // class consturction 함수를 출력함. 궁금해서 toString으로 찍어봤는데
  // function Coffee() {this.coffeeName="indian";}  뭐 이런게 출력 됨
  console.log(constructor.toString()); 
}

@classDecorator
class Coffee {
  coffeeName = "indian";
}

 

(2) factory 사용

function classDecorator(param) {
  console.log(param); // this is class
  return function (constructor: Function) {
    console.log(constructor.toString()); // function Coffee() {...}
  };
}

@classDecorator("this is class")
class Coffee {
  coffeeName = "indian";
}

 

(3) class extends

익명 class란 것도 있었군요. 배우고 갑니다.

function classDecorator(param) {
  console.log(param); // this is class
  return function <T extends { new (...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
      newProperty = "new property";
      coffeeName = "override";
    };
  };
}


@classDecorator("this is class")
class Coffee {
  coffeeName = "indian";
}

const p = new Coffee();
console.log(p); // 새로운 newProperty + 기존에 존재한 property는 override 된다!

 

 

 

참고한 글)

 

 

실행순서

jbee.io/typescript/TS-6-Decorator/

 

ts 핸드북

typescript-kr.github.io/pages/decorators.html

 

기본

m.blog.naver.com/pjt3591oo/222120496022

www.notion.so/Decorators-ee9928ba4a0b4c5584ddaf478e3910ee => Very Good

 

속성 설명자

darrengwon.tistory.com/986


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체