[Emotion CSS] 커스텀 컴포넌트 제작
회사에서 주로 부트스트랩을 사용한터라..부끄럽지만 공통 컴포넌트를 만들어 본 적이 없다.
이번 사이드 프로젝트를 통해 공통 컴포넌트를 만들면서 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 만드는데 너무 많은 시간을 지체했나 싶지만 그래도 뿌듯하고 재밌었다!