제네릭
Generic
타입을 파라미터화하여 재사용 가능한 코드를 작성하는 기법. Java, TypeScript, Rust 등에서 지원.
Generic
타입을 파라미터화하여 재사용 가능한 코드를 작성하는 기법. Java, TypeScript, Rust 등에서 지원.
제네릭(Generic)은 타입을 파라미터화하여 재사용 가능한 컴포넌트를 작성하는 프로그래밍 기법입니다. 함수나 클래스를 특정 타입에 종속시키지 않고, 사용 시점에 타입을 지정할 수 있게 합니다. 이를 통해 타입 안정성을 유지하면서도 코드 중복을 줄일 수 있습니다.
제네릭의 핵심 이점은 컴파일 타임 타입 안정성입니다. Java의 List<String>은 문자열만 저장할 수 있고, 잘못된 타입 추가는 컴파일 오류로 잡힙니다. Object를 사용하면 런타임에 ClassCastException이 발생할 수 있지만, 제네릭은 이를 컴파일 타임에 방지합니다.
TypeScript에서 제네릭은 함수, 클래스, 인터페이스에 적용됩니다. Array<T>, Promise<T>, Map<K, V> 등 많은 내장 타입이 제네릭입니다. 제약 조건(extends)을 사용해 타입 파라미터를 특정 인터페이스를 구현한 타입으로 제한할 수도 있습니다.
Rust에서는 제네릭이 성능 저하 없이 동작합니다. 컴파일러가 단형화(monomorphization)를 통해 사용되는 각 타입에 대해 별도의 코드를 생성합니다. 이는 런타임 오버헤드 없이 제네릭의 편의성을 제공합니다. 트레이트 바운드로 타입 제약을 표현합니다.
// TypeScript 제네릭 예제
// 제네릭 함수
function identity<T>(arg: T): T {
return arg;
}
const str = identity<string>("hello"); // 타입 명시
const num = identity(42); // 타입 추론
// 제네릭 인터페이스
interface Result<T, E> {
success: boolean;
data?: T;
error?: E;
}
// 제네릭 클래스
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
// 제약 조건 (extends)
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): number {
console.log(arg.length);
return arg.length;
}
logLength("hello"); // OK - string has length
logLength([1, 2, 3]); // OK - array has length
// logLength(123); // Error - number doesn't have length
// 여러 타입 파라미터
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const swapped = swap([1, "hello"]); // [string, number]
프론트엔드 개발자: "API 호출 함수가 엔드포인트마다 거의 똑같은데, 응답 타입만 다릅니다. 코드 중복이 심해요."
기술 리더: "제네릭 함수로 추상화하면 됩니다. fetch<T>(url): Promise<T> 형태로 만들어 보세요."
프론트엔드 개발자: "호출 시 타입을 지정하면 반환값도 그 타입으로 추론되겠네요."
기술 리더: "네, Axios 인스턴스도 그렇게 구현되어 있어요. 에러 타입도 제네릭으로 받으면 더 완벽합니다."
면접관: "제네릭을 사용하는 이유와 장점을 설명해 주세요."
지원자: "타입 안정성을 유지하면서 재사용 가능한 코드를 작성할 수 있습니다. any를 쓰면 타입 검사가 무력화되지만, 제네릭은 사용 시점에 타입이 결정되어 IDE 자동완성과 컴파일 오류 검출이 가능합니다."
면접관: "공변성(covariance)과 반공변성(contravariance)에 대해 아시나요?"
지원자: "공변성은 T가 U의 하위 타입이면 Container<T>도 Container<U>의 하위 타입인 경우입니다. 반공변성은 그 반대이고, 함수 파라미터 위치에서 발생합니다. TypeScript에서는 이를 엄격하게 검사합니다."
리뷰어: "이 유틸리티 함수가 any를 반환하고 있어서 타입 안정성이 없네요."
작성자: "여러 타입에서 사용되어서 any로 했는데, 더 좋은 방법이 있을까요?"
리뷰어: "제네릭을 사용하세요. 입력 타입에서 반환 타입을 추론하게 하면 any 없이도 유연성을 유지할 수 있습니다."
작성자: "알겠습니다. T extends 조건도 추가해서 잘못된 타입 전달을 막겠습니다."