<aside> ➡️
</aside>
<aside> ❓
공통 컴포넌트를 만들 때마다 항상 고민하는 부분들이 있습니다.
어디까지 생각하고 props를 전달해야 하는지, 나중에 디자이너에 의해 스타일이 변경되진 않을지, boolean 혹은 값의 유무에 따라 보여지는 뷰를 항상 조건부 렌더링으로 표현해야 하는지 등등…
항상 공통 컴포넌트를 만들어 놓고, 추후에 추가 기능이 생기거나 디자인이 변경되거나 코드적으로 수정사항이 발생하면 새로운 props를 추가하고, 코드 리팩토링 하고, 공통 컴포넌트를 리팩토링 하다보니 이 컴포넌트를 사용하는 모든 곳에서 수정이 필요할 때도 있고,,,
물론 제가 아직 공통 컴포넌트를 효율적으로 구현하지 못하는 실력의 한계가 있긴 하겠지만 그럼에도 불구하고 지금까지 만들었던 공통 컴포넌트는 재사용성이 떨어지고 협업에서 사람들과 쓰기에 올바른 패턴은 아니었다고 생각합니다.
</aside>
import { useState } from 'react';
import { IcClipboardCopy } from '@svg';
import useToast from 'src/hooks/useToast';
import { accountNumberStyle, buttonWrapperStyle, iconStyle } from './ClipboardCopyButton.style';
import Toast from '../../Toast/Toast';
const ClipboardCopyButton = () => {
const { showToast, isToastVisible } = useToast();
const [toastMessage, setToastMessage] = useState('');
const accountNumber = '12345678';
const handleCopyClick = async () => { ~~~ };
return (
<button css={buttonWrapperStyle} onClick={handleCopyClick}>
<span css={accountNumberStyle}>{accountNumber}</span>
<span css={iconStyle}>
<IcClipboardCopy />
<Toast isVisible={isToastVisible} toastBottom={3}>
{toastMessage}
</Toast>
</span>
</button>
);
};
export default ClipboardCopyButton;
위와 같은 고민을 해결하기 위한 방법으로 Compound Component Pattern이라는 방식이 있습니다. - (이하 CCP라고 칭함)
조합
하여 다양한 기능을 제공할 수 있게 합니다.<aside> 📍
이 패턴의 주요 특징
import React from "react";
import styles from "./BottomSheetItem.module.css";
const BottomSheetItem = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.cardWrapper}>{children}</div>;
};
const ItemTitle = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.title}>{children}</div>;
};
const ItemCategory = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.category}>{children}</div>;
};
const ItemDescription = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.itemDescription}>{children}</div>;
};
const ItemRating = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.itemRating}>{children}</div>;
};
const ItemReview = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.itemReview}>후기 {children}</div>;
};
const ItemDistance = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.itemDistance}>{children}</div>;
};
const ItemAddress = ({ children }: { children: React.ReactNode }) => {
return <div className={styles.itemAddress}>{children}</div>;
};
const Image = ({ src, alt }: { src: string; alt: string }) => {
return <img src={src} alt={alt} className={styles.image} />;
};
const Badge = () => {
return <div>🍎</div>;
};
BottomSheetItem.ItemTitle = ItemTitle;
BottomSheetItem.ItemCategory = ItemCategory;
BottomSheetItem.ItemDescription = ItemDescription;
BottomSheetItem.ItemRating = ItemRating;
BottomSheetItem.ItemReview = ItemReview;
BottomSheetItem.ItemDistance = ItemDistance;
BottomSheetItem.ItemAddress = ItemAddress;
BottomSheetItem.Image = Image;
BottomSheetItem.Badge = Badge;
export { BottomSheetItem };
import { BottomSheetItem } from "../../../../components/common/BottomSheetItem/BottomSheetItem";
import image1 from "../../../../assets/images/액정 키스ㅋㅋ.jpeg";
import styles from "./Home.module.css";
const Home = () => {
return (
<>
<BottomSheetItem>
<section className={styles.section}>
<div className={styles.sectionHeader}>
<BottomSheetItem.ItemTitle>카페 나나빈</BottomSheetItem.ItemTitle>
<BottomSheetItem.ItemCategory>카페</BottomSheetItem.ItemCategory>
<BottomSheetItem.Badge />
</div>
<BottomSheetItem.ItemDescription>
그린바나나가루를 사용하여 만든 디저트카페 나나빈입니다! 시그니처
음료까지 맛보러오세요^^
</BottomSheetItem.ItemDescription>
<div className={styles.ratingAndReview}>
<BottomSheetItem.ItemRating>⭐️4.5</BottomSheetItem.ItemRating>
<BottomSheetItem.ItemReview>19</BottomSheetItem.ItemReview>
</div>
<div className={styles.distanceAndAddress}>
<BottomSheetItem.ItemDistance>420m</BottomSheetItem.ItemDistance>
<BottomSheetItem.ItemAddress>
영등포동7가
</BottomSheetItem.ItemAddress>
</div>
</section>
<BottomSheetItem.Image src={image1} alt="Image1" />
</BottomSheetItem>
</>
);
};
export default Home;