반응형
Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 | 31 |
Tags
- ArrayList
- Array
- vscode
- CSS
- list
- 자바스크립트
- input
- 단위테스트
- Visual Studio Code
- Java
- math
- string
- 자바문법
- 정규식
- junit5
- javascript
- SpringBoot
- java테스트
- Eclipse
- 배열
- 문자열
- 인텔리제이
- 스프링부트
- HashMap
- 테스트자동화
- 자바
- js
- junit
- html
- IntelliJ
Archives
- Today
- Total
어제 오늘 내일
[TypeScript] 10. 제네릭 기초 — 재사용 가능한 타입 만들기 본문
이 글의 목표: 많은 입문자가 벽을 느끼는 제네릭을, "타입의 매개변수"라는 관점에서 쉽게 이해합니다.
문제: any는 타입 정보를 잃는다
배열의 첫 원소를 반환하는 함수를 만든다고 합시다.
// any를 쓰면 반환 타입도 any가 되어, 이후 타입 검사를 다 잃는다
function first(arr: any[]): any {
return arr[0];
}
const n = first([1, 2, 3]); // n의 타입: any 😢
n.toUpperCase(); // 컴파일러가 막지 못함 (실행 시 오류 발생)
타입마다 함수를 따로 만드는 것도 비효율적입니다.
function firstNumber(arr: number[]): number { return arr[0]; }
function firstString(arr: string[]): string { return arr[0]; }
// ... 타입마다 반복? 말이 안 된다
해결: 제네릭 — 타입을 매개변수로 받자
<T>는 "지금은 모르지만, 호출할 때 정해질 타입"을 나타내는 자리표시자입니다.
// T는 타입 매개변수. 함수를 부를 때 실제 타입으로 채워진다
function first<T>(arr: T[]): T {
return arr[0];
}
const n = first([1, 2, 3]); // T = number → n은 number
const s = first(["a", "b"]); // T = string → s는 string
n.toFixed(2); // ✅ number 메서드 사용 가능
s.toUpperCase(); // ✅ string 메서드 사용 가능
// n.toUpperCase(); // ❌ 이제 제대로 막아준다
값을 넣는 게 아니라 타입을 넣는다는 게 핵심입니다. T는 함수의 인자로부터 자동 추론됩니다.
타입을 직접 지정할 수도 있다
앞선 예제에서는 인자([1, 2, 3])를 보고 T가 자동으로 정해졌습니다. 그런데 인자만 봐서는 타입을 알 수 없을 때가 있습니다. 대표적인 게 빈 배열입니다.
// 빈 배열 []에는 원소가 없어, TypeScript가 T를 추론할 근거가 없다
const auto = first([]); // T가 정해지지 않아 원치 않는 타입이 될 수 있다
// 이럴 때 함수 이름 뒤 <>에 타입을 직접 넣어 T를 확정한다
const empty = first<string>([]);
// first<string>은 "이번 호출에서 T는 string으로 쳐라"는 뜻
// 따라서 empty의 타입은 string이 된다
first<string>([])에서 <string>이 바로 "타입 인자(type argument)"입니다. 값 인자를 () 안에 넣듯이, 타입 인자는 <> 안에 넣습니다. 인자로부터 추론이 되는 경우엔 생략하고, 추론이 안 되거나 원하는 타입과 다를 때만 이렇게 명시하면 됩니다.
여러 타입 매개변수
필요하면 여러 개를 쓸 수 있습니다.
// 두 값을 받아 튜플로 묶는다
function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
const result = pair("나이", 30); // 타입: [string, number]
관례적으로 T, U, V 또는 TKey, TValue처럼 이름을 짓습니다.
제네릭 함수 실전 예제
// 배열을 받아 각 원소를 변환한 새 배열을 반환 (map의 단순 버전)
function mapArray<T, U>(arr: T[], fn: (item: T) => U): U[] {
const result: U[] = [];
for (const item of arr) {
result.push(fn(item)); // fn의 반환 타입 U가 결과 배열의 원소 타입
}
return result;
}
// numbers를 문자열 배열로 변환
const lengths = mapArray([1, 22, 333], (n) => n.toString());
// T = number, U = string → lengths는 string[]
제네릭 타입(인터페이스/타입 별칭)
함수뿐 아니라 타입 정의에도 제네릭을 쓸 수 있습니다.
// 어떤 타입의 데이터든 담을 수 있는 API 응답 래퍼
interface ApiResponse<T> {
status: number;
data: T; // T가 실제 데이터 타입으로 채워진다
timestamp: number;
}
type User = { id: number; name: string };
// ApiResponse를 User로 특수화
const res: ApiResponse<User> = {
status: 200,
data: { id: 1, name: "철수" }, // data는 User 타입으로 검사됨
timestamp: Date.now(),
};
console.log(res.data.name); // ✅ User의 name에 안전하게 접근
제네릭 클래스
// 어떤 타입이든 담는 스택 자료구조
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop(); // number | undefined
// numberStack.push("x"); // ❌ number만 허용
제네릭은 "재사용 + 타입 안전"의 균형
제네릭이 없으면 둘 중 하나를 포기해야 합니다.
any: 재사용은 되지만 타입 안전을 잃는다.- 타입별 중복 함수: 안전하지만 재사용이 안 된다.
제네릭은 둘 다 얻게 해줍니다.
정리
- 제네릭
<T>는 "타입을 매개변수로 받는" 문법이다. - 인자로부터 타입이 자동 추론되며, 필요하면
<>로 명시한다. - 함수, 타입/인터페이스, 클래스 모두 제네릭으로 만들 수 있다.
any의 유연함과 구체 타입의 안전함을 동시에 얻는 도구다.
다음 글에서는 제네릭에 조건을 거는 "제약(constraints)"을 다룹니다.
반응형
'IT > TypeScript' 카테고리의 다른 글
| [TypeScript] 9. 구조적 타이핑 — TypeScript가 타입을 비교하는 방식 (0) | 2026.08.10 |
|---|---|
| [TypeScript] 8. 함수와 타입 — 오버로드, 선택적 매개변수, this (0) | 2026.08.10 |
| [TypeScript] 7. 타입 좁히기(Narrowing) (0) | 2026.08.10 |
| [TypeScript] 6. 유니온과 인터섹션, 그리고 리터럴 타입 (0) | 2026.08.10 |
| [TypeScript] 5. interface vs type — 무엇을 언제 쓸까 (0) | 2026.08.09 |
Comments