본문 바로가기
C.W.K.
Stream
Lesson 06 of 06 · published

TC39 데코레이터: TypeScript 5.0의 Stage 3 모델

~11 min · classes, decorators, tc39, stage-3

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"데코레이터는 클래스가 정의되는 순간, 붙어 있는 요소의 동작을 감싸거나 초기화에 참여하는 함수야."

값과 문맥을 받는 함수

현재 데코레이터 모델에서 함수는 장식할 값과 문맥 객체를 받아. 메서드 데코레이터라면 첫 인자가 원래 메서드이고 둘째 인자가 ClassMethodDecoratorContext야. 문맥에는 이름, 요소 종류, 정적·비공개 여부와 접근 도구가 들어 있어.

데코레이터는 대체 값을 반환하거나 아무것도 반환하지 않을 수 있어. 메서드의 대체 함수를 반환하면 호출을 감싸 로깅이나 시간 측정을 더할 수 있고, undefined를 반환하면 원래 메서드가 그대로 남아. 모든 일은 인스턴스 호출 때가 아니라 클래스 정의와 초기화 규칙에 맞춰 연결돼.

어디에 붙일 수 있을까

TC39 Stage 3 모델은 클래스, 메서드, getter, setter, 필드, auto-accessor 데코레이터를 다뤄. 접근자는 지원되지 않는다는 오래된 문장을 믿으면 안 돼. getter와 setter는 각각 장식할 수 있고, accessor 키워드로 만든 auto-accessor도 전용 문맥과 반환 모양을 가져.

반면 매개변수 데코레이터는 이 모델에 포함되지 않아. TypeScript의 레거시 experimentalDecorators 모드와 그 생태계에서는 볼 수 있지만, TypeScript 5.0부터 플래그 없이 사용하는 새 모델과 같은 계약이 아니야.

레거시 모드와 섞지 않기

experimentalDecorators는 더 오래된 제안의 호출 규칙을 구현해. 새 모델은 값과 문맥 객체를 전달하고 emitDecoratorMetadata 및 매개변수 데코레이터와 호환되지 않아. 기존 라이브러리가 어느 모델을 요구하는지 확인한 뒤 설정과 데코레이터 함수를 한쪽에 맞춰야 해.

새 코드는 현재 모델을 우선하되, 프레임워크가 레거시 메타데이터에 의존한다면 무작정 플래그를 지우지 마. 호출 인자와 반환 규칙이 달라 기존 데코레이터가 그대로 작동한다고 보장할 수 없어.

초기화 훅은 addInitializer

문맥 객체의 addInitializer는 클래스나 인스턴스 초기화 시점에 실행할 작업을 등록해. 예를 들어 메서드를 인스턴스에 바인딩하는 데 사용할 수 있어. 존재하지 않는 finishedHook 같은 이름을 외우지 말고, 장식 대상별 문맥 타입이 제공하는 실제 필드를 확인해.

데코레이터는 횡단 관심사를 모으는 강한 도구야. 반복 로깅처럼 여러 멤버에 같은 규칙이 실제로 필요할 때 쓰고, 평범한 함수 조합이 더 읽기 쉬운 문제까지 데코레이터로 숨기지는 마.

Code

Stage 3 데코레이터 — 메서드와 클래스·typescript
// 단순 method decorator (Stage 3 문법).
function logged(originalMethod: any, context: ClassMethodDecoratorContext) {
  const methodName = String(context.name);
  return function (this: any, ...args: any[]) {
    console.log(`Calling ${methodName}(${args.join(', ')})`);
    const result = originalMethod.call(this, ...args);
    console.log(`${methodName} returned ${result}`);
    return result;
  };
}

class Calculator {
  @logged
  add(a: number, b: number): number {
    return a + b;
  }
}

const c = new Calculator();
c.add(2, 3);
// 출력:
//   Calling add(2, 3)
//   add returned 5

// Class decorator — class를 봉인하고 같은 값을 반환.
function sealed<T extends new (...args: any[]) => any>(
  Ctor: T,
  context: ClassDecoratorContext,
) {
  Object.seal(Ctor);
  Object.seal(Ctor.prototype);
  return Ctor;
}

@sealed
class Greeter {
  greet() { return 'hi' }
}

External links

Exercise

메서드 실행 시간을 기록하는 @timed 데코레이터를 작성해 계산 메서드에 적용해. 원래 반환값과 this가 보존되고 시간 기록도 남는지 확인해.
Hint
원래 메서드를 받는 대체 함수를 반환해. 호출 전후에 performance.now()를 읽고 originalMethod.call(this, ...args)의 결과를 그대로 반환하면 돼.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.