React 컴포넌트에서 TypeScript Generics
Source: Dev.to
Introduction
제네릭은 매일 React 컴포넌트에서 사용하는 것은 아니지만, 특정 상황에서는 컴포넌트를 유연하면서도 타입‑안전하게 만들 수 있게 해줍니다.
Basic Component Definitions
Without Props
// Just FC component with no props
const Component = () => Hey ya!!!;
With Props using React.FC
// FC component with props
interface Props {
name: string;
}
const Component: React.FC = ({ name }) => (
Hey {name} ya!!!
);
React.FC는 몇 가지 이유로 선호도가 떨어졌는데, 그 중 하나는 우리가 필요로 하는 방식으로 제네릭을 처리하지 못한다는 점입니다. 그럼에도 불구하고 가독성을 위해 여전히 사용하는 개발자들이 많습니다.
Without React.FC
// FC component with simple props
interface Props {
name: string;
}
const Component = ({ name }: Props) => Hey {name} ya!!!;
When Generics Are Needed
컴포넌트가 API에서 받아온 배열 형태의 DTO(Data Transfer Object)를 받는 상황을 생각해 보세요. 컴포넌트는 “이름”을 나타내는 속성을 표시해야 하는데, 정확한 속성 이름은 DTO마다 다를 수 있습니다.
Example DTO
interface ResponseDTO {
id: number;
name: string;
}
Component Tied to a Specific DTO
interface ExampleProps {
data: ResponseDTO[];
getName: (item: ResponseDTO) => string;
}
export const Example = ({
data,
getName = (item) => item.name,
}: ExampleProps) => (
{data.map((item) => (
{getName(item)}
))}
);
이 코드는 ResponseDTO의 정확한 형태를 컴포넌트가 알고 있기 때문에 동작합니다. 어떤 DTO든 “이름과 비슷한” 값을 가지고 있다면 처리하도록 만들고 싶다면 제네릭이 필요합니다.
Generic Props Interface
interface Props {
data: T[];
getName: (item: T) => string;
}
제네릭 파라미터 T는 data 배열과 getName 콜백을 연결시켜, 두 요소가 같은 타입을 사용하도록 보장합니다.
Generic Component
export const NameViewer = ({ data, getName }: Props) => (
<>
Look at the names:
{data.map((item) => (
{getName(item)}
))}
);
Note: In
.tsxfiles, writing “ (with a trailing comma) disambiguates the generic declaration from JSX syntax.
Using the Generic Component
const unknownTypeData = [
{ id: 1, nameProp: 'John' },
{ id: 2, nameProp: 'Jane' },
];
const anotherUnknownTypeData = ['Foo', 'Bar'];
export const BusinessComponent = () => (
item.nameProp}
/>
item}
/>
);
NameViewer는 적절한 getName 함수만 제공하면 이제 어떤 타입의 데이터든 받아들일 수 있습니다.
Conclusion
이 간단한 예제는 제네릭을 활용하면 다양한 데이터 형태에 맞게 React 컴포넌트를 조정하면서도 타입 안전성을 유지할 수 있음을 보여줍니다. 앞으로의 글에서는 조건부 제네릭과 같은 더 고급 패턴을 살펴볼 예정입니다.