본문 바로가기
웹 프론트엔드/React 리액트 (기초)

[React] 하이오더(HoC) 기능으로 컴포넌트 확장하기 ②

by 지구인한군 2020. 10. 12.

2020/10/09 - [Web프론트엔드/리액트] - [React] 하이오더(HoC) 기능으로 컴포넌트 확장하기 ①


 바로 지난 시간에 하이오더 컴포넌트(HoC)의 개념과 그것을 이용해 컴포넌트를 확장할 수 있다고 배웠습니다. 그래서 오늘은 실습으로 컴포넌트에 툴팁 기능을 붙여서 확장해보겠습니다. 스타일이 적용되어 있지 않은 Input 컴포넌트와 이미 withStyles()로 확장되어 있는 Button 컴포넌트 두 가지 예시로 진행하겠습니다. 먼저 HoC 작성 ㄱㄱ!

 

// ..\test_project\src\components\withTooltip.jsx

import React from "react";
import withStyles, { css } from "../styles/withStyles";

export default function (tooltip) {
  // 확장 시킬 기본 컴포넌트 (첫글짜는 대문자 필수)
  return (BaseComponent) => {
    // 새로운 컴포넌트 이름 만들기
    const { displayName, name: componentName } = BaseComponent;
    const baseComponentName = displayName || componentName;

    // 확장 컴포넌트 만들기
    function ComponentWithTooltip({
      isError, // 툴팁에 에러메시지를 출력
      styles, // 컴포넌트에 스타일 적용
      ...props // 기본 컴포넌트의 프러퍼티 (전개연산자는 마지막에)
    }) {
      // 에러일 경우 스타일 변경
      let tooltipColor = styles.default;
      if (isError) {
        tooltipColor = styles.error;
      }
      return (
        <React.Fragment>
          <BaseComponent {...props} />
          {<div {...css(tooltipColor)}>{tooltip}</div>}
        </React.Fragment>
      );
    }

    // DOM에 보여질 확장 컴포넌트 이름
    ComponentWithTooltip.displayName = `withTooltip(${baseComponentName})`;

    // 기본 프로퍼티 값
    ComponentWithTooltip.defaultProps = {
      isError: false,
    };

    // 확장 컴포넌트에 또 스타일 컴포넌트 확장하기
    return withStyles(({ color }) => ({
      default: {
        color: color.default,
      },
      error: {
        color: color.error,
      },
    }))(ComponentWithTooltip);
  };
}

 

 내용은 간단합니다. 확장시킬 컴포넌트 밑에 툴팁 텍스트를 붙여주고, isError 프로퍼티로 문자 색깔을 변경하는 정도의 기능입니다. ComponentWithTooltip() 함수의 return() 부분이 핵심입니다. withStyles도 하이오더 컴포넌트라는 것을 꼭 인지하시고, 마지막 부분에 withStyles(...)(ComponentWithTooltip) 사용법 또한 중요합니다.

 

// ..\test_project\src\stories\Story_withTooltip.jsx

import React from "react";
import { storiesOf } from "@storybook/react";
import withTooltip from "../components/withTooltip";
import Input from "../components/Input";
import Button from "../components/Button";

const InputwithTooltip = withTooltip("아이디를 입력하세요")(Input);
const ButtonwithTooltip = withTooltip("확인 버튼을 누르세요")(Button);

storiesOf("Tooltip", module)
  .addWithJSX("Inputwith", () => (
    <InputwithTooltip name="id" label="아이디" isError={true} />
  ))
  .addWithJSX("Buttonwith", () => (
    <ButtonwithTooltip name="confirm" xlarge primary>
      확인
    </ButtonwithTooltip>
  ));

 

 스토리북에 추가해봤습니다. withTooltip(...)(컴포넌트) 형태로 새로운 확장 컴포넌트가 됐습니다. 이런 식으로 툴팁 기능을 각 컴포넌트 소스에 코딩하는 것이 아닌, 하이오더 기능으로 확장을 하면 코드의 재활용이 용이합니다.

 

이것을 커링 함수를 이용한 데코레이터 디자인 패턴이라고도 하는데 더 공부하고 싶은 분은 검색을 추천드립니다.

 

Input 컴포넌트 확장

 JSX탭을 보시면 Input -> withTooltip -> withStyles 순으로 확장되어 있습니다. Input은 스타일이 없기 때문입니다.

 

Button 컴포넌트 확장

 이번에 JSX탭을 보시면 Button-> withStyles -> withTooltip -> withStyles 순입니다. 왜냐면 Button은 지난 시간에 이미 withStyles()을 적용한 컴포넌트이기 때문입니다. 이런 식으로 계속 붙여서 사용하면 무한 확장이 가능하지만 Button의 예시처럼 너무 많은 계층이 생기면 관리하기가 어렵기 때문에 핵심 기능만 잘 조합하는 것이 중요합니다.

 

그럼 오늘은 여기까지 하겠습니다.

 

생각보다 참 간단하죠?


2020/10/14 - [Web프론트엔드/리액트] - [React] 컨텍스트(context) 기능으로 데이터 관리하기

댓글