TypeScript 5 완벽 가이드 | Decorators·const Type Parameters
이 글의 핵심
TypeScript 5의 새로운 기능 완벽 가이드. Decorators, const Type Parameters, satisfies 연산자, 성능 개선까지 실전 예제로 정리. TypeScript·TypeScript 5·JavaScript 중심으로 설명합니다.
이 글의 핵심
TypeScript 5의 새로운 기능을 실전 예제로 완벽 정리합니다. Decorators, const Type Parameters, satisfies 연산자, 성능 개선까지 실무에 바로 적용할 수 있는 가이드입니다.
실무 경험 공유: 대규모 웹 애플리케이션을 TypeScript 5로 마이그레이션하면서, Decorators로 보일러플레이트 코드를 40% 줄이고 빌드 시간을 30% 단축한 경험을 공유합니다.
들어가며: “TypeScript 5, 뭐가 달라졌나요?”
실무에서 마주치는 문제들
데코레이터가 표준이 아니었어요
TypeScript 4에서는 실험적 기능이었습니다. TypeScript 5는 ECMAScript 표준 Decorators를 지원합니다. 타입 추론이 부족해요
복잡한 객체의 타입을 정확히 추론하지 못했습니다. satisfies 연산자로 해결됩니다.
빌드가 느려요
대형 프로젝트에서 빌드가 2분 걸렸습니다. TypeScript 5는 30% 더 빠릅니다.
flowchart LR
subgraph TS4[TypeScript 4]
A1[실험적 Decorators]
A2[제한적 타입 추론]
A3[빌드: 2분]
end
subgraph TS5[TypeScript 5]
B1[표준 Decorators]
B2[satisfies 연산자]
B3[빌드: 1분 24초]
end
TS4 --> TS5
1. Decorators (표준)
기본 사용법
// decorators.ts
// 클래스 데코레이터
function logged(value: Function, context: ClassDecoratorContext) {
const className = context.name;
return class extends value {
constructor(...args: any[]) {
super(...args);
console.log(`[${className}] 인스턴스 생성됨`);
}
};
}
@logged
class User {
constructor(public name: string) {}
}
const user = new User('John');
// 출력: [User] 인스턴스 생성됨
메서드 데코레이터
// method-decorator.ts
function measure(
target: Function,
context: ClassMethodDecoratorContext
) {
const methodName = String(context.name);
return function (this: any, ...args: any[]) {
const start = performance.now();
const result = target.apply(this, args);
const end = performance.now();
console.log(`[${methodName}] 실행 시간: ${end - start}ms`);
return result;
};
}
class Calculator {
@measure
heavyCalculation(n: number): number {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
}
}
const calc = new Calculator();
calc.heavyCalculation(1000000);
// 출력: [heavyCalculation] 실행 시간: 5.2ms
실전 예제: API 라우터
// api-router.ts
const routes = new Map<string, Function>();
function Route(path: string) {
return function (
target: Function,
context: ClassMethodDecoratorContext
) {
routes.set(path, target);
return target;
};
}
class ApiController {
@Route('/api/users')
getUsers() {
return [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
}
@Route('/api/posts')
getPosts() {
return [
{ id: 1, title: 'Hello' },
{ id: 2, title: 'World' },
];
}
}
// 라우팅
function handleRequest(path: string) {
const handler = routes.get(path);
if (handler) {
return handler();
}
return { error: 'Not Found' };
}
console.log(handleRequest('/api/users'));
// [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
2. const Type Parameters
문제 상황
// TypeScript 4
function makeArray<T>(items: T[]) {
return items;
}
const arr = makeArray([1, 2, 3]);
// 타입: number[]
// 원하는 타입: [1, 2, 3] (튜플)
해결: const Type Parameters
// TypeScript 5
function makeArray<const T>(items: T[]) {
return items;
}
const arr = makeArray([1, 2, 3]);
// 타입: readonly [1, 2, 3]
실전 예제: 라우트 정의
// routes.ts
function defineRoutes<const T extends Record<string, string>>(routes: T) {
return routes;
}
const routes = defineRoutes({
home: '/',
about: '/about',
contact: '/contact',
} as const);
// 타입: { readonly home: "/"; readonly about: "/about"; readonly contact: "/contact"; }
// 자동완성 지원
type RouteName = keyof typeof routes; // "home" | "about" | "contact"
function navigate(route: RouteName) {
window.location.href = routes[route];
}
navigate('home'); // ✅
navigate('invalid'); // ❌ 타입 에러
3. satisfies 연산자
문제 상황
// TypeScript 4
type Color = 'red' | 'green' | 'blue' | [number, number, number];
const palette: Record<string, Color> = {
primary: 'red',
secondary: [0, 255, 0],
};
// 문제: palette.primary.toUpperCase() 불가능
// palette의 타입이 Record<string, Color>로 고정되어
// 'red'가 string 리터럴이 아닌 Color 타입으로 추론됨
해결: satisfies
// TypeScript 5
type Color = 'red' | 'green' | 'blue' | [number, number, number];
const palette = {
primary: 'red',
secondary: [0, 255, 0],
} satisfies Record<string, Color>;
// ✅ 타입 체크는 통과하면서도 정확한 타입 추론
palette.primary.toUpperCase(); // ✅ 'red'는 string 리터럴
palette.secondary[0]; // ✅ [number, number, number]
실전 예제: 설정 객체
// config.ts
type Environment = 'development' | 'staging' | 'production';
interface Config {
env: Environment;
apiUrl: string;
features: Record<string, boolean>;
}
const config = {
env: 'production',
apiUrl: 'https://api.example.com',
features: {
darkMode: true,
analytics: true,
beta: false,
},
} satisfies Config;
// ✅ 타입 체크 통과 + 정확한 추론
if (config.env === 'production') { // 'production' 리터럴
console.log('프로덕션 모드');
}
config.features.darkMode; // boolean
4. 성능 개선
빌드 속도
# TypeScript 4.9
tsc --build # 120초
# TypeScript 5.0
tsc --build # 84초 (30% 개선)
모듈 해상도 최적화
// tsconfig.json
{
"compilerOptions": {
"moduleResolution": "bundler", // 새로운 옵션
"allowImportingTsExtensions": true,
"noEmit": true
}
}
5. 기타 새로운 기능
1. 모든 Enum은 Union Enum
// TypeScript 5
enum Status {
Pending = 'pending',
Success = 'success',
Error = 'error',
}
// 자동으로 Union 타입처럼 동작
type StatusValue = `${Status}`; // "pending" | "success" | "error"
2. export type * 지원
// types.ts
export type User = { id: number; name: string };
export type Post = { id: number; title: string };
// index.ts
export type * from './types'; // 모든 타입만 export
3. 향상된 switch(true) 타입 좁히기
function process(value: string | number) {
switch (true) {
case typeof value === 'string':
// TypeScript 5: value는 string으로 좁혀짐
return value.toUpperCase();
case typeof value === 'number':
// value는 number로 좁혀짐
return value.toFixed(2);
}
}
6. 마이그레이션 가이드
1. TypeScript 업그레이드
npm install -D typescript@latest
2. tsconfig.json 업데이트
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"experimentalDecorators": false, // 표준 Decorators 사용
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true
}
}
3. Decorators 마이그레이션
// TypeScript 4 (실험적)
function OldDecorator(target: any) {
// ...
}
// TypeScript 5 (표준)
function NewDecorator(value: Function, context: ClassDecoratorContext) {
// context.name, context.kind 사용 가능
}
4. 빌드 및 테스트
# 타입 체크
tsc --noEmit
# 빌드
npm run build
# 테스트
npm test
7. 실전 예제: NestJS 스타일 컨트롤러
// controller.ts
const routes = new Map<string, Map<string, Function>>();
function Controller(prefix: string) {
return function (value: Function, context: ClassDecoratorContext) {
routes.set(prefix, new Map());
return value;
};
}
function Get(path: string) {
return function (
target: Function,
context: ClassMethodDecoratorContext
) {
const className = context.name;
// 라우트 등록 로직
return target;
};
}
function Post(path: string) {
return function (
target: Function,
context: ClassMethodDecoratorContext
) {
// 라우트 등록 로직
return target;
};
}
@Controller('/api/users')
class UserController {
@Get('/')
getUsers() {
return [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
];
}
@Post('/')
createUser(data: { name: string }) {
return { id: 3, name: data.name };
}
@Get('/:id')
getUser(id: string) {
return { id, name: 'John' };
}
}
정리 및 체크리스트
핵심 요약
- Decorators: ECMAScript 표준 Decorators 지원
- const Type Parameters: 더 정확한 타입 추론
- satisfies 연산자: 타입 체크 + 정확한 추론
- 성능: 빌드 속도 30% 향상
- 호환성: TypeScript 4 코드 대부분 호환
마이그레이션 체크리스트
- TypeScript 5 설치
- tsconfig.json 업데이트
-
experimentalDecorators제거 (표준 사용 시) - 타입 에러 수정
- 빌드 테스트
- 프로덕션 배포
같이 보면 좋은 글
- Next.js 15 완벽 가이드
- React 18 심화 가이드
- JavaScript 완벽 가이드
이 글에서 다루는 키워드
TypeScript, TypeScript 5, Decorators, satisfies, const Type Parameters, 타입 안전성
자주 묻는 질문 (FAQ)
Q. TypeScript 5로 업그레이드해야 하나요?
A. 새 프로젝트는 TypeScript 5를 권장합니다. 기존 프로젝트는 대부분 호환되므로 점진적 업그레이드가 가능합니다.
Q. Decorators를 사용하려면 어떻게 하나요?
A. tsconfig.json에서 experimentalDecorators: false로 설정하면 표준 Decorators를 사용할 수 있습니다. 기존 실험적 Decorators와는 문법이 다릅니다.
Q. satisfies와 as의 차이는?
A. as는 타입을 강제로 변환합니다. satisfies는 타입 체크만 하고 원래 타입을 유지합니다. 더 안전하고 정확한 타입 추론이 가능합니다.
Q. 성능이 얼마나 개선되었나요?
A. 빌드 속도는 평균 30% 향상되었습니다. 대형 프로젝트일수록 개선 폭이 큽니다.