본문 바로가기
JS

TypeScript 제네릭: 유연하고 재사용 가능한 코드 작성하기

by memory-log 2025. 1. 19.

TypeScript 제네릭

유연하고 재사용 가능한 코드 작성하기

TypeScript의 강력한 기능 중 하나인 제네릭(Generic)에 대해 알아보겠습니다. 제네릭을 사용하면 다양한 타입에 대해 동작하는 유연하고 재사용 가능한 코드를 작성할 수 있습니다.

제네릭이란?

제네릭은 타입을 마치 함수의 매개변수처럼 사용할 수 있게 해주는 기능입니다. 이를 통해 여러 타입에 대해 동작하는 함수, 클래스, 인터페이스 등을 만들 수 있습니다. 제네릭을 사용하면 코드의 중복을 줄이고 타입 안정성을 유지할 수 있습니다.

제네릭 함수 예시

다음은 제네릭 함수의 간단한 예시입니다:

function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<string>("Hello, TypeScript!");
let output2 = identity<number>(42);

console.log(output1); // "Hello, TypeScript!"
console.log(output2); // 42

identity 함수는 어떤 타입의 인자든 받아서 그대로 반환합니다.

<T>는 타입 매개변수를 나타내며, 함수를 호출할 때 실제 타입으로 대체됩니다.

제네릭 인터페이스

제네릭은 인터페이스에도 사용할 수 있습니다:

interface Box<T> {
  value: T;
}

let stringBox: Box<string> = { value: "A string value" };
let numberBox: Box<number> = { value: 42 };

이 예시에서 Box 인터페이스는 어떤 타입의 value든 가질 수 있습니다.

TypeScript의 제네릭은 실무에서 다양한 상황에서 유용하게 사용됩니다. 다음은 실무에서 자주 사용되는 제네릭 코드 예시입니다:

API 응답 처리

API 응답을 타입 안전하게 처리하는 데 제네릭을 사용할 수 있습니다:

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
  const response = await fetch(url);
  return await response.json();
}

// 사용 예
interface User {
  id: number;
  name: string;
}

const result = await fetchData<User>('/api/user/1');
console.log(result.data.name); // 타입 안전성 보장

재사용 가능한 컴포넌트

React와 같은 프레임워크에서 재사용 가능한 컴포넌트를 만들 때 제네릭이 유용합니다:

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

// 사용 예
const users = [{ id: 1, name: '홍길동' }, { id: 2, name: '김철수' }];
<List items={users} renderItem={(user) => user.name} />

이 코드는 제네릭을 사용한 재사용 가능한 React 컴포넌트를 정의하고 있습니다. 각 부분을 자세히 설명해드리겠습니다:

  1. 인터페이스 정의:
    interface ListProps<T> {
    items: T[];
    renderItem: (item: T) => React.ReactNode;
    }
  • ListProps<T>는 제네릭 인터페이스입니다.
  • items: T[]는 T 타입의 배열을 나타냅니다.
  • renderItem: (item: T) => React.ReactNode는 T 타입의 아이템을 받아 React 노드를 반환하는 함수입니다.
  1. 컴포넌트 정의:
    function List<T>({ items, renderItem }: ListProps<T>) {
    return (
     <ul>
       {items.map((item, index) => (
         <li key={index}>{renderItem(item)}</li>
       ))}
     </ul>
    );
    }
  • List<T>는 제네릭 함수 컴포넌트입니다.
  • itemsrenderItem을 props로 받습니다.
  • items.map()을 사용해 각 아이템을 <li> 요소로 변환합니다.
  • renderItem(item)을 호출하여 각 아이템을 렌더링합니다.
  1. 사용 예:
    const users = [{ id: 1, name: '홍길동' }, { id: 2, name: '김철수' }];
    <List items={users} renderItem={(user) => user.name} />
  • users 배열을 items prop으로 전달합니다.
  • renderItem prop으로 각 사용자의 name을 반환하는 함수를 전달합니다.

이 컴포넌트는 어떤 타입의 배열이든 받아 리스트로 렌더링할 수 있으며, 각 아이템을 어떻게 표시할지 renderItem 함수를 통해 사용자가 정의할 수 있습니다.

제네릭의 장점

  1. 타입 안정성: 컴파일 시점에 타입 검사를 수행하여 런타임 오류를 줄입니다.
  2. 코드 재사용: 다양한 타입에 대해 동작하는 함수나 클래스를 만들 수 있습니다.
  3. 유연성: 필요에 따라 다양한 타입을 지정할 수 있습니다.

결론

제네릭은 TypeScript에서 매우 강력하고 유용한 기능입니다. 이를 통해 더 유연하고 재사용 가능한 코드를 작성할 수 있으며, 동시에 타입 안정성도 유지할 수 있습니다. 제네릭을 잘 활용하면 더 견고하고 확장 가능한 애플리케이션을 개발할 수 있습니다.

댓글