디딤돌 창업 프로젝트 - Refutsal/React

[React] component로 만들어 재사용성 높여보기

맏리믓 2023. 4. 11. 23:58

들어가며

    - 저번 시간에 "Mui" 를 통해 "Modal" 을 사용 하는 방법에 대해 설명 해 보았다.

    - Modal, Button 특정 상황에만 한두번 사용 되지 않고 다양한 곳에서 사용되는 기능 들이 있다.

    - 이러한 것들은 사용 할때마다 만들어 사용 하는 것 보다 component 로 따로 빼두면 코드의 재사용성이 높아지게 된다.


방법

 1. BasicModal.tsx

    - 우선 원하는 이름의(여기서는 "BasicModal.tsx") 파일을 만들어 준다.

    - 그 후 저번 시간에 했던 Modal 의 코드를 넣어 준다.

    - 이 때 Modal 내부에 들어갈 데이터와 open 여부, close 함수는 이 component 를 호출 하는 파일에서 보내 줄 것이다.

    - 따라서 기존에 있던 useState 부분은 제거 해 준다.

    - 또한 해당 데이터를 파라미터로 받아야 하기 때문에 데이터 들의 type 을 지정해 주는 type ModalBox 를 만들어 준다.

import Box from '@mui/material/Box';
import Modal from '@mui/material/Modal';

import { styled } from '@mui/material/styles';

const HeatmapGraphModalBox = styled(Box)(({ theme }) => ({
  position: 'absolute' as 'absolute',
  top: '50%',
  left: '50%',
  transform: 'translate(-50%, -50%)',
  width: 500,
  [theme.breakpoints.down('sm')]: {
    width: 300,
  },
})) as typeof Box;

type ModalBox = { // 파라미터의 type 을 저장
  data: string | JSX.Element;
  open: boolean;
  onClose: () => void;
};

// 기존의 open, onClose 를 호출 하는 부분으로 부터 받아서 사용
export default function BasicModal({ data, open, onClose }: ModalBox) {
  return (
    <Modal open={open} onClose={onClose}>
      <HeatmapGraphModalBox>{data}</HeatmapGraphModalBox>
    </Modal>
  );
}

 2. HeatmapGraph.tsx(기존에 사용하던 파일)

    - 당연히 Modal 을 위한 style 은 제거 해 준다.

    - 기존의 <Modal> tag 를 사용하던 방식과 유사하게 사용해 주면 된다.

    - 이때 추가적으로 data 부분에 원하는 데이터를 넘겨 주면 된다.

    - 아래는 BasicModal 을 호출 하는 부분이다.

      <BasicModal
        data={<HeatmapSvg width={'100%'} />} //넘겨서 Modal 에 띄우고자 하는 data
        open={open} //Modal 의 state( 열렸는지 아닌지의 상태)
        onClose={handleClose} //열린 modal 을 닫아주는 함수를 전달
      ></BasicModal>

    - 이렇게 되면 data, open, onClose 가 function BasicModal({ data, open, onClose }: ModalBox) 의 data, open, onClose로 들어가게 되는 것이다.


전체 코드(HeatmapGraph.tsx만)

import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import * as React from 'react';

import BasicModal from '@/components/common/BasicModal';
import { colors } from '@/theme/colors';
import { styled } from '@mui/material/styles';

const HeatmapGraphBox = styled(Box)({
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  color: 'white',
  gap: '0.5rem',
}) as typeof Box;

const MagnifyButton = styled(Button)({
  paddingInline: '1rem',
  color: 'white',
  fontSize: '1rem',
}) as typeof Button;

type HeatmapGraphType = {
  data: number[][];
  color: string;
};

type HeatmapGraphModalBoxType = {
  width: string;
};

export const HeatmapGraph = ({ data, color }: HeatmapGraphType) => {
  const [open, setOpen] = React.useState(false);
  function handleOpen() {
    setOpen(true);
  }

  function handleClose() {
    setOpen(false);
  }

  const squares = data.map((v, i) => {
    return v.map((w, j) => {
      return (
        <rect
          key={`heat_r_${i}_${j}`}
          width="20"
          height="18.333"
          fill={colors[color]}
          opacity={Math.sqrt(w / 3)}
          x={22 * j + 2}
          y={19.333 * i + 2}
        />
      );
    });
  });

  const HeatmapSvg = ({ width }: HeatmapGraphModalBoxType) => {
    return (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width={width}
        // height="350"
        fill="none"
        viewBox="0 0 200 350"
      >
        {/* FIELD START */}

        <path
          fill="#254B1E"
          d="M200 0H549.999V199.999H200z"
          transform="rotate(90 200 0)"
        ></path>
        <path
          fill="#2B5C26"
          d="M200 50H250V249.999H200z"
          transform="rotate(90 200 50)"
        ></path>
        <path
          fill="#2B5C26"
          d="M200 150H250V349.999H200z"
          transform="rotate(90 200 150)"
        ></path>
        <path
          fill="#2B5C26"
          d="M200 249.999H250V449.998H200z"
          transform="rotate(90 200 249.999)"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M1 1H198.999V348.999H1z"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M103.125 77.75C60.169 77.75 25.296 43.472 24.756 1h156.737c-.54 42.472-35.412 76.75-78.368 76.75zM103.125 272.249c-42.956 0-77.829 34.278-78.369 76.75h156.737c-.54-42.472-35.412-76.75-78.368-76.75z"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M200 174.75L1.25 174.75"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M100 206.5c-17.397 0-31.5-14.103-31.5-31.5s14.103-31.5 31.5-31.5 31.5 14.103 31.5 31.5-14.103 31.5-31.5 31.5z"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M128.545 1H141.31799999999998V58.091H128.545z"
          transform="rotate(90 128.545 1)"
        ></path>
        <path
          stroke="#C5C5C5"
          strokeWidth="2"
          d="M128.545 336.226H141.31799999999998V393.317H128.545z"
          transform="rotate(90 128.545 336.226)"
        ></path>

        {/* FIELD END */}

        {/* HEATMAP START */}
        {squares}
        {/* HEATMAP END */}
      </svg>
    );
  };

  return (
    <HeatmapGraphBox>
      <HeatmapSvg width={'200'} />
      <MagnifyButton onClick={handleOpen}>🔍 자세히 보기</MagnifyButton>
      <BasicModal
        data={<HeatmapSvg width={'100%'} />}
        open={open}
        onClose={handleClose}
      ></BasicModal>
    </HeatmapGraphBox>
  );
};

export default HeatmapGraph;

결과

자세히 보기 누르기 전
자세히 보기를 누른 후

    - 코드 변경 전과 동일 한 결과가 나오는 것을 볼 수 있다.