[테스트 글] TypeScript 유틸리티 타입 완벽 가이드
TypeScript의 내장 유틸리티 타입들을 실제 예제와 함께 알아봅니다. Partial, Required, Pick, Omit 등 자주 사용하는 타입들을 마스터하세요.
#typescript
#javascript
#타입시스템
TypeScript를 사용하다 보면 기존 타입을 변형해서 새로운 타입을 만들어야 할 때가 많습니다. 이럴 때 유틸리티 타입을 활용하면 코드 중복을 줄이고 타입 안전성을 높일 수 있습니다.
기본 타입 정의
먼저 예제에서 사용할 기본 타입을 정의하겠습니다.
interface User {
id: number;
name: string;
email: string;
age: number;
createdAt: Date;
}
Partial<T>
모든 프로퍼티를 선택적으로 만듭니다. 업데이트 함수에서 유용합니다.
type PartialUser = Partial<User>;
// 결과:
// {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// createdAt?: Date;
// }
function updateUser(id: number, updates: Partial<User>) {
// 일부 필드만 업데이트 가능
}
updateUser(1, { name: '홍길동' }); // OK
updateUser(1, { age: 25, email: 'new@email.com' }); // OK
Required<T>
Partial의 반대입니다. 모든 프로퍼티를 필수로 만듭니다.
interface Config {
host?: string;
port?: number;
secure?: boolean;
}
type RequiredConfig = Required<Config>;
// 모든 필드가 필수가 됨
const config: RequiredConfig = {
host: 'localhost',
port: 3000,
secure: true,
};
Pick<T, K>
특정 프로퍼티만 선택합니다. API 응답에서 필요한 필드만 추출할 때 유용합니다.
type UserPreview = Pick<User, 'id' | 'name'>;
// 결과:
// {
// id: number;
// name: string;
// }
function getUserPreview(user: User): UserPreview {
return {
id: user.id,
name: user.name,
};
}
Omit<T, K>
Pick의 반대입니다. 특정 프로퍼티를 제외합니다.
type UserWithoutId = Omit<User, 'id' | 'createdAt'>;
// 결과:
// {
// name: string;
// email: string;
// age: number;
// }
// 새 사용자 생성 시 id와 createdAt은 서버에서 생성
function createUser(data: UserWithoutId): User {
return {
...data,
id: generateId(),
createdAt: new Date(),
};
}
Record<K, T>
키 타입과 값 타입을 지정하여 객체 타입을 만듭니다.
type UserRole = 'admin' | 'user' | 'guest';
const rolePermissions: Record<UserRole, string[]> = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read'],
};
// 인덱스 시그니처 대신 사용
type UserMap = Record<string, User>;
Readonly<T>
모든 프로퍼티를 읽기 전용으로 만듭니다.
const frozenUser: Readonly<User> = {
id: 1,
name: '홍길동',
email: 'hong@example.com',
age: 30,
createdAt: new Date(),
};
frozenUser.name = '김철수'; // 컴파일 에러!
실전 활용 예제
여러 유틸리티 타입을 조합하여 사용하는 실전 예제입니다.
// API 응답 타입
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
// 사용자 생성 요청 타입 (id, createdAt 제외)
type CreateUserRequest = Omit<User, 'id' | 'createdAt'>;
// 사용자 업데이트 요청 타입 (모든 필드 선택적, id 제외)
type UpdateUserRequest = Partial<Omit<User, 'id' | 'createdAt'>>;
// 사용자 목록 응답 타입 (일부 필드만)
type UserListItem = Pick<User, 'id' | 'name' | 'email'>;
type UserListResponse = ApiResponse<UserListItem[]>;
// 실제 사용
async function fetchUsers(): Promise<UserListResponse> {
const response = await fetch('/api/users');
return response.json();
}
마무리
유틸리티 타입은 TypeScript의 강력한 기능 중 하나입니다. 적절히 활용하면:
- 코드 중복 감소
- 타입 안전성 향상
- 유지보수성 개선
다음 포스트에서는 조건부 타입과 infer 키워드에 대해 알아보겠습니다.