[React:ts] 재사용 가능한 UI 컴포넌트 만들기

WHY?

 코드스테이츠에서 파이널 프로젝트를 진행하면서 UI 컴포넌트 부분에 많이 관여를 했었다. 여러가지 컴포넌트를 만들면서 느꼈던 점이 있었다. 사이트에 모달창을 쓸 일이 많았는데, 각각의 모달창 컴포넌트들을 일일이 하나씩 만들어서 사용하다보니 굉장히 번거로웠다. 진행한 프로젝트를 예로 들어보면, 로그인 모달, 회원가입 모달, 글 삭제 모달, 회원 탈퇴 모달, 공유하기 모달 등등 각각의 코드 뭉치를 따로따로 만들었다. 그러다보니 각 모달창의 스타일도 통일하기가 힘들고, 새로 모달창을 만들려면 또 그 모달창에 대한 스타일이나 구조같은 것들도 새로 처음부터 짜야된다는 번거로움도 있었다(물론 어느정도는 복붙을 하겠지만). 이러한 부분은 본래 컴포넌트의 의의를 퇴색시키기도 한다.

 똑같은 짓을 반복하는건 컴퓨터에게 맡기는게 낫지 않겠는가? 그래서 새로운 프로젝트를 시작해보면서 이러한 여러 번 재사용하는 UI 컴포넌트들을 필요할 때마다 새로 만들지 말고, 라이브러리처럼 구성해서 내용물만 바꿔서 쓸 순 없을까? 하는 생각을 시작으로 컴포넌트를 구성하기 시작했다.


HOW?

※ Typescript와 Styled-components를 사용합니다.

  1. 우선, Modal.tsx 파일을 만들어 Modal 컴포넌트를 만든다.

    export const Modal = () => {
    	return <></>
    }
  2. Modal이 받을 props들을 정해준다.
    이 Modal이라는 컴포넌트는 여러 군데에 다른 형태로 사용될 것이기 때문에 크기와 내용물이 필요할 것이다. 그리고 외부 또는 'X' 클릭 시 모달창이 꺼지도록 구현하고 싶으니 상태 변경함수도 내려받아 보겠다.

    import { Dispatch, SetStateAction } from 'react';
    
    interface Props {
    	width: string;
     	height: string;
     	element: JSX.Element;
     	modal: boolean;
     	setModal: Dispatch<SetStateAction<boolean>>; // (arg0: boolean) => void 형태로 지정해주어도 된다.
    }
    export const Modal = ({ width, height, element, modal, setModal }:Props) => {
    	return <></>
    }

    element라는 녀석은 컴포넌트나 html 태그들을 받아오기 위해 JSX.Element 타입으로 설정해주었다.

  1. 컴포넌트 구조를 짜준다.

    // 모달창 View 부분
    const View = styled.div<{ width: string; height: string }>``;
    // 모달창 내용물 감싸는 div
    const Wrapper = styled.div``;
    // 모달창 팝업시 외부 접근방지 배경
    const Canvas = styled.div``;
    
    ...
    
    export const Modal = ({ width, height, element, modal, setModal }:Props) => {
    	return (
         <>
         	<View width={width} height={height}>
         	  <div className="exit-wrapper">
                &times;
              </div>
              <Wrapper>{element}</Wrapper>
            </View>
            <Canvas />
         </>
       );
    };

    꼭 구조를 똑같이 따를 필욘 없다! 본인이 편한대로 구성하면 됨!

  2. 각 element의 css를 작성해준다.

    const View = styled.div<{ width: string; height: string }>`
      @keyframes switchModalOn {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }
      position: fixed;
      display: flex;
      flex-direction: column;
      left: calc(50vw - ${(props) => props.width}px / 2);
      top: calc(50vh - ${(props) => props.height}px / 2);
      width: ${(props) => props.width}px;
      height: ${(props) => props.height}px;
      padding: 8px;
      background-color: white;
      border-radius: 8px;
      animation-name: switchModalOn;
      animation-duration: 0.5s;
      z-index: 2000;
      .exit-wrapper {
        position: absolute;
        top: 4px;
        right: 4px;
        font-size: 32px;
        width: 32px;
        height: 32px;
        line-height: 26px;
        background-color: transparent;
        cursor: pointer;
      }
    `;
    
     const Canvas = styled.div`
       position: fixed;
       top: 0;
       left: 0;
       width: 100vw;
       height: 100vh;
       background-color: rgba(0, 0, 0, 0.4);
       z-index: 53;
       animation-name: switchModalOn;
       animation-duration: 0.5s;
    `;
  • CSS는 본인이 원하는 대로!
  1. 이제, 모달이 사용될 곳에다 모달 컴포넌트를 추가해보자.

    import { useState } from 'react';
    import { Modal } from '.../Modal';
    
    export const ModalInMe = () => {
      const [modal, setModal] = useState(false);
      
      return (
        <div className="im-container">
          <div className="im-wrapper">
            <button onClick={() => { setModal(true); }}>모달</button>
          </div>
          {modal && (
            <Modal
              modal=[modal}
              setModal={setModal}
              width="250"
              height="200"
              element={<div></div>}
            />
          )}
        </div>
      );
    };
  2. 모달 끄는 이벤트도 만들어줘서 완성시킨다.

    ...
    
    export const Modal = ({ width, height, element, modal, setModal }:Props) => {
      
      	const modalOff = () => {
           setModal(false);
        };
    	return (
         <>
         	<View width={width} height={height}>
         	  <div className="exit-wrapper" onClick={modalOff}>
                &times;
              </div>
              <Wrapper>{element}</Wrapper>
            </View>
            <Canvas onClick={modalOff} />
         </>
       );
    };

이제, 내가 원하는 어디서든 원하는 모달창을 만들 수가 있다!
 


짠! 예제페이지(개편중)

좋은 웹페이지 즐겨찾기