Search for a command to run...
제네릭은 타입스크립트의 매우 강력한 기능 중 하나로, 컴포넌트나 함수를 다양한 타입과 함께 동작하도록 만들어 재사용성을 극대화합니다. 타입을 마치 함수의 파라미터처럼 사용하는 것이라고 생각할 수 있습니다.
만약 여러 타입을 받을 수 있는 함수를 만들어야 한다면 어떻게 해야 할까요? any 타입을 사용할 수도 있지만, 이는 타입스크립트의 장점을 포기하는 것과 같습니다. 타입 안정성을 잃기 때문이죠.
// any를 사용한 좋지 않은 예
function getFirstElement(arr: any[]) {
return arr[0]; // 반환 타입이 any가 되어 타입 추론이 불가능
}
이때 제네릭을 사용하면 타입 안정성을 유지하면서 유연한 함수를 만들 수 있습니다.
제네릭은 <T>와 같은 형태로 타입을 변수처럼 사용합니다. 관례적으로 T (Type), E (Element), K (Key), V (Value) 등을 사용합니다.
function getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
// string[]을 인자로 전달 -> 반환 타입이 string으로 추론됨
const fruits = ["Apple", "Banana", "Cherry"];
const firstFruit = getFirstElement(fruits); // firstFruit는 string | undefined 타입
// number[]를 인자로 전달 -> 반환 타입이 number로 추론됨
const numbers = [1, 2, 3];
const firstNumber = getFirstElement(numbers); // firstNumber는 number | undefined 타입
위 예제에서 T는 함수가 호출될 때 전달된 인자의 타입으로 결정됩니다. 이로써 getFirstElement 함수는 어떤 타입의 배열이든 처리할 수 있는 범용적인 함수가 되었습니다.
제네릭은 함수뿐만 아니라 인터페이스나 클래스에도 적용할 수 있습니다.
// 제네릭 인터페이스
interface ApiResponse<T> {
data: T;
status: "success" | "error";
message?: string;
}
// 사용자 정보를 담는 API 응답
const userResponse: ApiResponse<{ id: number; name: string; }> = {
data: { id: 1, name: "lunarcat7" },
status: "success"
};
// 포스트 목록을 담는 API 응답
const postsResponse: ApiResponse<Array<{ title: string; content: string }>> = {
data: [
{ title: "첫 포스트", content: "..." },
{ title: "두 번째 포스트", content: "..." },
],
status: "success"
};
이처럼 제네릭을 활용하면, 반복적인 타입 선언을 줄이고 코드의 중복을 제거하며, 매우 유연하고 재사용 가능한 컴포넌트를 설계할 수 있습니다. 제네릭은 타입스크립트를 한 단계 더 깊이 있게 사용하는 핵심 열쇠입니다.