데코레이터 — class·method·property
함수를 클래스 정의에 직접 붙이는 메타 도구. ES2024 표준과 legacy 차이.
고급 파트 시작. 데코레이터는 클래스·메서드·속성 위에 @name 한 줄로 함수를 적용해 추가 동작을 입히는 도구. Angular·NestJS·TypeORM 등 OOP 색이 진한 프레임워크에서 광범위하게 사용. 16편은 ES2024 표준과 옛 legacy 형태의 차이, 그리고 4가지 데코레이터 종류까지.
ES2024 표준 vs Legacy
| Legacy (옛) | ES2024 표준 (현재) | |
|---|---|---|
| tsconfig | experimentalDecorators: true | 5.0+ 기본 활성 |
| @types | reflect-metadata 필요 | 없어도 됨 |
| 매개변수 데코 | 지원 | 미지원 (제안 단계) |
| 시그니처 | (target, key, descriptor) | (value, context) |
현재 상태. TS 5.0+ 는 ES2024 표준이 기본. Angular·NestJS·TypeORM 같은 라이브러리들은 아직 legacy 사용 — experimentalDecorators: true + reflect-metadata. 본 편은 ES2024 표준 위주.
class 데코레이터
// ES2024 표준
function logCreation<T extends new (...args: any[]) => any>(
Target: T,
context: ClassDecoratorContext
) {
return class extends Target {
constructor(...args: any[]) {
super(...args);
console.log(`[${context.name}] 인스턴스 생성`);
}
};
}
@logCreation
class User {
constructor(public name: string) {}
}
new User("준성"); // "[User] 인스턴스 생성"
method 데코레이터
function logCall(
method: Function,
context: ClassMethodDecoratorContext
) {
return function (this: any, ...args: any[]) {
console.log(`${String(context.name)} 호출`, args);
return method.apply(this, args);
};
}
class Calc {
@logCall
add(a: number, b: number) {
return a + b;
}
}
new Calc().add(1, 2); // "add 호출 [1, 2]" 출력 후 3
accessor (getter/setter) 데코레이터
function notNull(
value: { get: () => any; set: (v: any) => void },
context: ClassAccessorDecoratorContext
) {
return {
get: value.get,
set(this: any, newValue: any) {
if (newValue == null) throw new Error(`${String(context.name)} 은 null 불가`);
value.set.call(this, newValue);
},
init(initialValue: any) {
if (initialValue == null) throw new Error(`${String(context.name)} 초기값 null`);
return initialValue;
},
};
}
class User {
@notNull
accessor name: string = "";
}
property 데코레이터 (field)
function defaultValue<T>(value: T) {
return function (
target: undefined,
context: ClassFieldDecoratorContext
) {
return function (this: any, initial: T | undefined) {
return initial ?? value;
};
};
}
class Config {
@defaultValue(3000)
port!: number;
@defaultValue("localhost")
host!: string;
}
데코레이터 팩토리 — 인자 받기
// 데코레이터에 옵션을 주려면 한 단계 더 감싸기
function trace(prefix: string) {
return function (method: Function, context: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log(`[${prefix}] ${String(context.name)}`);
return method.apply(this, args);
};
};
}
class Service {
@trace("DB")
query(sql: string) { ... }
@trace("API")
fetch(url: string) { ... }
}
실전 — 캐싱 데코레이터
function memoize(method: Function, context: ClassMethodDecoratorContext) {
const cache = new Map<string, any>();
return function (this: any, ...args: any[]) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = method.apply(this, args);
cache.set(key, result);
return result;
};
}
class Stats {
@memoize
expensive(n: number): number {
console.log("계산 중...");
return n * n;
}
}
const s = new Stats();
s.expensive(5); // "계산 중..." 출력 후 25
s.expensive(5); // 캐시 hit, 즉시 25
Legacy (옛) 데코레이터 — Angular·NestJS 가 여전히 사용
// 옛 형식 — tsconfig: experimentalDecorators: true
function Component(options: { selector: string }) {
return function (target: Function) {
Reflect.defineMetadata("selector", options.selector, target);
};
}
@Component({ selector: "app-user" })
class UserComponent { }
// NestJS 스타일
@Controller("users")
class UserController {
@Get(":id")
findOne(@Param("id") id: string) { ... }
}
실전 권고. 새로 만드는 일반 코드는 데코레이터를 거의 안 씁니다 — 단순 함수·고차 함수로 충분. 프레임워크가 요구할 때(Angular·NestJS·TypeORM·class-validator) 만. 데코레이터는 강력하지만 "마법" 처럼 동작이 숨겨져 디버깅이 어려워요.
한 표 — 데코레이터 4종 (ES2024)
| 대상 | 위치 | context.kind |
|---|---|---|
| class | class 위 | "class" |
| method | 메서드 위 | "method" |
| accessor (get/set) | accessor 위 | "accessor", "getter", "setter" |
| field (속성) | 필드 위 | "field" |
17편 — namespace 와 .d.ts
JS 라이브러리에 타입 입히기. DefinitelyTyped(@types/*) 의 동작 방식.
📚 쉽게 배우는 타입스크립트 교재
이전: 15편 비동기 타입 · 현재: 16편 (고급 시작) · 다음 → 17편 .d.ts · 진행: 16/20
이전: 15편 비동기 타입 · 현재: 16편 (고급 시작) · 다음 → 17편 .d.ts · 진행: 16/20