Web/[JS] FrontEnd

Modal 만들기

ihl 2021. 3. 27. 18:09

모달창

  프로젝트에서 모달창을 구현해야하는 일이 생겼다. 라이브러리를 이용해도 좋지만 직접 구현하는 것이 더 학습에 도움이 될 것 같아서 모달만들기 글 를 참고하여 직접 구현을 해보았고 만족스러운 결과가 나왔다. 

 

1. 구조

모달창의 구조

  모달창의 구조는 DImmer, OuterContainer, InnerContainer, Icon으로 구성된다. 각 요소의 역할은 다음과 같다.

  • Dimmer
    • 화면을 0.3투명도의 검은 화면 Div로 채운다.
    • 다른 컴포넌트들보다 위에 위치 한다.(z-index) 
  • OuterContainer
    • InnerContainer를 감싸고 있다.
    • position을 fixed로 주어 스크롤을 내려도 위치를 유지시킨다.
    • Dimmer보다도 더 위에 위치한다.
  • InnerContainer
    • Dimmer위에 떠있는 하얀 Div를 의미하며 InnerContainer의 Children에 따라 다른 컨텐츠의 Modal이 된다.
    • positon을 relative, top = 50%로 주어 가운데 위치를 유지시킨다.
    • transform을 translateY(-50%)로 주어 스크롤이 있는 페이지에서도 가운데 위치를 유지시킨다.
  • Icon
    • InnerContainer의 Children 중 하나로 클릭하면 모달창이 닫히는 구조를 갖게한다.

2. Styled Component

또 다른 종류의 모달창

  작업 도중 경고창이라는 새로운 모달창이 필요해졌다. 위의 모달과 이 경고창은 같은 모달이지만 다른 종류의 용도를 갖고 있으므로 색상, 애니메이션 등이 달라져야했다. 이 부분은 Styled Component를 이용하여 해결할 수 있었다.

 

import { Dimmer, OuterContainer, InnerContainer, Icon } from './CenterModalStyle';

const CenterModal = ({ visible, color, isBlackBtn, onClose, className, children, backColor, isWarning }) => {
  return (
    <>
      <Dimmer visible={visible} backColor={backColor}></Dimmer>
      <OuterContainer tabIndex="-1" visible={visible}>
        <InnerContainer tabIndex="0" color={color} className={className} isWarning={isWarning}>
          {children}
          <Icon bg={isBlackBtn ? './res/close_black.png' : './res/close_white.png'} onClick={onClose}></Icon>
        </InnerContainer>
      </OuterContainer>
    </>
  );
};

  Styled Component는 css에 js 변수를 전달할 수 있는 라이브러리이다. 따라서 나는 모달 창을 생성할 때 color, isWarning 등의 props를 받아 여러 종류의 모달창을 구분하여 생성할 수 있게 만들었다. 

 

import styled, { css, keyframes } from 'styled-components';

const shakeAnimation = keyframes` 
  0%, 100% { transform: translate(0, -50%); }
  10%, 30%, 50%, 70%, 90% { transform: translate(-10px, -50%); }
  20%, 40%, 60%, 80% { transform: translate(10px, -50%); }
`;

export const Dimmer = styled.div`
  box-sizing: border-box;
  display: ${(props) => (props.visible ? 'block' : 'none')};
  //...생략
  background-color: ${(props) => (props.backColor ? 'rgba(0, 0, 0, 0)' : 'rgba(0, 0, 0, 0.3)')}; 
`;

export const OuterContainer = styled.div`
  box-sizing: border-box;
  display: ${(props) => (props.visible ? 'block' : 'none')};
  //...생략
`;

export const InnerContainer = styled.div`
  background-color: ${props => props.color};
  //...생략
  ${(props) => props.isWarning && css`
   animation: ${shakeAnimation} 0.3s alternate;
  `}
`;

export const Icon = styled.div`
  //...생략
  background-image: url(${props => props.bg});
`;

  Styled Component를 이용하면 다른 css가 적용된 HTML 요소들을 마치 컴포넌트처럼 사용할 수 있다. 컴포넌트처럼 props를 전달하면 css에서 이를 props로 받아 해당 요소의 값에 따라 다른 css를 적용할 수 있다.

 

3. Close

import { Dimmer, OuterContainer, InnerContainer, Icon } from './CenterModalStyle';

const CenterModal = ({ visible, color, isBlackBtn, onClose, className, children, backColor, isWarning }) => {
  const onClickDimmerHandler = () => {
    onClose();
  };

  const prevendEventPropagation = (e) => {
    e.stopPropagation();
  };

  return (
    <>
      <Dimmer visible={visible} backColor={backColor}></Dimmer>
      <OuterContainer tabIndex="-1" visible={visible} onClick={onClickDimmerHandler}>
        <InnerContainer tabIndex="0" color={color} className={className} isWarning={isWarning} onClick={prevendEventPropagation}>
          {children}
          <Icon bg={isBlackBtn ? './res/close_black.png' : './res/close_white.png'} onClick={onClose}></Icon>
        </InnerContainer>
      </OuterContainer>
    </>
  );
};

  모달창은 보통 모달창 밖을 클릭하면 닫히게 되어있다. 나도 이러한 액션을 모달에 적용시켜보았다. 

 

  모달창을 닫을 때 중요한 점은 모달 안쪽을 클릭하면 닫히지 않고, 바깥쪽을 클릭했을 때만 모달이 닫힌다는 점이다. 나는 일단 OuterContainer를 클릭하면 모달이 닫히게 만들 되 InnerContainer를 클릭하면 prevendEventPropagation함수를 실행시켜 이벤트 전달을 막아 모달창이 닫히지 않도록 구현하였다.

 

github