TypeScript 5 완벽 가이드 | Decorators·const Type Parameters·satisfies 연산자
이 글의 핵심
TypeScript 5의 새로운 기능을 실전 예제로 완벽 정리합니다. Decorators, const Type Parameters, satisfies 연산자, 성능 개선까지 실무에 바로 적용할 수 있는 가이드입니다.
실무 경험 공유: 대규모 웹 애플리케이션을 TypeScript 5로 마이그레이션하면서, Decorators로 보일러플레이트 코드를 40% 줄이고 빌드 시간을 30% 단축한 경험을 공유합니다.
들어가며: “TypeScript 5, 뭐가 달라졌나요?”
실무 문제 시나리오
시나리오 1: 데코레이터가 표준이 아니었어요
TypeScript 4에서는 실험적 기능이었습니다. TypeScript 5는 ECMAScript 표준 Decorators를 지원합니다.
시나리오 2: 타입 추론이 부족해요
복잡한 객체의 타입을 정확히 추론하지 못했습니다. satisfies 연산자로 해결됩니다.
시나리오 3: 빌드가 느려요
대형 프로젝트에서 빌드가 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% 향상되었습니다. 대형 프로젝트일수록 개선 폭이 큽니다.