본문 바로가기
개발/리액트

[리액트] HOC 컴포넌트를 만들어보자 #1 (feat.타입스크립트)

by 핸디(Handy) 2021. 6. 14.

다음과 같은 컴포넌트가 있습니다.

컴포넌트

SELECT 버튼을 클릭하면 포트폴리오 적용중...이라는 텍스트와 함께 원형 로딩바가 돌고 일정 시간 후에 랜더링 되는 컴포넌트입니다.

저는 이 컴포넌트를 이런식으로 구성했습니다.

const [loading, setLoading] = React.useState(false);
const handleSelectedPF = (portfolio: RRSW) => {
  setLoading(true);
  onChangeSelectedPF(portfolio);
  timer.current = window.setTimeout(() => {
    setLoading(false);
  }, 500);
};

return (
  {!loading ? (
    <CurrentSelectedPF selectedPF={selectedPF} />
  ) : (
    <Card
      style={{
        textAlign: "center",
        backgroundColor: "#F5F5F5",
        width: "280px",
        height: "450px",
      }}
    >
      <LoadingProgress height={"30rem"} description={"포트폴리오 적용중..."} />
    </Card>
  )}
)

코드는 필요한 부분만 가져와 간략화하였습니다.

맨 처음에는 false로 초기화하고 handleSelectedPF가 호출되면 0.5초 후에 true로 바꿔주는 간단한 컴포넌트입니다.

그걸 위해서 return에서 삼항연산자를 이용해 loading중이면 <LoadingProgress> 컴포넌트를 띄워주고 로딩이 끝나면

<CurrentSelectedPF> 컴포넌트를 띄웠습니다.

하지만 이런 종류의 컴포넌트들은 많이 있었고 이럴때마다 매번 삼항연산자를 이용해 코드를 복잡스럽게 만들기가 싫어, 그동안 공부만 하고 있었던 HOC를 이용해서 컴포넌트를 리펙토링 하고자 했습니다.

//WithLoading.tsx
import React from "react";
import LoadingProgress from "src/component/main/common/wiget/LoadingProgress";

interface WithLoadingProps {
  loading: boolean;
  text: string;
}

const withLoading = <P extends object>(Component: React.ComponentType<P>) =>
  class WithLoading extends React.Component<P & WithLoadingProps> {
    render() {
      const { loading, text, ...props } = this.props;
      return loading ? <LoadingProgress height={"100%"} description={text} /> : <Component {...(props as P)} />;
    }
  };

export default withLoading;

WithLoading은 들어온 컴포넌트에 Loading기능을 붙여 반환하는 커링함수입니다.

커링에 대해 한마디로 표현하자면 '반환 값이 함수인 디자인 패턴이며, 함수를 반환하는 함수'입니다.

따라서 이 함수는 loading, text 정보를 주면 해당 props로 loading 기능을 가진 컴포넌트를 반환해주는 커링 함수형 컴포넌트?라고 생각하시면 될 듯합니다.

사용법은 간단합니다.

import WithLoading from "src/hoc/WithLoading";

const NewCompomentWithLoading = WithLoading(OriginCompoment);

WithLoading으로 원래 컴포넌트를 감싸면 됩니다.


이제 원래 코드를 리펙토링 해보도록 하겠습니다.

const CurrentSelectedPFWithLoading = WithLoading(CurrentSelectedPF);
const [loading, setLoading] = React.useState(false);
const handleSelectedPF = (portfolio: RRSW) => {
  setLoading(true);
  onChangeSelectedPF(portfolio);
  timer.current = window.setTimeout(() => {
    setLoading(false);
  }, 500);
};

return (
	<CurrentSelectedPFWithLoading loading={loading} text={"포트폴리오 적용중..."} selectedPF={selectedPF} />
)

이번에도 코드를 간략화하긴 했습니다.

코드를 보면 맨 위에서 WithLoading으로 감싸준 CurrentSelectedPFWithLoading 컴포넌트가 만들어졌고,

그 이후에 삼항연산자로 바꿨던 로직을 모조리 없애고 해당 컴포넌트에 바로 값을 넣어주는 형식으로 변경되었습니다.

보시다시피 HOC를 이용하면 함수의 중복을 줄일 수 있습니다.

저는 삼항연산자을 이용 해 불필요했던 코드를 제거했고, WithLoading를 통해 언제든지 원하는 컴포넌트에 Loading 기능을 붙일 수 있게 되었습니다.

이로써 HOC 컴포넌트를 만들게 되었습니다.


하지만!! HOC 컴포넌트를 만들 때 많은 개발자들 사이의 암묵적인 규칙이 여러 가지 있습니다.

우리가 With를 앞에 붙인 것도 이러한 맥락입니다. 제가 어긴 암묵적인 규칙은 바로 

"컴포넌트 인자 하나만 전달한다."

저는 인자를 loading과 text를 넘김으로써 해당 규칙을 어겼습니다.

이런 경우 다중 커링(curying) 기법을 이용해 컴포넌트를 인자를 하나씩 받도록 변경하여 인자 규칙을 지킬 수 있습니다.

따라서 다음 포스트에서는 다중 커링을 이용해서 해당 컴포넌트를 다시 리펙토링 해보도록 하겠습니다.

댓글