반응형
컴포넌트를 설계하다 보면 가끔 이런 상황에 직면합니다. 하나의 컴포넌트에 너무 많은 props를 전달하게 되어 코드가 복잡해지고, 내부 로직은 거대해지며, 조금만 요구사항이 바뀌어도 전체를 수정해야 하는 상황이죠.
이런 '거대 컴포넌트(Mega-component)'의 한계를 극복하고, 사용자에게는 자유도를, 설계자에게는 유지보수 편의성을 제공하는 Compound Components(합성 컴포넌트) 패턴을 마스터해 봅시다.
1. 🛑 "Prop Drilling"과 거대 컴포넌트의 문제점
예를 들어, 아코디언 컴포넌트를 만든다고 가정해 보겠습니다.
JavaScript
// ❌ 좋지 않은 예: 모든 것을 Props로 제어함
<Accordion
items={items}
allowMultiple={true}
headerStyle={{ color: 'blue' }}
defaultIndex={0}
/>
이 방식은 얼핏 편해 보이지만, 특정 아이템 사이에 광고를 넣거나 헤더의 위치를 바꾸고 싶을 때 대응하기가 매우 어렵습니다. 컴포넌트 내부가 **"닫힌 구조"**이기 때문입니다.
2. ✨ Compound Components 패턴이란?
하나의 컴포넌트를 여러 개의 하위 컴포넌트로 분리하고, 이들이 서로 협력하여 하나의 기능을 수행하도록 만드는 패턴입니다. Context API를 사용하여 부모와 자식 간에 상태를 공유하는 것이 핵심입니다.
2-1. 왜 사용하는가?
- 선언적 구조: UI의 구조를 JSX로 직접 조작할 수 있어 가독성이 뛰어납니다.
- 유연성: 자식 컴포넌트의 순서를 바꾸거나, 그 사이에 다른 HTML 요소를 자유롭게 끼워 넣을 수 있습니다.
- 상태 관리의 캡슐화: 외부에서 isOpen 같은 상태를 일일이 관리할 필요 없이, 컴포넌트 내부에서 알아서 처리됩니다.
3. 🛠️ 실전 구현: Toggle 컴포넌트 만들기
전형적인 Compound Components 패턴의 구현 단계는 다음과 같습니다.
1단계: Context 생성 및 부모 컴포넌트 정의
JavaScript
const ToggleContext = createContext();
function Toggle({ children }) {
const [on, setOn] = useState(false);
const toggle = () => setOn(!on);
// 1. 상태와 로직을 Provider를 통해 공유
return (
<ToggleContext.Provider value={{ on, toggle }}>
{children}
</ToggleContext.Provider>
);
}
2단계: 자식 컴포넌트 정의
JavaScript
function On({ children }) {
const { on } = useContext(ToggleContext);
return on ? children : null;
}
function Off({ children }) {
const { on } = useContext(ToggleContext);
return on ? null : children;
}
function Button() {
const { toggle } = useContext(ToggleContext);
return <button onClick={toggle}>스위치</button>;
}
3단계: 정적 속성으로 연결 (선택 사항이지만 추천)
JavaScript
Toggle.On = On;
Toggle.Off = Off;
Toggle.Button = Button;
4단계: 사용하는 쪽에서의 모습
JavaScript
// 사용자가 원하는 대로 레이아웃을 구성할 수 있음!
<Toggle>
<Toggle.On>켜짐 상태입니다. 💡</Toggle.On>
<Toggle.Off>꺼짐 상태입니다. 🌑</Toggle.Off>
<hr />
<Toggle.Button />
</Toggle>
4. 🎯 어떤 상황에 사용하면 좋을까요?
이 패턴은 주로 **UI 라이브러리(UI Library)**나 디자인 시스템을 구축할 때 매우 강력합니다.
- Tabs: <Tabs><TabList><Tab /></TabList><TabPanels><Panel /></TabPanels></Tabs>
- Select/Dropdown: <Select><Option /></Select>
- Modal: <Modal><Header /><Content /><Footer /></Modal>
💡 시니어의 조언: 모든 컴포넌트를 이 패턴으로 만들 필요는 없습니다. 단순한 컴포넌트는 일반적인 Props 방식이 더 빠릅니다. 하지만 내부 요소의 순서가 자주 바뀌거나 재사용성이 극도로 높아야 하는 경우라면 반드시 고려해 보세요.
반응형
'개발 > React' 카테고리의 다른 글
| [고급반] Step 2-3. Headless UI 패턴: 스타일에서 독립된 로직의 힘 (0) | 2025.12.23 |
|---|---|
| [고급반] Step 2-2. 로직 재사용의 기술: Render Props와 HOC (0) | 2025.12.23 |
| [고급반] Step 1-3. 대용량 데이터 렌더링: Windowing 기법으로 1만 개 리스트 다루기 (0) | 2025.12.23 |
| [고급반] Step 1-2. Memoization 전략: memo, useMemo, useCallback의 명과 암 (0) | 2025.12.23 |
| [고급반] Step 1-1. 재조정(Reconciliation)과 가상 DOM의 내부 원리 (0) | 2025.12.23 |