--- id: react-component-composition title: 컴포넌트 합성 (Composition over Configuration) category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [react, composition, children, slots, vibe-coding] tech_stack: { language: "TypeScript / React 18+", applicable_to: ["Web", "React Native"] } applied_in: [] aliases: [render props, compound components, slot pattern] --- # 컴포넌트 합성 (Composition) > 새 옵션 props 추가가 답답해지면 멈춰라. **자식이 직접 채우게 하는 합성**이 prop 폭발보다 거의 항상 낫다. boolean prop 30개 컴포넌트는 anti-signal. ## 📖 핵심 개념 3가지 합성 패턴: 1. **Children pass-through**: `{children}` 받아 가운데에 끼움 2. **Slot props (named children)**: `header` / `footer` 등 이름 있는 영역 3. **Compound components**: `` 같이 부모 + 자식 협력 ## 💻 코드 패턴 ### Children pass-through ```tsx function Card({ children }: { children: ReactNode }) { return
{children}
; }

Title

body

``` ### Slot props ```tsx function Modal({ title, body, actions }: { title: ReactNode; body: ReactNode; actions: ReactNode }) { return
{title}
{body}
; } } /> ``` ### Compound components ```tsx const TabsContext = createContext<{ active: string; setActive: (k: string) => void } | null>(null); function Tabs({ defaultActive, children }: ...) { const [active, setActive] = useState(defaultActive); return {children}; } function TabList({ children }) { return
{children}
; } function Tab({ id, children }: { id: string; children: ReactNode }) { const ctx = useContext(TabsContext)!; return ; } function TabPanel({ id, children }) { const ctx = useContext(TabsContext)!; return ctx.active === id ?
{children}
: null; } Tabs.List = TabList; Tabs.Tab = Tab; Tabs.Panel = TabPanel; AB ``` ## 🤔 의사결정 기준 | 상황 | 패턴 | |---|---| | 단순 wrapper (border, padding) | children | | 정해진 레이아웃, 영역 의미 다름 | slot props | | 부모-자식 상태 공유 (Tabs, Accordion, Menu) | compound components | | 외부에서 마음대로 조립 가능해야 | render props 또는 hook 노출 | | 옵션이 5개 이상의 boolean prop | composition 으로 리팩터 | ## ❌ 안티패턴 - **boolean prop 폭발**: `