타입스크립트의 데코레이터

Pien·2023년 4월 1일
1

Typescript

목록 보기
2/2

데코레이터란?

데코레이터란 ECMAScript의 실험적 기능이며, 현재는 타입스크립트에서만 지원하는 문법 중 하나로 @를 사용해서 데코레이터임을 표시한다.
데코레이터를 이용하면 클래스, 메서드, 프로퍼티 등에 추가적인 기능을 부여할 수 있으며, 직접 데코레이터를 작성해 가독성이 좋은 코드를 구현할 수 있다.
라이브러리 중 데코레이터를 기본적으로 사용하는 라이브러리도 있으며, 대표적으로 Angular, nestJs, TypeORM이 있다.

데코레이터에 대해

최근 타입스크립트 5.0 버전이 공식 발표되었다. 변경 사항중 하나는 기존에 실험적 기능이었던 데코레이터문법을 정식으로 지원한다는 것이다.

데코레이터는 타입스크립트 1.5 버전에서 최초로 도입되었으며, 5.0버전에 이르기 전까지는 실험적 기능으로 제공 했었고, 데코레이터를 사용하기 위해선 tsconfig.json파일에서 experimentalDecorators 설정을 true로 설정했을 경우에만 사용할 수 있었다.
데코레이터가 정식기능으로 올라갔지만, 타입스크립트5.0에서의 데코레이터와 기존의 실험적 데코레이터와는 완벽한 호환을 지원하지 않는다. 두 데코레이터는 서로 다른 방식으로 타입을 체크, 출력하므로, 실험적 데코레이터 함수가 신규 데코레이터에서의 동작 방식을 지원할 가능성은 작을 것이다.
실험적 데코레이터를 사용하고 싶다면, 기존과 동일하게 experimentalDecorators 설정을 true로 설정하면 기존의 실험적 데코레이터 방식으로 동작시킬 수 있다

데코레이터의 종류

Class 데코레이터

Class 데코레이터는 Class 선언을 수정하거나 클래스 자체를 변경하는 데 사용된다. Class 데코레이터는 Class 생성자 함수를 인자로 받아 수정된 Class 생성자 함수를 반환한다.

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
  // 클래스 생성자 함수 수정, 수정된 클래스 반환
  return class extends constructor {
    newProperty = "new property";
    hello = "override";
  }
}

// 데코레이터 선언
@classDecorator
class MyClass {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}

console.log(new MyClass("world"));

// { newProperty: "new property", hello: "override" }

MyClass 클래스에 @classDecorator 데코레이터를 적용한 결과, @classDecorator 데코레이터가 새로운 클래스를 생성하면서 기존의 값을 덮어씌워 기존 클래스에서 생성한 변수가 모두 변경되었다. 이처럼 Class 데코레이터를 통해 클래스의 프로퍼티나 메서드를 변경하거나 추가할 수 있다.

Method 데코레이터

Method 데코레이터는 Method의 동작을 변경하거나 메타데이터를 추가하는 등의 동작에 사용된다. Method 데코레이터는 세 개의 인자를 받아 동작한다. 
세 개의 인자는 첫 번째 Class의 인스턴스 또는 생성자 함수, 두 번째 데코레이터가 적용될 메서드의 이름, 세 번째 Method의 Descriptor객체 이렇게 총 세 개의 인자를 받아 동작한다.
이렇게 Method 데코레이터는 Method의 Descriptor 객체를 수정해 Method의 동작을 변경한다.

function log(target: Object, key: string, descriptor: PropertyDescriptor) {
  // 기존 메소드 
  const originalMethod = descriptor.value;

  descriptor.value = function(...args: any[]) {
    // 호출 한 순간의 로그
    console.log(`Method ${key} called with arguments: ${args}`);
    // 기존 메소드 실행
    const result = originalMethod.apply(this, args);
    // 결과 값의 로그
    console.log(`Method ${key} returned ${result}`);
    return result;
  };

  return descriptor;
}

class MyClass {
  @log
  add(x: number, y: number): number {
    return x + y;
  }
}

const myInstance = new MyClass();
myInstance.add(2, 3);

// Method add called with arguments: 2,3 
// Method add returned 5

add Method에 @log 데코레이터를 만들어 적용했다. @log 데코레이터는 Method 값을 받아 Method가 실행되기 전과 후의 값을 로그로 출력해주는 데코레이터이다.
이처럼 Method 데코레이터는 기존 Method에서 동작을 추가 하거나, 동작을 변경시키거나 반환값을 변경하는 등 다양한 작업에 사용된다.
Method 데코레이터는 위와 같은 로그 출력뿐 아니라 인증, 유효성검사, 트랜잭션 등 많은 곳에서 사용할 수 있으며, 손쉽게 부가적인 기능을 추가할 수 있다.

Property 데코레이터

Property 데코레이터는 Class 선언 부에서 정의된 Property를 수정하는 데 사용된다. Property 데코레이터는 두 개의 인자를 받아 동작한다.
두 개의 인자는 첫 번째 Class의 인스턴스 또는 생성자 함수, 두 번째 데코레이터가 적용될 Property의 이름 이렇게 총 두 개의 인자를 받아 동작한다.

function minLength(length: number) {
  return function(target: any, key: string) {
    let value = target[key];

    const getter = function() {
      return value;
    };

    const setter = function(newVal: string) {
	  // length로 받은 값 보다 짧을 경우 콘솔에 에러 표시
      if (newVal.length < length) {
        console.log(`Error: ${key} must be at least ${length} characters long.`);
      } else {
        // 신규값 할당
        value = newVal;
      }
    };

	// Property 정보 수정
    Object.defineProperty(target, key, {
      get: getter,
      set: setter,
      // Property 열거 여부
      enumerable: true,
      // Property 설정 변경 여부
      configurable: true,
    });
  };
}

class MyClass {
  @minLength(5)
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

const myInstance = new MyClass('Gisuk');
console.log(myInstance.name);
// Gisuk
myInstance.name = 'Tom'; // Error: Tom must be at least 5 characters long.
console.log(myInstance.name);
// Gisuk
myInstance.name = 'GisukJang';
console.log(myInstance.name);
// GisukJang

MyClass의 name Property에 @minLength() 데코레이터를 만들어 적용했다. @minLength 데코레이터는 숫자를 인자로 받아 해당 인자보다 짧은 문자열은 Property에 할당하지 않도록 검증하는 데코레이터이다. 
@minLength(5) 데코레이터를 주입받은 name은 생성 후 Tom 문자열을 할당했지만, @minLength(5) 데코레이터에 의해 할당되지 않아, 로그에는 그대로 Gisuk 문자열이 출력되는 것을 볼 수 있다.

마치며

nestJs 처음 데코레이터를 접하며, 처음에는 데코레이터가 어떤 방식으로 모르며 사용해 왔다. 그냥 이거 붙이면 작동하더라.. 저거 붙이면 작동하더라
공식 문서를 보면서 기본적으로 제공해주는 데코레이터를 사용하는데 급급했고, TypeORM 0.3버전에서 Custom Repository가 비활성화되면서 필요한 데코레이터를 만들어 사용해 보며 데코레이터의 유용함을 느꼈다.
데코레이터를 이용하면, 각종 Method에 추가적인 기능을 부여하기 쉽고, 가독성과 유지보수성이 올라간다 생각한다.
타입스크립트가 이제 공식적으로 데코레이터를 지원함에 따라 데코레이터를 활용하는 사람이 더욱 늘어날 것이다. 나 또한 데코레이터를 더 활용해 좋은 코드를 만들어 가고 싶다.

0개의 댓글