---
id: typescript-decorator-patterns
title: TypeScript Decorators (Stage 3) — 실용 사용법
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [typescript, decorators, metaprogramming, vibe-coding]
tech_stack: { language: "TypeScript 5+", applicable_to: ["Backend", "Library"] }
applied_in: []
aliases: [@decorator, ECMAScript decorators, NestJS, class decorator]
---

# TypeScript Decorators (Stage 3)

> TS 5+ 에서 stage 3 decorator 가 표준. 옛 experimentalDecorators 와 다름. **클래스 / 메서드 / 필드 / 접근자 / auto-accessor** 5종. 신규 코드는 stage 3.

## 📖 핵심 개념
- 클래스 decorator: 클래스 자체 변환.
- 메서드 decorator: 메서드 wrapping (logging, retry, cache).
- 필드 decorator: 초기화 / 변환.
- accessor / auto-accessor: getter/setter wrap.

## 💻 코드 패턴

### 메서드 decorator — logging
```ts
function log<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  ctx: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
  return function (this: This, ...args: Args): Return {
    console.log(`[${String(ctx.name)}] called with`, args);
    const result = target.apply(this, args);
    console.log(`[${String(ctx.name)}] returned`, result);
    return result;
  };
}

class API {
  @log
  fetch(id: string) { return `user-${id}`; }
}
```

### Cache (memoization)
```ts
function cache<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  _ctx: ClassMethodDecoratorContext<This, any>
) {
  const memo = new Map<string, Return>();
  return function (this: This, ...args: Args): Return {
    const key = JSON.stringify(args);
    if (memo.has(key)) return memo.get(key)!;
    const v = target.apply(this, args);
    memo.set(key, v);
    return v;
  };
}

class Calc { @cache fib(n: number): number { return n < 2 ? n : this.fib(n - 1) + this.fib(n - 2); } }
```

### Class decorator — auto-register
```ts
const registry = new Map<string, new () => any>();

function service(name: string) {
  return function <T extends new (...args: any[]) => any>(target: T, _ctx: ClassDecoratorContext<T>) {
    registry.set(name, target);
    return target;
  };
}

@service('user')
class UserService { ... }
```

### Field decorator — required + non-empty
```ts
function nonEmpty<This, Value extends string | undefined>(
  _target: undefined,
  ctx: ClassFieldDecoratorContext<This, Value>
) {
  return function (this: This, value: Value) {
    if (!value) throw new Error(`${String(ctx.name)} cannot be empty`);
    return value;
  };
}

class Config { @nonEmpty apiKey!: string; }
```

## 🤔 의사결정 기준
| 사용처 | decorator 적합 |
|---|---|
| Logging / metrics / retry wrap | ✅ |
| DI 컨테이너 등록 (Nest/Inversify) | ✅ |
| Validation (class-validator) | ✅ |
| Simple 변환 — 함수로 충분 | ❌ |
| Hot path (마이크로 최적화) | ❌ — wrap overhead |
| Stage 2 (legacy) 사용 라이브러리 (Nest 일부) | experimentalDecorators 모드 유지 |

## ❌ 안티패턴
- **stage 2 + stage 3 혼용**: 동작 다름. 한 컨벤션.
- **메서드 안 this 잃음**: arrow function 으로 wrap 하면 this 깨짐. function expression 사용.
- **decorator 가 side effect (전역 mutate)**: 모듈 import 만으로 부작용. 명시적 init 함수 권장.
- **타입 추론 잃음**: this/arg/return generic 명시 안 하면 any 처럼.
- **decorator 안에서 비동기 작업**: decorator 자체는 동기. 비동기 setup 은 별도.
- **체이닝 순서 혼동**: 위에서 아래 적용 (apply 순서는 역). 의존 있으면 한 줄 줄이기.

## 🤖 LLM 활용 힌트
- TS 5+ stage 3 시그니처 명시 — `_target, ctx: Class*DecoratorContext`.
- this/Args/Return generic 풀 명시.

## 🔗 관련 문서
- [[TypeScript_Generic_Constraints]]
- [[Pure_Functions_in_Practice]]
