[Emotion CSS] 커스텀 컴포넌트 제작

2024. 7. 3. 17:37

회사에서 주로 부트스트랩을 사용한터라..부끄럽지만 공통 컴포넌트를 만들어 본 적이 없다.

이번 사이드 프로젝트를 통해 공통 컴포넌트를 만들면서 css도 만져보고 이것저것 해보는 중!

 

많이 썼던 modal을 만들어보면서 리뷰받았던 내용을 수정한 과정을 적어 보려고 한다.

(ㅎㅈ님 감사합니다😃)

 

코드리뷰로 받은 피드백

 

css(+emotion)를 제대로 만져본 적이 없던 터라 처음엔 삽질을 좀 했다.

 

우선 모달을 구성하면서, 화면에 필요한 모달 구성이

1. 중앙 배치 
- 확인 버튼
- 확인 버튼 + 취소 버튼

2. 하단 배치
- 사진 
- 텍스트
- 확인 버튼 + 취소 버튼 + X버튼

이렇게 되어 있다.

 

그래서 난 기본이 되는 BaseModal과 확인, 취소가 가능한 ConfirmModal과 사진과 텍스트(프로필 관련)가 쓰이는 ProfileModal로 나눴다.

 

근데 확인+취소가 가능한 게 중앙 배치, 하단 배치에 다 있기 때문에 이 부분을 옵션으로 줄 생각을 하고 있었다.


 

1. 첫 번째 코드 (mode = 'center' | 'bottom')

첫 번째로 생각한 건 (일단 빨리 짜보려고..) center 또는 bottom을 넘겨줘서 css에서 조건식으로 처리하는 방법으로 진행했다.

  • ConfrimModal.tsx
import { useModal } from "hooks";
import { BaseModal, Button } from "components";
import * as S from "./ConfirmModal.styled";

interface ConfirmModalProps {
  content?: React.ReactNode;
  confirmLabel?: string;
  cancelLabel?: string;
  mode?: "center" | "bottom";
}

const ConfirmModal = ({
  content,
  cancelLabel,
  confirmLabel,
  mode,
}: ConfirmModalProps) => {
  const { handleCloseModal } = useModal();

  return (
    <BaseModal mode={mode}>
    // Confirm Modal 내부 코드
    </BaseModal>
  );
};

export default ConfirmModal;
  • BaseModal.styled.ts
export const ModalContainer = styled.dialog<{ mode?: "center" | "bottom" }>`
  ${({ mode }) => css`
    display: flex;
    flex-direction: column;
    justify-content: center;
    border-radius: 1rem;
    border: 1px solid #ffffff; 
    background-color: #ffffff;
    position: ${mode === "bottom" ? "fixed" : "absolute"};
    top: ${mode === "bottom" ? "" : "50%"};
    bottom: ${mode === "bottom" && "10px"};
    left: 50%;
    width: ${mode === "bottom"
      ? "95%"
      : "342px"}; // TODO: 디자인 시스템에 따라 변경 예정
    height: auto;
    max-height: calc(100% - 100px);
    text-align: center;
    transform: ${mode === "bottom"
      ? "translateX(-50%)"
      : "translate(-50%, -50%)"};
  `}
`;

 

2. className 넘기기 (className='bottom-mode')

하단 배치가 필요한 ProfileModal에서 

  • ProfileModal.tsx
import { Badge, BaseModal } from "components";
import * as S from "./ProfileModal.styled";

interface ProfileModalProps {
  img?: string;
  name?: string;
  age?: string;
  info?: { location: string; company: string; job: string };
  introduceInfo?: {
    introduce: string;
    basicInfo: string[];
    keywords: string[];
  };
}

const ProfileModal = ({
  className,
  introduceInfo,
  img,
  name,
  age,
  info,
}: ProfileModalProps) => {
  return (
    <BaseModal className={'bottom-mode'} >
      // ProfileModal 내부 코드
    </BaseModal>
  );
};

export default ProfileModal;
  • BaseModal.styled.ts
import { css } from "@emotion/react";
import styled from "@emotion/styled";

interface ModalContainerProps {
  className?: string;
}

export const ModalWrapper = styled.div`
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  background-color: rgba(0, 0, 0, 0.5);
`;

export const ModalContainer = styled.dialog<ModalContainerProps>`
  ${({ className }) => css`
    display: flex;
    flex-direction: column;
    justify-content: center;
    border-radius: 1rem;
    border: 0px; //TODO: 디자인 시스템에 따라 변경 예정
    background-color: #ffffff; //TODO: 컬러는 디자인 시스템에 따라 변경 예정
    position: absolute;
    top: 50%;
    left: 50%;
    width: 345px; // TODO: 디자인 시스템에 따라 변경 예정
    height: auto;
    max-height: calc(100% - 100px);
    text-align: center;
    transform: translate(-50%, -50%);

    &.bottom-mode {
      position: fixed;
      bottom: 10px;
      width: 345px; // TODO: 디자인 시스템에 따라 변경 예정
      height: 585px; // TODO: 디자인 시스템에 따라 변경 예정
      transform: translateX(-50%);
      top: auto;
    }
  `}
`;

3. 최종 방법!(css함수 사용)

어찌저찌 서치 하면서 드디어 무슨 소린지를 알게 됨!!ㅎㅎ

  • ProfileModal.tsx
import { Badge, BaseModal } from "components";
import * as S from "./ProfileModal.styled";

interface ProfileModalProps {
  className?: string;
  img?: string;
  name?: string;
  age?: string;
  info?: { location: string; company: string; job: string };
  introduceInfo?: {
    introduce: string;
    basicInfo: string[];
    keywords: string[];
  };
}

const ProfileModal = ({
  className,
  introduceInfo,
  img,
  name,
  age,
  info,
}: ProfileModalProps) => {
  return (
    <BaseModal className={className} css={S.BottomModal}>
     // Profile Modal 내부 코드
    </BaseModal>
  );
};

export default ProfileModal;
  • ProfileModal.styled.ts
import { css } from "@emotion/react";
import styled from "@emotion/styled";

//...

export const BottomModal = css`
      position: fixed;
      bottom: 10px;
      width: 345px; // TODO: 디자인 시스템에 따라 변경 예정
      height: 585px; // TODO: 디자인 시스템에 따라 변경 예정
      transform: translateX(-50%);
      top: auto;
`;

  • css() 함수 사용 : css() 함수는 css 스타일 선언 내용을 인자로 받는데 객체형, 문자형으로 넘기면 된다.
  • className을 넘겨줘야 BaseModal에 BottomModal css가 적용된다.

 

저것도 처음엔 그 위에 덮어씌워지는 구조인지 몰라서  BaseModal.styled.ts에 있는 ModalContainer css와 겹치는 부분을 BottomModal css에도 다~적어줬다가 BaseModal에서 기본으로 가져가고 덮어씌워진다고 하여 공통 코드 부분을 삭제해서 마무리했다.

 

 

Modal 만드는데 너무 많은 시간을 지체했나 싶지만 그래도 뿌듯하고 재밌었다!