Frontend/Typescript

유틸리티 타입(Utility Types) 활용기: Partial, Pick, Omit, Readonly 등 실무 필수 타입

미니임 2026. 2. 19. 22:24

타입스크립트를 사용하다 보면 이미 정의된 인터페이스나 타입을 조금만 수정해서 재사용하고 싶은 경우가 많습니다. 이때 유틸리티 타입을 활용하면 코드 중복을 획기적으로 줄이고 타입 안정성을 유지할 수 있습니다.

1. Partial<T>: 모든 프로퍼티를 선택 사항으로

Partial 타입은 인터페이스의 모든 프로퍼티를 optional(?)로 변환합니다. 주로 수정(Update) 기능을 구현할 때 유용합니다.

실무 예제: 프로필 정보 업데이트

사용자의 전체 정보 중 변경된 부분만 서버에 보낼 때 사용합니다.

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Partial<User>를 사용하면 모든 속성이 있어도 되고 없어도 되는 상태가 됩니다.
function updateUser(id: number, fieldsToUpdate: Partial<User>) {
  console.log(`${id}번 유저의 정보를 업데이트합니다:`, fieldsToUpdate);
}

// 모든 필드를 채울 필요 없이 원하는 필드만 전달 가능
updateUser(1, { name: "John Doe" });
updateUser(2, { email: "new@example.com", age: 30 });

2. Pick<T, K>: 원하는 프로퍼티만 쏙쏙

Pick은 인터페이스에서 특정 키(K)들만 선택하여 새로운 타입을 만듭니다. 요약 정보나 목록을 보여줄 때 자주 사용됩니다.

실무 예제: 유저 목록 미리보기

유저의 전체 정보 중 이름과 이메일만 필요한 요약 리스트를 만들 때 유용합니다.

interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  stock: number;
}

// Product에서 name과 price만 선택합니다.
type ProductPreview = Pick<Product, "name" | "price">;

const items: ProductPreview[] = [
  { name: "기계식 키보드", price: 150000 },
  { name: "무선 마우스", price: 80000 }
];

3. Omit<T, K>: 특정 프로퍼티만 제외하기

Omit은 Pick의 반대입니다. 특정 키(K)를 제외한 나머지 모든 프로퍼티를 포함하는 타입을 만듭니다. 민감한 정보(비밀번호 등)를 제거할 때 필수적입니다.

실무 예제: 보안을 위한 데이터 필터링

유저 정보를 클라이언트에 보낼 때 비밀번호나 내부 관리용 데이터는 숨겨야 합니다.

interface UserAccount {
  uuid: string;
  username: string;
  passwordHash: string;
  lastLogin: Date;
  isAdmin: boolean;
}

// 비밀번호 해시와 관리자 여부만 뺀 타입을 만듭니다.
type PublicUserProfile = Omit<UserAccount, "passwordHash" | "isAdmin">;

function getProfile(user: UserAccount): PublicUserProfile {
  const { passwordHash, isAdmin, ...publicInfo } = user;
  return publicInfo;
}

4. Readonly<T>: 수정을 허용하지 않는 타입

Readonly는 모든 프로퍼티를 readonly로 만듭니다. **설정값(Config)**이나 **상태(State)**가 외부에서 실수로 변경되는 것을 방지합니다.

실무 예제: 시스템 설정값 보호

interface AppConfig {
  apiKey: string;
  endpoint: string;
  retryCount: number;
}

const config: Readonly = {
  apiKey: "SECRET_KEY_123",
  endpoint: "[https://api.server.com](https://api.server.com)",
  retryCount: 5
};

// Error: Cannot assign to 'apiKey' because it is a read-only property.
// config.apiKey = "NEW_KEY"; 

5. Record<K, T>: 키-값 쌍의 맵 구조 정의

Record는 속성 키를 K로, 속성 값을 T로 하는 타입을 만듭니다. **객체를 매핑(Mapping)**하거나 딕셔너리 구조를 만들 때 매우 강력합니다.

실무 예제: 에러 코드 메시지 매핑

type ErrorCode = "404" | "500" | "403";

const errorMessages: Record<ErrorCode, string> = {
  "404": "페이지를 찾을 수 없습니다.",
  "500": "서버 내부 오류가 발생했습니다.",
  "403": "접근 권한이 없습니다."
};

console.log(errorMessages["404"]);

6. Required<T>: 모든 프로퍼티를 필수값으로

Partial의 반대입니다. 원래 인터페이스에 선택 사항(?)이 있었더라도, 모든 값을 강제로 입력하게 만듭니다.

실무 예제: 필수 설정 검증

interface GraphConfig {
  width?: number;
  height?: number;
  color?: string;
}

// 모든 값이 반드시 있어야 하는 상황에서 사용
const fullConfig: Required<GraphConfig> = {
  width: 100,
  height: 100,
  color: "blue"
};

실무 팁: 유틸리티 타입의 조합

유틸리티 타입은 중첩해서 사용할 수 있습니다.

interface Article {
  id: number;
  title: string;
  content: string;
  author: string;
  createdAt: Date;
}

// '아이디'와 '작성일'은 빼고, 나머지는 선택 사항으로 만들어서 수정용 타입 생성
type UpdateArticleDto = Partial<Omit<Article, "id" | "createdAt">>;

const updateData: UpdateArticleDto = {
  title: "제목만 수정합니다."
};

요약

타입역할주 활용처

Partial 모든 속성을 선택 사항으로 변경 수정 요청(Patch)
Pick 특정 속성만 골라서 타입 생성 목록, 요약 정보
Omit 특정 속성만 빼고 타입 생성 민감 정보 제거
Readonly 모든 속성을 읽기 전용으로 변경 설정값, 상수
Record 키-값 쌍의 집합 정의 딕셔너리, 매핑 테이블

유틸리티 타입을 적절히 활용하면 중복되는 인터페이스 선언을 줄일 수 있을 뿐만 아니라, 원본 타입이 변경되었을 때 파생된 타입들도 자동으로 동기화되는 효과를 얻을 수 있습니다.

반응형