Skip to main content

TypeScript로 React Generic UI를 만들기 위해 반드시 확인해야하는 것

Button 컴포넌트는 item prop을 받아서 그 item의 name 속성을 버튼에 표시합니다.

Button 컴포넌트는 제네릭을 사용하여 { name: string } 형태를 포함하는 어떤 타입의 객체도 받을 수 있습니다.

이 예시에서는 user 객체를 Button 컴포넌트에 전달하고 있으며, user 객체는 { name: string, age: number } 형태를 가지고 있습니다. 이렇게 하면 Button 컴포넌트는 다양한 형태의 객체를 유연하게 다룰 수 있으면서도 타입 안정성을 보장받을 수 있습니다.

T extends { name: string } 구문을 사용하는 이유는 타입스크립트에서 함수나 컴포넌트가 제네릭 타입 T를 사용할 때, 해당 타입이 특정 속성을 반드시 포함하도록 보장하기 위해서입니다.

이런 방식을 통해 타입 안정성을 제공하고, 런타임 오류를 방지할 수 있습니다.

T extends { name: string } 구문을 사용하면, 다양한 타입의 객체를 처리하면서도 필요한 속성을 갖춘 객체만을 허용하는 균형을 맞출 수 있습니다.

import React from "react";

// Button 컴포넌트의 Props 타입 정의
// T는 최소한 { name: string } 형태를 가져야 함
type ButtonProps<T extends { name: string }> = {
item: T; // 여기서 item은 T 타입을 가짐
};

// Button 컴포넌트 정의
// 제네릭 타입 T를 사용하여 다양한 형태의 item을 받을 수 있음
function Button<T extends { name: string }>({ item }: ButtonProps<T>) {
return <button>{item.name}</button>;
}

// 사용 예시
const App = () => {
const user = { name: "John", age: 30 }; // user 객체에는 name과 age 속성이 있음

return (
<div>
<Button item={user} /> {/* Button 컴포넌트에 user 객체 전달 */}
</div>
);
};

export default App;

extends한 타입 이외의 다른 속성을 포함해서 Button 컴포넌트로 보내지면 어떻게 될까?

Button 컴포넌트에서 직접적으로 선택한 name, id는 확장하지 않으면 타입에러가 발생하지만 comment의 경우, 직접적으로 Button에서 사용되지는 않으므로 타입에러가 발생되지 않는다.

T 타입의 객체는 최소한 id와 name 속성을 가져야 합니다. 하지만 comment와 같은 것은 최소요건은 아니기 때문에 타입에러가 발생하지 않습니다.

interface ButtonProps<T> {
item: T;
}

const Button = <
T extends {
id: string;
name: string;
}
>({
item,
}: ButtonProps<T>) => {
return <button key={item.id}>{item.name}</button>;
};

const BUTTONS = [
{ name: "kiwon 1", id: "1", comment: "fdjsiao" },
{ name: "kiwon 2", id: "2", comment: "fdsioa" },
{ name: "kiwon 3", id: "3", comment: "difjsoaji" },
];

const App = () => {
return (
<div
style={{
display: "flex",
gap: "8px",
justifyContent: "center",
width: "100%",
}}
>
{BUTTONS.map((button) => (
<Button item={button} />
))}
</div>
);
};

export default App;