Compound Pattern
2024.06.20
쇼핑몰에서 Card 컴포넌트를 설계한다고 할때, 제품을 보여주는 Card 컴포넌트의 타입은 다음과 같이 3가지가 있다고 합시다.
위 Card 컴포넌트를 그냥 Props로 값을 넘기고 And
연산자를 통해서 값이 있을 경우에만 화면에 보이게 렌더링하는 방식으로 구성을 할 수 있습니다.
<Card
title={"제품명"}
thumnail={"이미지 주소"}
description={"Lorem ipsum dolor sit amet, consectetur adipiscing elit."}
price={12000}
sale={10}
star={1.5}
/>
하지만, 만약에 다음과 같이 속성의 배치 자체가 바뀌는 경우라면 어떨까요?
그렇다고 각각의 컴포넌트를 분리하게 된다면, 상태를 관리하는 컴포넌트가 필요하게 되고, 해당 컴포넌트를 재상용성이 떨어지게 됩니다.
이때 Compound Pattern을 사용하게 되면 각 구성 요소들을 Headless하게 설계할 수 있습니다.
1. Compound Pattern 이란?
원하는데로 결합하고 조합하여 컴포넌트를 구성할 수 있는 패턴입니다 부모 컴포넌트와 자식 컴포넌트간의 긴밀하게 작동할 수 있게 설계 되었습니다.
컴포넌트의 상태와 동작을 부모 컴포넌트에 캡슐화하여 자식 컴포넌트는 부모 컴포넌트의 상태를 공유할 수 있습니다. 이를 통해서 각 컴포넌트를 독립적으로 관리할 수 있고 조립 가능한 방식으로 설계를 할 수 있습니다.
2. 예시
<Card />
컴포넌트에 대해서 다음과 같이 코드를 구성할 수 있습니다.
-
카드의 최상단 부모 컴포넌트
const CardContext = createContext(); const Card = ({ children }) => { const [state, setState] = useState({ price: 0, sale: null, }); return ( <CardContext.Provider value={[state, setState]}> <div>{children}</div> </CardContext.Provider> ); };
-
카드 자식 컴포넌트
각 컴포넌트의 상태를 부모 컴포넌트의
<Card />
에서 관리합니다. 이렇게 하면sale
값이 있을때price
값에 sale값을 반영한 값을 자동으로 반영 할 수 있게 됩니다.const Title = ({ children }) => <h2>{children}</h2>; const Description = ({ children }) => <p>{children}</p>; const Price = ({ value }) => { const [state, setState] = useContext(CardContext); useEffect(() => { setState((prev) => ({ ...prev, price: value })); }, [value, setState]); const discountedPrice = state.sale ? (value * (100 - state.sale)) / 100 : null; return ( <p> {discountedPrice !== null ? ( <> <span style={{ textDecoration: "line-through" }}>{value}</span>{" "} <span>{discountedPrice.toFixed(2)}</span> </> ) : ( <span>{value}</span> )} </p> ); }; const Sale = ({ value }) => { const [, setState] = useContext(CardContext); useEffect(() => { setState((prev) => ({ ...prev, sale: value })); }, [value, setState]); return <div>Sale: {value}% off</div>; }; Card.Title = Title; Card.Description = Description; Card.Price = Price; Card.Sale = Sale; export default Card;
-
카드 컴포넌트 사용
해당 컴포넌트의 상태를 각각 관리하지만 공유하고, 컴포넌트가 사용되는 곳에 따라서 UI를 외부에서 구성을 할 수 있게 됩니다.
<Card> <Card.Title>Product Name</Card.Title> <Card.Description> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </Card.Description> <Card.Price value={100} /> <Card.Sale value={10} /> </Card>
3. 결론
Compound 패턴의 가장 큰 장점은 개발 후에 직관적인것 같습니다. 유연성이 높아 유지보수를 하기 좋은것도 있지만, 코드의 가독성이 매우 높아서 쉽게 읽힌다는 점입니다. 그리고 기존에는 UI와 로직만 구분했다면, CSS가 없는 HTML+로직으로만 구성하여 Headless하게 코드를 구성할 수 있다는 점에도 매우 만족하게 되었습니다.