youtube 를 보던중 react 컴포넌트 재사용을 위한 패턴으로 HOC(High-Order Component) 에 대한 영상을 보고 자주 쓰게 되는 패턴인지 간단한 예제를 만들어 보았다.
HOC는 고차 컴포넌트의 약자로, React에서 컴포넌트 로직을 재사용하기 위한 고급 패턴입니다. 쉽게 말해, 다른 컴포넌트를 감싸서 기능을 추가하거나 수정하는 컴포넌트라고 생각하면 됩니다.
HOC는 React API의 공식적인 부분은 아니지만, React의 구성적 특성을 활용한 패턴으로 널리 사용됩니다. 구체적으로는 컴포넌트를 입력받아 새로운 컴포넌트를 반환하는 함수라고 정의할 수 있습니다
ref: https://gemini.google.com/app/cee763a5bcc971e0
"react": "^18.2.0",
"typescript": "^5.2.2",
"tailwindcss": "^3.4.3",
"vite": "^5.2.0"
...
export interface BoxProps {
children: React.ReactNode;
name: string | number;
}
export const Box = (props: BoxProps) => {
return (
<div className="p-3 bg-red-100">
<div>{props.name}</div>
<div className="p-1 bg-slate-100">{props.children}</div>
</div>
);
};
기본 컴포넌트를 props로 받아 기능이 추가된 컴포넌트를 반환한다.
export const withShadow = (Component: React.ComponentType<BoxProps>) => {
return (props: React.ComponentProps<typeof Component>) => {
return (
<div className="shadow-xl">
<Component {...props} />
</div>
);
};
};
export const withNotification = (
Component: React.ComponentType<BoxProps & { notification: string }>,
) => {
return (props: React.ComponentProps<typeof Component>) => {
return (
<div>
<Component {...props} />
<div className="text-red-700 font-bold text-left">
{props.notification}
</div>
</div>
);
};
};
//기본 컴포넌트에 그림자추가
export const BoxWithShadow = withShadow(Box);
//기본 컴포넌트에 노티 추가
export const BoxWithNotification = withNotification(Box);
//기본 컴포넌트에 노티 + 그림자 추가 (중첩가능)
export const BoxWithShadowWithNotification = withNotification(BoxWithShadow);
//props type을 한정 시키는것도 가능
interface BoxNumberProps {
children: React.ReactNode;
name: number;
}
export const BoxNumber = (props: BoxNumberProps) => {
return <Box name={props.name}>{props.children}</Box>;
};
export interface BoxAllProps {
children: React.ReactNode;
name: string | number;
isShadow?: boolean;
isNotification?: boolean;
notification?: string;
}
export const BoxAllProps = (props: BoxAllProps) => {
return (
<div>
<div className={`${props.isShadow ? 'shadow-xl' : null}`}>
<div className="p-3 bg-red-100">
<div>{props.name}</div>
<div className="p-1 bg-slate-100">{props.children}</div>
</div>
</div>
{props.isNotification && (
<div className="text-red-700 font-bold text-left">
{props.notification}
</div>
)}
</div>
);
};
export default function Home() {
return (
<div className="text-xl flex flex-col gap-20">
<Box name="기본">기본 박스</Box>
<BoxWithShadow name="그림자 추가">기본+그림자</BoxWithShadow>
<BoxWithNotification //
name="노티 추가"
notification="this is notification"
>
기본+노티
</BoxWithNotification>
<BoxWithShadowWithNotification
name="그림자, 노티 추가"
notification="this is notification"
>
그림자 + 노티
</BoxWithShadowWithNotification>
<BoxNumber name={1234}>name type number</BoxNumber>
// name에 stirng 값 전달시 type 에러 발생
<BoxAllProps
name="HOC 아님"
isShadow={true}
isNotification={true}
notification="this is notification"
>
단일 컴포넌트
</BoxAllProps>
</div>
);
}
간단한 예제를 가지고 했지만 HOC 내부에 상태를 넣거나 useEffect를 넣어 인증이나 로딩등 추가 기능을 넣는것도 가능하다. 예제에서보면 단일 컴포넌트로도 모든 기능 커버가 가능하기 때문에 어떤 방식이 무조건 좋다고 할 수는 없을것 같다. 예제코드와 경험에 비춰봣을때 당장은 타입 한정시키는 방법은 사용해 볼만한거같다.
복잡한 컴포넌트 또는 props가 너무 많은 컴포넌트는 HOC를 이용해 잘 분리 하겨 사용해도 좋을것 같다.
예제코드 Github
https://github.com/hinyc/my-test-app
인공지능이 답해준 HOC 장단점
https://gemini.google.com/app/cee763a5bcc971e0