Post

react로 제네릭 컴포넌트 만들기

타입스크립트로 사이드 프로젝트 리팩토링 중 같은 컴포넌트이지만 타입만 다른 경우에 사용할 패턴을 찾아야 헀습니다. 그래서 열심히 구글링 하던 도중 가장 유용하게 사용한 제네릭 컴포넌트 패턴을 소개해 보겠습니다.

이 글은 2024-03-19 에 업데이트 되었습니다.

이 글은 HTML, CSS , JavaScript, TypeScript, React에 대한 기본적인 지식을 알고 있어야 합니다.

리팩토링 이전의 컴포넌트 패턴

리팩토링 이전의 저의 컴포넌트는 공통된 레이아웃 리스트 컴포넌트에 다양한 타입의 카드들을 담아야 했었습니다.

따라서 아래와 같이 리스트의 props에 다양한 타입을 줘서 여러가지 컴포넌트를 만들었습니다. 하지만 이렇게 코드를 짜다 보니 같은 코드를 여러 곳에서 중복하게 되었고, 가독성이 안좋은 코드가 되었습니다.

따라서 타입스크립트의 제네릭 컴포넌트 패턴을 필수적으로 사용해야 한다고 생각 했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React from "react";

// 다양한 타입을 지정해줘야함 불편...
interface ListItem = {
  item: string | number | Item | AnotherItem;
};

function List({ items }: { items: ListItem[] }) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item.item}</li>
      ))}
    </ul>
  );
}

const items: ListItem[] = [
  { item: "Apple" },
  { item: "Banana" },
  { item: "Cherry" },
];

function App() {
  return (
    <div>
      <List items={items} />
    </div>
  );
}

export default App;

제네릭 컴포넌트 패턴

제네릭 컴포넌트 패턴은 리액트와 타입스크립트를 사용하여 재사용 가능하고 유연한 컴포넌트를 만드는 패턴 중 하나입니다. 이 패턴은 컴포넌트의 타입을 제네릭으로 정의하여 여러 종류의 데이터나 상태를 다룰 수 있도록 합니다.

예를 들어, 리스트를 렌더링하는 컴포넌트를 만들 때, 리스트에 표시될 아이템의 타입이 다양할 수 있습니다. 이 때 제네릭을 사용하여 해당 컴포넌트를 다양한 타입의 리스트를 렌더링할 수 있도록 만들 수 있습니다.

사용법

제네릭 컴포넌트를 만드는 방법에는 많은 방법이 있겠지만 저는 3가지 단계로 나누어 학습 하였습니다.

1. 제네릭 타입 정의하기
제네릭 컴포넌트의 타입을 정의하는 단계로 타입스크립트로 일반적인 컴포넌트를 정의할 때 쓰는 interface에 제네릭을 사용할 수 있게 <T>라는 키워드를 붙혀 줍니다. 또한 타입스크립트에서 다시 한번 타입을 추론할 수 있도록 renderItem을 작성해 줘야 합니다.

1
2
3
4
interface IList<T> = {
  item: T;
  renderItem: (item: T, index: number) => React.ReactNode;
};

2. 제네릭 컴포넌트 선언하기
이제 위에서 제네릭 인터페이스를 선언한 것과 같이 제네릭 컴포넌트를 선언해 줍니다.

1
2
3
4
5
const List = <T,>({ list, render }: IList<T>) => {
  return list.map((item, index) => render(item, index));
};

export default List;

3. 제네릭 컴포넌트 사용하기
다양한 타입의 배열을 List 컴포넌트의 props로 넘겨주고 List 컴포넌트에서는 이것을 추론하여 renderItem의 타입을 알 수 있게 되면서 다양한 타입을 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const stringItems = [{ item: "Apple" }, { item: "Banana" }, { item: "Cherry" }];
const numberItems = [{ item: 1 }, { item: 2 }, { item: 3 }];

function App() {
  return (
    <div>
      <h1>String List</h1>
      <List
        items={stringItems}
        renderItem={(item, index) => <Item>item</Item>}
      />

      <h1>Number List</h1>
      <List
        items={stringItems}
        renderItem={(item, index) => <Item>item</Item>}
      />
    </div>
  );
}

따라서 아래의 gif파일과 같이 아주쉽게 타입 추론을 사용하실 수 있습니다.

image

CodeSandbox에서 결과 실습하기

아래의 사이트에서 결과를 확인하실 수 있습니다. 다양한 타입을 선언해보고 리스트 형식도 바꿔보실 수 있습니다!

결론

프로젝트를 진행할 때, 코드의 품질과 유연성은 항상 고려해야 할 중요한 요소입니다. 특히, 프로젝트가 성장하고 확장될수록 코드의 관리가 점점 더 중요해집니다.

참고 사이트

https://res.cloudinary.com/dxesudkxn/image/upload/v1711952423/blog/kzfeeorbsootbfqy0xal.gif

https://deemmun.tistory.com/81

This post is licensed under CC BY 4.0 by the author.