타입스크립트 Utility Types로 반복 코드 줄이기
문제 상황
레거시 프로젝트에 TypeScript를 점진적으로 도입하는 중이었다. API 응답 타입을 정의하다 보니 비슷한 형태의 타입을 반복해서 작성하게 되었다.
interface User {
id: number;
email: string;
name: string;
createdAt: string;
}
interface UserCreateRequest {
email: string;
name: string;
}
interface UserUpdateRequest {
email?: string;
name?: string;
}
이런 식으로 CRUD마다 타입을 새로 정의하니 필드가 추가될 때마다 여러 곳을 수정해야 했다.
Utility Types 활용
TypeScript 내장 유틸리티 타입을 사용해 기본 타입에서 파생시키는 방식으로 변경했다.
interface User {
id: number;
email: string;
name: string;
createdAt: string;
}
type UserCreateRequest = Omit<User, 'id' | 'createdAt'>;
type UserUpdateRequest = Partial<UserCreateRequest>;
type UserResponse = Pick<User, 'id' | 'email' | 'name'>;
특히 Omit과 Pick을 조합하니 의도가 명확해졌다. 생성 요청에는 id가 없어야 하고, 업데이트는 부분 수정이 가능하다는 것이 타입 정의만으로 드러났다.
커스텀 유틸리티 타입
프로젝트 전체에서 자주 쓰는 패턴은 별도 유틸리티로 만들었다.
type ApiResponse<T> = {
data: T;
error: string | null;
timestamp: number;
};
type Nullable<T> = { [K in keyof T]: T[K] | null };
ApiResponse로 모든 API 응답을 래핑하니 에러 핸들링 로직이 일관되게 작성되었다.
결과
- 타입 정의 코드량 약 30% 감소
- User 인터페이스 수정 시 파생 타입 자동 반영
- 새 팀원이 타입 구조 파악하는 시간 단축
유틸리티 타입은 TypeScript 3.5부터 대부분 안정화되었는데, 생각보다 활용도가 높았다. DRY 원칙이 타입 시스템에도 적용된다는 걸 체감했다.