[refactor] 하드코딩 되어있던 모달 & GNB 리팩토링하기

KoEunseo·2023년 5월 24일
0

project

목록 보기
34/37

https://github.com/codestates-seb/seb40_main_029/pull/205/commits/f3398b5df61d01801fcc0ffc83fa42e22b586744

gnb랑 모달이 하드코딩 되어있었어서 계속 마음에 걸렸다.
그래서 뿌셨다!

우선 모달 리스트를 따로 만들어 놓았다. 여기저기서 쓰일 데이터다.
이 리스트를 받아서 gnb에서 label을 text로 표현할 것이다. 해당 모달을 오픈하기도 하고, withAuth에 따라 팝업창을 띄우기도 한다.
그리고 모달 아이템에서는 type에 따라 컨트롤할 모달, 툴팁 내용, 아이콘이 달라진다.

모달 리스트

interface Modal {
  type: string;
  component: JSX.Element;
  label: string;
  modalIcon: typeof FontAwesomeIcon;
  iconProp?: IconProp;
  isFull: boolean;
  withAuth: boolean;
  tooltip?: Tooltip;
}

export const modalList: Modal[] = [
  {
    type: 'LetterModal',
    component: <Letter />,
    label: '편지',
    modalIcon: FontAwesomeIcon,
    iconProp: faEnvelope,
    isFull: false,
    withAuth: false,
    tooltip: {
      info: '오른쪽 아래 + 버튼을 눌러서 친구에게 편지를 보낼 수 있어요.',
      place: 'right',
    },
  },
  {
    type: 'TodoModal',
    component: <TodoList lookbackRefresher={undefined} />,
    label: '오늘할일',
    modalIcon: FontAwesomeIcon,
    iconProp: faHighlighter,
    isFull: false,
    withAuth: false,
  },
  {
    type: 'FriendModal',
    component: <Friends />,
    label: '친구',
    modalIcon: FontAwesomeIcon,
    iconProp: faUserGroup,
    isFull: false,
    withAuth: false,
    tooltip: {
      info: '+ 버튼을 눌러서 친구의 무드카드를 얻어보세요.',
      place: 'right',
    },
  },
  {
    type: 'ThemeModal',
    component: <ThemeStore />,
    label: '색상테마',
    modalIcon: FontAwesomeIcon,
    iconProp: faStore,
    isFull: false,
    withAuth: false,
  },
  {
    type: 'MonthlyModal',
    component: <MonthlyLookback setHiddenCard={undefined} />,
    label: '한달기록',
    modalIcon: FontAwesomeIcon,
    iconProp: faCalendarDays,
    isFull: true,
    withAuth: true,
  },
  {
    type: 'LookbackModal',
    component: (
      <LookBack lookbackRefresh={undefined} setHiddenCard={undefined} />
    ),
    label: '일년기록',
    modalIcon: FontAwesomeIcon,
    iconProp: faFilm,
    isFull: true,
    withAuth: true,
  },
];

gnb을 나누어서 gnb item을 만들고 onClick event를 props로 받도록 했다.
그리고 logout item은 event가 다르기도 하고 auth에 따라 ui에 보이기도 안보이기도 해야해서 따로 분리해서 만들었는데, 지금 생각해보면 굳이 그러지 않았더도 됐을 것 같네...

gnb item

interface GnbItemProps {
  label: string;
  icon: IconProp;
  onClick: React.MouseEventHandler<HTMLElement>;
}

const GnbItem = (props: GnbItemProps) => {
  const { label, icon, onClick } = props;

  return (
    <>
      <NavItem onClick={onClick}>
        <DarkIcon>
          <FontAwesomeIcon icon={icon} size="lg" />
        </DarkIcon>
        <Label>{label}</Label>
      </NavItem>
    </>
  );
};

모달 아이템

interface ModalType {
  modalType: string;
  children: React.ReactNode;
}
const ModalItem: React.FC<ModalType> = ({ modalType, children }) => {
  const type = modalList.find(modal => {
    return modal.type === modalType;
  });
  const dispatch = useDispatch();
  const handleCloseModal = () => {
    dispatch(closeModal());
  };
  return (
    <Style.Basic>
      <Header
        closeModal={handleCloseModal}
        title={type?.label}
        tooltip={type?.tooltip}
        icon={type?.iconProp}
      />
      <Style.Content>{children}</Style.Content>
    </Style.Basic>
  );
};

Gnb

interface GnbDispatchType {
  type: string;
  withAuth: boolean;
}

const Gnb = () => {
  const accessToken = getCookie('accessToken');

  const dispatch = useDispatch();
  const { authPopup, handlePopup } = usePopUp();
  const handleModal = (modal: GnbDispatchType) => {
    modal.withAuth && !accessToken
      ? handlePopup(modal.withAuth)
      : dispatch(
          openModal({
            modalType: modal.type,
            isOpen: true,
          })
        );
  };

  //withAuth & !accessToken일때 popup
  return (
    <>
      {authPopup && <Overlay />}
      <Bubble>
        {modalList.map(modal => {
          return (
            <GnbItem
              key={modal.type}
              label={modal.label}
              icon={modal.iconProp}
              onClick={() => handleModal(modal)}
            />
          );
        })}
        {accessToken ? <LogoutItem /> : null}
      </Bubble>
    </>
  );
};

isfull이 true냐 false냐

모달에 따라 무드카드와 나란히 띄우거나 혹은 모달 하나만 화면에 가득차거나 해야한다. 이 UI를 표현하기 위해서 수많은 노력을 했었는데, 이 방법은 isFull이 false일때는 무드카드를 함께 렌더링하고, true일때는 무드카드 대신 null을 렌더링하는 액자 UI를 만들어 끼우는 것이다.

interface FullPageProp {
  isFull: boolean;
  children: React.ReactNode;
}

const IsFullModal: React.FC<FullPageProp> = ({ isFull, children }) => {
  return (
    <ContentLayout>
      {isFull ? null : (
        <div>
          <MoodSelector /> //이게 무드카드 컴포넌트다.
        </div>
      )}
      <div>{children}</div>
    </ContentLayout>
  );
};

global modal

위에서 만든 액자를 모달 렌더링할때 끼워팔면 된다.

function GlobalModal() {
  const { modalType, isOpen } = useSelector(selectModal);
  if (!isOpen) {
    return <IsFullModal isFull={false}>{null}</IsFullModal>;
  }

  const findModal = modalList.find(modal => {
    return modal.type === modalType;
  });

  const renderModal = () => {
    return findModal.component;
  };
  return <IsFullModal isFull={findModal.isFull}>{renderModal()}</IsFullModal>;
}
profile
주니어 플러터 개발자의 고군분투기

0개의 댓글