티스토리 뷰

타입스크립트를 사용하다 보면 비슷한 구조의 인터페이스를 여러 개 만들어야 하는 상황을 마주하게 됩니다. 예를 들어, 모든 필드가 필수인 타입이 있고, 이를 수정하기 위해 모든 필드가 선택 사항(?)인 타입이 필요한 경우입니다.
이때 **맵드 타입(Mapped Types)**을 사용하면 기존 타입을 가공하여 중복 없이 새로운 타입을 정의할 수 있습니다.
1. 맵드 타입이란?
맵드 타입은 자바스크립트 배열의 map 함수처럼, 타입 내의 프로퍼티들을 순회하며 새로운 프로퍼티 타입을 생성하는 문법입니다.
기본 문법
type NewType = {
[K in keyof ExistingType]: NewPropertyType;
};
- keyof ExistingType: 기존 타입의 모든 키(Property Names)를 가져옵니다.
- K in ...: 가져온 키들을 하나씩 순회합니다 (마치 for...in 루프와 같습니다).
- NewPropertyType: 각 키에 할당할 새로운 타입을 지정합니다.
2. 기초 예제: 모든 필드 변환하기
모든 값이 boolean인 타입을 string으로 변환하는 간단한 예제를 보겠습니다.
interface AppFeatures {
isDarkMode: boolean;
isLoggedIn: boolean;
hasNotifications: boolean;
}
// 모든 필드를 string으로 바꾸는 맵드 타입
type FeatureLabels = {
[K in keyof AppFeatures]: string;
};
/*
결과 타입:
type FeatureLabels = {
isDarkMode: string;
isLoggedIn: string;
hasNotifications: string;
}
*/
const labels: FeatureLabels = {
isDarkMode: "다크 모드 활성화 여부",
isLoggedIn: "로그인 여부",
hasNotifications: "알림 수신 여부"
};
3. 매핑 수정자(Mapping Modifiers) 활용하기
맵드 타입을 정의할 때 프로퍼티 앞에 readonly를 붙이거나, 뒤에 ?를 붙여 속성을 변경할 수 있습니다. 특히 - 기호를 사용하면 기존 속성을 제거할 수도 있습니다.
예제 1: 모든 필드를 읽기 전용 및 선택 사항으로 변경 (+)
interface User {
id: number;
name: string;
}
// readonly와 ?를 추가
type ReadonlyPartialUser = {
readonly [K in keyof User]?: User[K];
};
/*
결과:
{
readonly id?: number;
readonly name?: string;
}
*/
예제 2: 속성 제거하기 (-)
기존 타입에 있던 readonly나 ?를 강제로 제거하고 싶은 경우 -를 사용합니다.
interface DraftArticle {
id?: number;
title?: string;
readonly createdAt: Date;
}
// 선택 사항(?)과 readonly를 모두 제거하여 필수값으로 변경
type RequiredArticle = {
-readonly [K in keyof DraftArticle]-?: DraftArticle[K];
};
/*
결과:
{
id: number;
title: string;
createdAt: Date;
}
*/
4. 실무형 예제: 키 재매핑 (Key Remapping via as)
타입스크립트 4.1 버전부터 도입된 as 절을 사용하면 키의 이름을 변경하거나 필터링할 수 있습니다. 템플릿 리터럴 타입과 함께 사용하면 매우 강력합니다.
예제 3: Getter 메서드 타입 생성하기
인터페이스의 속성 이름을 기반으로 get이 붙은 메서드 리스트를 자동으로 생성해 봅시다.
interface Person {
name: string;
age: number;
location: string;
}
// 키의 첫 글자를 대문자로 바꾸고 앞에 'get'을 붙임
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type PersonGetters = Getters<Person>;
/*
결과:
{
getName: () => string;
getAge: () => number;
getLocation: () => string;
}
*/
예제 4: 특정 타입의 필드만 필터링하기
as 절에서 never를 반환하면 해당 키는 결과 타입에서 제외됩니다.
interface MixedData {
id: number;
name: string;
isActive: boolean;
score: number;
}
// 오직 number 타입인 속성만 남기기
type OnlyNumbers<T> = {
[K in keyof T as T[K] extends number ? K : never]: T[K];
};
type NumericData = OnlyNumbers<MixedData>;
/*
결과:
{
id: number;
score: number;
}
*/
5. 실무 활용: API 응답 및 폼 데이터 검증
실무에서는 API로부터 받은 데이터를 폼 데이터로 변환하거나, 각 필드에 대한 에러 메시지 타입을 정의할 때 자주 쓰입니다.
interface RegistrationForm {
email: string;
password: string;
nickname: string;
}
// 각 필드의 유효성 검사 결과(에러 메시지)를 담는 타입
type ValidationResult<T> = {
[K in keyof T]: {
isValid: boolean;
message: string;
};
};
const formErrors: ValidationResult<RegistrationForm> = {
email: { isValid: false, message: "이메일 형식이 아닙니다." },
password: { isValid: true, message: "" },
nickname: { isValid: true, message: "" }
};
요약
맵드 타입은 DRY(Don't Repeat Yourself) 원칙을 타입 시스템에 적용한 결과입니다.
- 기본 순회: [K in keyof T]를 통해 기존 타입을 복제하거나 변형합니다.
- 수정자 활용: readonly, ?를 추가하거나 -를 통해 제거합니다.
- 키 재매핑: as와 템플릿 리터럴을 사용해 프로퍼티 이름을 동적으로 생성합니다.
- 필터링: never와 조건부 타입을 조합해 특정 조건의 키만 추출합니다.
맵드 타입을 마스터하면 프로젝트의 스케일이 커져도 타입 관리가 매우 유연해지며, 휴먼 에러를 획기적으로 줄일 수 있습니다.
'Frontend > Typescript' 카테고리의 다른 글
| TypeScript 선언 파일(.d.ts) 작성법: 타입 없는 외부 라이브러리 마스터하기 (0) | 2026.02.20 |
|---|---|
| 맵드 타입(Mapped Types)과 조건부 타입(Conditional Types) (0) | 2026.02.20 |
| 유틸리티 타입(Utility Types) 활용기: Partial, Pick, Omit, Readonly 등 실무 필수 타입 (0) | 2026.02.19 |
| TypeScript 객체지향 마스터하기: 인터페이스와 클래스를 활용한 추상화 구현 (0) | 2026.02.19 |
| TypeScript 열거형(Enum) vs const 객체: 성능과 가독성 측면에서의 비교 분석 (0) | 2026.02.19 |
- Total
- Today
- Yesterday
- HTML
- prompt engineering
- 협력
- HBM
- CSS
- CSR
- 엣지컴퓨팅
- 카카오
- MSA
- 스마트안경
- on-device ai
- SSR
- sLLM
- AI
- Rag
- java
- react
- 구글
- Javascript
- TypeScript
- It용어
- 멀티모달
- 웹기초
- Nextjs
- LLM
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |