TypeScript 유틸리티 타입, 내 일상 속에서 이해하기 Ⅰ

jinew·2025년 3월 21일

🍏 TypeScript

목록 보기
7/7
post-thumbnail

📄 유틸리티 타입이란?

TypeScript의 유틸리티 타입은 이름 그대로 '유용한 도구'이다. 우리가 주방에서 요리할 때 다양한 도구(칼, 도마, 믹서기 등)를 사용하듯이, TypeScript에서도 타입을 더 편리하게 조작할 수 있는 여러 도구가 내장되어 있다.

이 유틸리티 타입들은 기존 타입을 변환하여 새로운 타입을 만들어 내는데, 이는 마치 우리가 원재료(감자)를 다양한 조리기구로 변형(감자칩, 매쉬드 포테이토, 감자튀김)하는 것과 비슷하다.

그럼 자주 사용되는 유틸리티 타입들을 하나씩 살펴보자.


1. Partial<T> - 모든 속성을 선택적으로 만들기

Partial은 객체 타입의 모든 속성을 선택적(optional)으로 만든다.

💭 이는 마치 '장보기 목록의 선택사항'과 같다.

장보기 목록을 생각해 보자. 필수로 사야 할 항목도 있지만, 있으면 좋지만 없어도 괜찮은 선택적인 항목들도 있다.

// 원래 장보기 목록 타입 (모든 항목이 필수)
interface ShoppingList {
  milk: number;  // 우유 몇 개
  eggs: number;  // 계란 몇 개
  bread: number; // 빵 몇 개
}

// Partial을 적용한 장보기 목록 (모든 항목이 선택적)
type OptionalShoppingList = Partial<ShoppingList>;

// 이제 일부 항목만 포함해도 됩니다
const myList: OptionalShoppingList = {
  milk: 1
  // eggs와 bread는 선택적이므로 없어도 에러가 발생하지 않음
};

✅ 실제 사용 예시

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

// 일부 정보만 업데이트할 수 있는 함수
function updateUser(userId: number, updates: Partial<User>) {
  // 데이터베이스에서 사용자를 찾아 updates 객체의 속성만 업데이트
}

// 사용 예시
updateUser(123, { age: 30 });  // 나이만 업데이트
updateUser(456, { name: "김철수", email: "kim@example.com" });  // 이름과 이메일 업데이트
  • 사용자 정보 업데이트 함수를 생각해 보자. 사용자가 모든 정보를 한 번에 업데이트하는 것이 아니라 일부 정보만 업데이트할 수 있게 하고 싶을 때 Partial을 사용한다.

2. Required<T> - 모든 속성을 필수로 만들기

RequiredPartial의 반대로, 객체 타입의 모든 선택적 속성을 필수로 만든다.

💭 이는 마치 '입학 서류 제출'과 같다.

대학 입학 서류를 생각해 보자. 원래는 추천서가 선택사항이었는데, 정책이 바뀌어서 모든 서류가 필수가 되었다.

// 원래 입학 서류 타입 (일부 선택적)
interface AdmissionDocuments {
  application: string;
  transcript: string;
  recommendationLetter?: string;  // 선택적
}

// Required를 적용한 입학 서류 (모두 필수)
type RequiredDocuments = Required<AdmissionDocuments>;

// 이제 모든 항목이 필수!
const myDocuments: RequiredDocuments = {
  application: "지원서.pdf",
  transcript: "성적표.pdf",
  // recommendationLetter가 없으면 에러 발생
  recommendationLetter: "추천서.pdf"
};

3. Pick<T, K> - 특정 속성만 선택하기

Pick은 객체 타입에서 특정 속성만 선택하여 새로운 타입을 만든다.

💭 이는 마치 '메뉴판을 보며 메뉴 고르기'와 같다.

// 전체 음식점 메뉴
interface Restaurant {
  burger: string;
  pizza: string;
  pasta: string;
  salad: string;
  dessert: string;
}

// 내가 좋아하는 음식만 선택 (burger, pasta, dessert)
type MyFavorites = Pick<Restaurant, "burger" | "pasta" | "dessert">;

const myMenu: MyFavorites = {
  burger: "치즈버거",
  pasta: "까르보나라",
  dessert: "티라미수"
  // salad와 pizza는 이 타입에 존재하지 않아 포함할 수 없음
};

✅ 실제 사용 예시

interface APIResponse {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
  updatedAt: Date;
  role: string;
  settings: object;
}

// UI에 표시할 사용자 정보만 선택
type UserDisplay = Pick<APIResponse, "id" | "name" | "email">;

// 사용 예시
function displayUser(user: UserDisplay) {
  console.log(`${user.name} (${user.email})`);
}
  • API 응답에서 필요한 데이터만 추출하여 사용할 때 유용하다.

4. Omit<T, K> - 특정 속성 제외하기

OmitPick의 반대로, 객체 타입에서 특정 속성을 제외한 나머지로 새로운 타입을 만든다.

💭 이는 마치 '음식 재료에서 알레르기 성분 제외하기'와 같다.

// 전체 재료 목록
interface Ingredients {
  flour: string;
  sugar: string;
  egg: string;
  milk: string;
  peanut: string;
}

// 땅콩 알레르기가 있어서 땅콩을 제외한 재료
type AllergyFree = Omit<Ingredients, "peanut">;

const safeRecipe: AllergyFree = {
  flour: "밀가루 100g",
  sugar: "설탕 50g",
  egg: "달걀 1개",
  milk: "우유 200ml"
  // peanut은 이 타입에서 제외되었으므로 포함할 수 없음
};

✅ 실제 사용 예시

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  token: string;
}

// 클라이언트에 전송할 안전한 사용자 정보 (비밀번호와 토큰 제외)
type SafeUser = Omit<User, "password" | "token">;

function sendUserToClient(user: User): SafeUser {
  // password와 token을 제외한 정보만 반환
  const { password, token, ...safeUser } = user;
  return safeUser;
}
  • 위와 같이 기존 타입에서 민감한 정보를 제외할 때 사용할 수 있다.

5. Readonly<T> - 모든 속성을 읽기 전용으로 만들기

Readonly는 객체 타입의 모든 속성을 읽기 전용(수정 불가)으로 만든다.

💭 이는 마치 '박물관 전시품'과 같다.

박물관 전시품은 눈으로 보기만 할 수 있고 만지거나 수정할 수 없다.

// 박물관 전시품
interface Exhibit {
  name: string;
  year: number;
  description: string;
}

// 읽기 전용 전시품
type ReadonlyExhibit = Readonly<Exhibit>;

const dinosaur: ReadonlyExhibit = {
  name: "티라노사우루스",
  year: -65000000,
  description: "백악기 후기의 대형 육식 공룡"
};

// 아래 코드는 에러 발생
// dinosaur.name = "트리케라톱스";  // 읽기 전용 속성이므로 수정할 수 없음

✅ 실제 사용 예시

interface Config {
  apiUrl: string;
  timeout: number;
  debug: boolean;
}

function processConfig(config: Readonly<Config>) {
  // config 객체의 속성을 읽을 수만 있고 수정할 수 없음
  console.log(`API URL: ${config.apiUrl}`);
  
  // 아래 코드는 에러 발생
  // config.timeout = 5000;  // 읽기 전용 속성이므로 수정할 수 없음
}
  • 함수에 전달된 객체가 변경되지 않도록 보호할 때 사용한다.



2편에서는 Record, Exclude, Extract, NonNullable, Parameters, ReturnType 에 대한 설명이 이어지겠다!

profile
멈추지만 않으면 도착해 🛫

0개의 댓글