카테고리 없음

TypeScript 22강: 고급 타입스크립트 기초 – 제네릭(Generic) 활용법

mystory55776 2025. 5. 22. 21:35

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 제네릭은 타입 안정성을 유지하면서도 유연하고 재사용 가능한 코드를 작성할 수 있는 필수 도구입니다. 함수, 클래스, 인터페이스 등 다양한 구조에서 사용할 수 있으며, 복잡한 어플리케이션 구조를 설계할 때 특히 강력한 힘을 발휘합니다. 제네릭을 자유자재로 활용하면 더욱 견고하고 유지보수하기 쉬운 코드를 만들 수 있습니다.