TypeScript 22강: 고급 타입스크립트 기초 – 제네릭(Generic) 활용법
TypeScript 제네릭(Generic)란 무엇인가?
TypeScript의 제네릭(Generic)은 다양한 타입에 대해 재사용 가능한 컴포넌트, 함수, 클래스 등을 만들 수 있는 강력한 기능입니다. 제네릭은 타입을 매개변수처럼 다룰 수 있게 해줍니다. 이를 통해 더 유연하고 타입 안전성이 높은 코드를 작성할 수 있습니다. 이 글에서는 제네릭의 개념부터 실전 활용법까지 상세히 살펴보겠습니다.
왜 제네릭을 사용하는가?
제네릭을 사용하면 다음과 같은 장점이 있습니다:
- 코드 재사용성 증가: 하나의 함수나 클래스로 여러 타입을 처리 가능
- 타입 안정성 향상: 컴파일 시 타입 검사를 강화하여 오류 방지
- 유연한 API 설계: 다양한 타입과 상황에 대응 가능한 구조 설계
기본 제네릭 함수 작성법
제네릭은 함수 이름 뒤에 <T>
형태로 사용하며, 이 T
는 임의의 타입을 의미합니다. 다음은 기본적인 제네릭 함수 예시입니다:
function identity<T>(arg: T): T { return arg; } // 사용 예시 const result1 = identity<number>(10); // number 타입 const result2 = identity<string>("Hello"); // string 타입
위 코드에서 identity
함수는 어떤 타입의 값을 받아서 그대로 반환합니다. 이때 호출 시 타입을 직접 명시하거나, TypeScript가 자동으로 추론할 수 있습니다.
제네릭을 사용하는 배열 함수
제네릭을 활용하면 배열을 다루는 함수도 보다 유연하게 작성할 수 있습니다.
function getFirstElement<T>(arr: T[]): T { return arr[0]; } const num = getFirstElement([1, 2, 3]); // number const str = getFirstElement(["a", "b"]); // string
이 함수는 배열의 첫 번째 요소를 반환하며, 배열의 타입에 따라 반환 타입도 자동으로 설정됩니다.
제네릭 인터페이스와 타입 정의
제네릭은 함수뿐 아니라 인터페이스나 타입 별칭에도 사용할 수 있습니다.
interface ApiResponse<T> { status: number; data: T; } const userResponse: ApiResponse<{ name: string; age: number }> = { status: 200, data: { name: "John", age: 30 } };
이처럼 ApiResponse
는 다양한 데이터 타입에 대해 재사용 가능하며, 서버 통신에서 매우 유용하게 쓰입니다.
제네릭 클래스 활용
클래스에서도 제네릭을 활용할 수 있습니다. 데이터 저장, 캐시, 상태 관리 등의 역할을 하는 클래스에서 자주 사용됩니다.
class Box<T> { private contents: T; constructor(value: T) { this.contents = value; } getContents(): T { return this.contents; } } const stringBox = new Box<string>("TypeScript"); const numberBox = new Box<number>(123);
위 예제에서 Box
클래스는 어떤 타입이든 안전하게 저장하고 반환할 수 있는 범용 저장소 역할을 합니다.
제네릭의 제약 조건 (Constraints)
모든 타입이 다 적절하지 않을 수 있기 때문에, 제네릭에는 제약을 줄 수 있습니다. 이를 통해 특정 속성을 가진 타입만 허용하도록 만들 수 있습니다.
function getLength<T extends { length: number }>(value: T): number { return value.length; } getLength("Hello"); // OK getLength([1, 2, 3]); // OK // getLength(123); // Error: number에는 length가 없음
이 예제에서 제네릭 타입 T
는 반드시 length
속성을 가져야 하므로, 문자열이나 배열은 허용되지만 숫자는 오류를 발생시킵니다.
제네릭과 유니언 타입의 차이
제네릭과 유니언 타입은 비슷해 보일 수 있지만 개념적으로 다릅니다. 유니언은 하나의 타입이 여러 값을 가질 수 있도록 하고, 제네릭은 다양한 타입을 안전하게 일반화합니다.
// 유니언 타입 function logUnion(value: string | number) { console.log(value); } // 제네릭 타입 function logGeneric<T>(value: T) { console.log(value); }
유니언 타입은 제한된 타입 집합을 허용하고, 제네릭은 호출 시 타입을 정의하여 더 다양한 상황에 대응할 수 있습니다.
실전에서 제네릭 활용 팁
- API 응답 처리: 다양한 엔티티 타입을 가진 공통 응답 구조 정의
- 커스텀 훅: React 등에서 상태 관리 로직을 제네릭으로 설계
- 유틸 함수: 정렬, 필터 등 다양한 데이터를 처리할 때 타입 안정성 유지
결론: 타입 안정성과 재사용성을 동시에
TypeScript 제네릭은 타입 안정성을 유지하면서도 유연하고 재사용 가능한 코드를 작성할 수 있는 필수 도구입니다. 함수, 클래스, 인터페이스 등 다양한 구조에서 사용할 수 있으며, 복잡한 어플리케이션 구조를 설계할 때 특히 강력한 힘을 발휘합니다. 제네릭을 자유자재로 활용하면 더욱 견고하고 유지보수하기 쉬운 코드를 만들 수 있습니다.