Q4-1
v1의 단일 Props 방식이 어떤 문제가 있었나요? Compound Component가 그걸 어떻게 해결했나요?
핵심 포인트
- v1은 단일 Props 방식 — 새 요구사항마다 props가 누적, 일부 컴포넌트가 props 10개 초과
- 해결 1: 마크업 자유도 부족 (헤더 위치 바꾸기, 우측에 액션 추가 같은 변형이 막힘)
- 해결 2: 학습 곡선 (props 10개 의미를 다 외워야 함)
- Compound는 "JSX 트리 자체로 구성을 표현" → 자유도와 직관성 동시 확보
모범 답안먼저 답해보고 펼치기
v1 디자인 시스템은 빠르게 만들기 위해 단일 Props 방식으로 갔어요. <Accordion title="..." subtitle="..." rightIcon={...} expandable showDivider ... /> 같은 형태죠. 처음엔 빨랐지만 새 화면 요구사항이 나올 때마다 prop이 하나씩 늘었어요. 1년쯤 지나니 prop이 10개를 넘는 컴포넌트가 생겼고, 동료들이 새 prop이 있는지 매번 storybook을 열어봐야 했습니다.
진짜 문제는 두 가지였어요. 첫째, 마크업 자유도. "헤더 우측에 토글 버튼을 넣고 싶다", "헤더와 본문 사이에 사용자 정보를 넣고 싶다" 같은 변형이 들어오면 prop으로 표현이 어려워서 억지로 prop을 추가하거나 새 컴포넌트를 fork 하는 식으로 처리됐어요. 둘째, 학습 곡선. prop의 이름과 효과를 다 외워야 동료들이 쓸 수 있는 상태였습니다.
Compound Component 패턴으로 바꾸니 두 문제가 동시에 풀렸어요. <Accordion> 안에 <Accordion.Summary>, <Accordion.Details>, <Accordion.Item> 같은 하위 컴포넌트를 두고, 사용자가 JSX 트리 자체로 구조를 표현합니다. 헤더 우측에 뭔가 넣고 싶으면 그냥 <Accordion.Summary> 안에 넣으면 끝이고, prop으로 모든 변형을 미리 예측할 필요가 없어졌어요. 동료들이 처음 보는 컴포넌트라도 HTML과 비슷한 사고로 쓸 수 있어서 학습 비용이 거의 0이 됐습니다. 결과적으로 v1 대비 prop 개수가 절반 이하로 줄었어요.
이 답변, 어땠나요?
꼬리 질문
- Q-a. 그러면 props가 다 사라졌나요? 어떤 건 여전히 props로 받나요?
"구성"은 children으로, "동작 정책"은 props로 분리했어요. 예를 들어 헤더 모양은
<Summary>children으로, "단일/다중 펼침 가능 여부"는 Accordion 본체의 prop으로 받는 식입니다. - Q-b. Compound 패턴은 잘못 쓰면 컴포넌트 트리가 깨지지 않나요? (예: Summary 빼먹기)
맞아요. TypeScript로 children의 타입을 강제할 순 있지만 강제력이 약합니다. 그래서 storybook에 정상 사용 예시를 명확하게 두고, 런타임에 핵심 자식이 없으면 dev mode에서 경고를 찍는 안전장치를 뒀어요.
- Q-c. shadcn/ui나 Radix UI 같은 트렌드와 비교하면 어떤가요?
Radix가 정확히 이 방향(headless + compound)이고, shadcn/ui도 Radix 기반이에요. 우리가 도입한 시점은 그보다 앞이었는데, 지금 보면 업계 흐름과 같은 사고였다고 평가합니다.
CS · 이론
- Compound Component pattern: React 커뮤니티에서 정착된 패턴. select/option 처럼 부모-자식이 함께 동작하는 구조를 React로 표현
- Inversion of Control: 라이브러리가 마크업을 정하지 않고 사용자에게 위임하는 사상. compound, render props, hooks가 모두 이 갈래
- Headless UI: Radix, Headless UI 라이브러리는 동작만 제공하고 시각은 사용자에게 위임. compound와 결합도가 높음
- Slot pattern (Vue/Svelte): 같은 문제를 다른 프레임워크에서 푸는 방식. JSX children + Context로 React에서 비슷하게 구현 가능