[TypeScript] 프로퍼티 데코레이터 사용하기

프로퍼티 데코레이터란

클래스의 프로퍼티에 적용되어 해당 프로퍼티의 동작을 제어할 수 있습니다. 값을 읽거나 설정할 때 추가 로직을 실행합니다. 사용하려면 tsconfig.json 파일이나 커맨드라인 옵션에서 experimentalDecorators를 활성화해야 합니다.

  • target: 인스턴스 프로퍼티인 경우 클래스의 프로토타입, 정적 프로퍼티인 경우 생성자 함수입니다.
  • propertyKey: 데코레이터가 적용된 속성의 이름

데코레이터 함수는 Object.defineProperty를 사용해 getter와 setter를 재정의할 수 있습니다.

데코레이터 예제

function LogValue(target: any, propertyKey: string) {
  let storedValue: any;

  const getter = () => storedValue;

  const setter = (newValue: any) => {
    console.log(`${propertyKey} 값이 ${storedValue}에서 ${newValue}로 변경됨`);
    storedValue = newValue;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Item {
  @LogValue
  public price: number;

  constructor(price: number) {
    this.price = price;
  }
}

const item = new Item(1000);
console.log(item.price);     // 출력: 1000
item.price = 1500;           // 출력: price 값이 1000에서 1500로 변경됨
console.log(item.price);     // 출력: 1500
@LogValueprice 속성의 변경을 추적하며, 설정 시 로그를 남깁니다.
function LimitLength(maxLength: number) {
  return (target: any, propertyKey: string) => {
    let value: string;

    const getter = () => value;

    const setter = (newValue: string) => {
      if (typeof newValue === "string" && newValue.length <= maxLength) {
        value = newValue;
      } else {
        console.error(`${propertyKey}는 ${maxLength}자를 넘을 수 없습니다.`);
      }
    };

    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
      enumerable: true,
      configurable: true,
    });
  };
}

class Profile {
  @LimitLength(10)
  public nickname: string;

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

const profile = new Profile("지훈");
console.log(profile.nickname);      // 출력: 지훈
profile.nickname = "너무긴닉네임입니다"; // 출력: nickname는 10자를 넘을 수 없습니다.
console.log(profile.nickname);      // 출력: 지훈 (변경 안 됨)

@LimitLength는 최대 길이를 설정하며, 초과 시 오류를 출력합니다.

function ReadOnly(target: any, propertyKey: string) {
  let value: any;

  Object.defineProperty(target, propertyKey, {
    get: () => value,
    set: (newValue) => {
      if (value === undefined) value = newValue; // 초기 설정만 허용
    },
    enumerable: true,
    configurable: true,
  });
}

class Config {
  @ReadOnly
  public version: string;

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

const config = new Config("1.0.0");
console.log(config.version);    // 출력: 1.0.0
config.version = "2.0.0";       // 무시됨
console.log(config.version);    // 출력: 1.0.0

@ReadOnly는 초기값 설정 후 변경을 막습니다.