Skip to content

베이스 모달 만들기

김서진 edited this page Dec 4, 2021 · 4 revisions

개요

커스텀 모달을 개발한 동기

데스크탑 앱인 디스코드를 클로닝한 덕스코드 프로젝트의 특성상
메인 레이아웃 위에서 일어나는 동작들(채팅, 화상채팅 등) 외의 동작들(채널 추가/삭제 등)은
로그인/회원가입 기능을 제외하면 모두 모달에서 동작됩니다.

모달이 많이 사용되는 만큼, 재사용 가능하고 다른 팀원들이 쉽게 사용할 수 있는 베이스 모달 컴포넌트를 구현해두기로 결정했습니다.

커스텀 모달 미리보기

2021-12-03 00 53 02

개발 과정

Related Pull Request

https://github.com/boostcampwm-2021/web09-Duxcord/pull/100

Modal Interface 정의

image

모달의 디자인을 분석하며 위의 그림과 같이 공통적인 부분들을 찾을 수 있습니다.

  1. 제목
  2. 부연 설명
  3. 우측 상단 닫기 버튼
  4. 좌측 하단 뒤로 가기 버튼
  5. 우측 하단 버튼

그리고 몸통 부분은 다른 컴포넌트로 끼워주기로 결정하였습니다.

또한 각 버튼이 수행하는 동작은 달라질 수 있으니
모달의 상태를 제어하는 함수들을 모은 ModalController라는 인터페이스를 따로 정의해 모달의 prop으로 받도록 하였고,
우측 하단의 버튼이 수행하는 동작과 색상은 ModalButton 인터페이스 형태로 받아오도록 하였습니다.

interface ModalData {
  title?: string;
  subTitle?: string;
  middleContent: ReactElement<any, any>;
  bottomRightButton?: ModalButton | null;
}

interface ModalController {
  hide: function;
  show?: function;
  previous?: function;
  next?: function;
}

interface ModalButton {
  text: string;
  onClickHandler: function;
  color: string;
}

Modal Component 정의

모달을 닫을 때 애니메이션을 주기 위하여 3초의 interval을 추가적으로 주어 hide 함수를 덮어썼습니다.
isHidden 속성을 BackgroundWrapper 모두에게 주어 사라질 때의 애니메이션을 서로 다르게 주었습니다.
좌측 하단 버튼이 '이전'일 경우와 '닫기'일 경우가 있어 상황에 맞게 렌더링되도록 코드를 짜두었습니다.
그 외의 속성들은 모두 위의 디자인과 알맞게 배치했습니다.

function Modal({
  props: { title, subTitle, middleContent, bottomRightButton },
  controller: { hide, previous },
}: {
  props: ModalData;
  controller: ModalController;
}) {
  const [hidden, setHidden] = useState(false);

  const hideModal = () => {
    setHidden(() => true);
    setTimeout(() => {
      hide();
    }, 300);
  };

  return (
    <Background onClick={hideModal} isHidden={hidden}>
      <Wrapper onClick={(e) => e.stopPropagation()} isHidden={hidden}>
        <Top>
          <ModalCloseIcon onClick={hideModal} />
        </Top>
        {title && <Title>{title}</Title>}
        {subTitle && <SubTitle>{subTitle}</SubTitle>}
        <div>{middleContent}</div>
        {bottomRightButton && (
          <Bottom>
            {previous ? (
              <BottomLeftButton onClick={previous}>이전</BottomLeftButton>
            ) : (
              <BottomLeftButton onClick={hideModal}>닫기</BottomLeftButton>
            )}
            <BottomRightButton
              color={bottomRightButton.color}
              onClick={bottomRightButton.onClickHandler}
            >
              {bottomRightButton.text}
            </BottomRightButton>
          </Bottom>
        )}
      </Wrapper>
    </Background>
  );
}

상속받은 모달 예시

function GroupJoinModal({ controller }: { controller: ModalController }) {

    const InputComponent = (
      <Input
        onChange={(e) => updateGroupCode(e.target.value)}
        placeholder='그룹 코드를 입력해주세요'
      />
    );

    return 
      (<Modal
        props={{
          title: '그룹 참가하기',
          subTitle: '아래에 초대 코드를 입력해 그룹에 참가하세요',
          middleContent: InputComponent,
          bottomRightButton: {
            title: '그룹 참가하기',
            color: colors.Blue,
            onClickHandler: joinGroup,
          },
        }}
        controller={controller}
      />);

모달의 업적

위의 베이스 모달 덕분에 정말 많은 모달 컴포넌트가 수월하게 생성될 수 있었습니다.

Clone this wiki locally