HOC 사용해 보기

홍인열·2024년 4월 30일
0
post-custom-banner

youtube 를 보던중 react 컴포넌트 재사용을 위한 패턴으로 HOC(High-Order Component) 에 대한 영상을 보고 자주 쓰게 되는 패턴인지 간단한 예제를 만들어 보았다.

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>
  );
};

HOC 생성 함수

기본 컴포넌트를 props로 받아 기능이 추가된 컴포넌트를 반환한다.

그림자 추가

export const withShadow = (Component: React.ComponentType<BoxProps>) => {
  return (props: React.ComponentProps<typeof Component>) => {
    return (
      <div className="shadow-xl">
        <Component {...props} />
      </div>
    );
  };
};

notification 추가

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>
    );
  };
};

HOC 생성

//기본 컴포넌트에 그림자추가
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>;
};

추가로 HOC 없이 모든 props를 받는 컴포넌트

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

profile
함께 일하고싶은 개발자
post-custom-banner

0개의 댓글